- New Express + TypeScript backend (server/) with pg, Better Auth, local file storage - De-Supabased Postgres schema (server/db) and TS reimplementations of DB functions - Frontend data layer rewired to REST (rest-client + backend-client compat shim) - Removed all Supabase references (code, config, deps, docs) - New brand assets: gradient favicon/app icons + dark/white wordmark logos Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
import { query, one } from '../db.js';
|
|
import { env } from '../env.js';
|
|
|
|
interface TriggerArgs {
|
|
buildId: string;
|
|
websiteUrl: string;
|
|
appName: string;
|
|
platform: string;
|
|
packageName: string;
|
|
config: Record<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* Trigger a cloud build.
|
|
*
|
|
* If CODEMAGIC_API_TOKEN + CODEMAGIC_APP_ID are configured, this calls the
|
|
* Codemagic API for real. Otherwise it runs a local SIMULATION so the build
|
|
* flow is demonstrable in development (progress + placeholder artifact).
|
|
*/
|
|
export async function triggerCloudBuild(args: TriggerArgs): Promise<{ cloudBuildId?: string; message: string }> {
|
|
if (env.CODEMAGIC_API_TOKEN && env.CODEMAGIC_APP_ID) {
|
|
const res = await fetch('https://api.codemagic.io/builds', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'x-auth-token': env.CODEMAGIC_API_TOKEN },
|
|
body: JSON.stringify({
|
|
appId: env.CODEMAGIC_APP_ID,
|
|
workflowId: args.platform === 'ios' ? 'ios-workflow' : 'android-workflow',
|
|
branch: 'main',
|
|
environment: {
|
|
variables: {
|
|
APP_NAME: args.appName,
|
|
WEBSITE_URL: args.websiteUrl,
|
|
PACKAGE_NAME: args.packageName,
|
|
},
|
|
},
|
|
}),
|
|
});
|
|
const json: any = await res.json().catch(() => ({}));
|
|
if (!res.ok) throw new Error(json?.error || json?.message || 'Codemagic build trigger failed');
|
|
const cloudBuildId = json?.buildId || json?._id;
|
|
await query(`UPDATE public.app_builds SET status = 'building', progress = 5 WHERE id = $1`, [args.buildId]);
|
|
return { cloudBuildId, message: 'Cloud build started (Codemagic)' };
|
|
}
|
|
|
|
// ---- Simulation (no Codemagic configured) ----
|
|
await query(`UPDATE public.app_builds SET status = 'building', progress = 10 WHERE id = $1`, [args.buildId]);
|
|
simulateBuild(args.buildId).catch((e) => console.error('[cloud-build sim]', e.message));
|
|
return { message: 'Cloud build simulated (Codemagic not configured)' };
|
|
}
|
|
|
|
async function simulateBuild(buildId: string) {
|
|
const steps = [25, 45, 65, 85];
|
|
for (const p of steps) {
|
|
await delay(2000);
|
|
await query(`UPDATE public.app_builds SET progress = $2 WHERE id = $1 AND status = 'building'`, [buildId, p]);
|
|
}
|
|
await delay(2000);
|
|
const build = await one<any>(`SELECT package_name FROM public.app_builds WHERE id = $1`, [buildId]);
|
|
const fileName = `${(build?.package_name || 'app').replace(/\./g, '-')}.apk`;
|
|
await query(
|
|
`UPDATE public.app_builds
|
|
SET status = 'complete', progress = 100,
|
|
download_url = $2, file_size_bytes = $3
|
|
WHERE id = $1 AND status = 'building'`,
|
|
[buildId, `/storage/apk-builds/simulated/${fileName}`, 12 * 1024 * 1024]
|
|
);
|
|
}
|
|
|
|
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
|
|
|
/** Returns the current build row (used by the status endpoint). */
|
|
export async function getCloudBuildStatus(buildId: string) {
|
|
return one(`SELECT * FROM public.app_builds WHERE id = $1`, [buildId]);
|
|
}
|