initttt
This commit is contained in:
153
apps/front/src/index.ts
Normal file
153
apps/front/src/index.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import "./instrumentation.js";
|
||||
|
||||
import { createHttpTelemetryMiddleware } from "@pkg/logic/core/http.telemetry";
|
||||
import type { FlowExecCtx } from "@pkg/logic/core/flow.execution.context";
|
||||
import { logDomainEvent } from "@pkg/logger";
|
||||
import { serve } from "@hono/node-server";
|
||||
import { settings } from "@pkg/settings";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { Hono } from "hono";
|
||||
|
||||
const app = new Hono().use("*", createHttpTelemetryMiddleware("front"));
|
||||
|
||||
const host = process.env.HOST || "0.0.0.0";
|
||||
const port = Number(process.env.PORT || "3000");
|
||||
|
||||
function normalizeBaseUrl(url: string): string {
|
||||
return url.endsWith("/") ? url.slice(0, -1) : url;
|
||||
}
|
||||
|
||||
function buildFlowExecCtx(): FlowExecCtx {
|
||||
return { flowId: randomUUID() };
|
||||
}
|
||||
|
||||
function getClientDownloadedApkName(): string {
|
||||
const filename = settings.clientDownloadedApkName.trim();
|
||||
return filename.toLowerCase().endsWith(".apk")
|
||||
? filename
|
||||
: `${filename}.apk`;
|
||||
}
|
||||
|
||||
app.get("/health", (c) => {
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
app.get("/ping", (c) => {
|
||||
return c.text("pong");
|
||||
});
|
||||
|
||||
app.get("/downloads/file/:buildId", async (c) => {
|
||||
const fctx = buildFlowExecCtx();
|
||||
const buildId = c.req.param("buildId");
|
||||
|
||||
logDomainEvent({
|
||||
event: "processor.apk_download.started",
|
||||
fctx,
|
||||
meta: { buildId },
|
||||
});
|
||||
|
||||
const buildResult = await mobileBuildController.validateActiveBuildId(
|
||||
fctx,
|
||||
buildId,
|
||||
);
|
||||
if (buildResult.isErr()) {
|
||||
logDomainEvent({
|
||||
level: "warn",
|
||||
event: "processor.apk_download.rejected",
|
||||
fctx,
|
||||
error: buildResult.error,
|
||||
meta: { buildId },
|
||||
});
|
||||
return c.json(
|
||||
{
|
||||
data: null,
|
||||
error: { ...buildResult.error, flowId: fctx.flowId },
|
||||
},
|
||||
404,
|
||||
);
|
||||
}
|
||||
|
||||
const build = buildResult.value;
|
||||
if (!build.apkAssetPath) {
|
||||
logDomainEvent({
|
||||
level: "warn",
|
||||
event: "processor.apk_download.missing_artifact",
|
||||
fctx,
|
||||
meta: { buildId },
|
||||
});
|
||||
return c.json(
|
||||
{
|
||||
data: null,
|
||||
error: {
|
||||
flowId: fctx.flowId,
|
||||
code: "NOT_FOUND",
|
||||
message: "APK not available",
|
||||
description: "This build does not have a generated APK yet",
|
||||
detail: `buildId=${buildId}`,
|
||||
},
|
||||
},
|
||||
404,
|
||||
);
|
||||
}
|
||||
|
||||
const assetUrl = `${normalizeBaseUrl(settings.appBuilderApiUrl)}${build.apkAssetPath}`;
|
||||
const assetResponse = await fetch(assetUrl);
|
||||
|
||||
if (!assetResponse.ok || !assetResponse.body) {
|
||||
logDomainEvent({
|
||||
level: "error",
|
||||
event: "processor.apk_download.fetch_failed",
|
||||
fctx,
|
||||
meta: {
|
||||
buildId,
|
||||
assetUrl,
|
||||
status: assetResponse.status,
|
||||
},
|
||||
});
|
||||
return c.json(
|
||||
{
|
||||
data: null,
|
||||
error: {
|
||||
flowId: fctx.flowId,
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Failed to fetch APK artifact",
|
||||
description: "Please try again later",
|
||||
detail: `assetUrl=${assetUrl} status=${assetResponse.status}`,
|
||||
},
|
||||
},
|
||||
502,
|
||||
);
|
||||
}
|
||||
|
||||
logDomainEvent({
|
||||
event: "processor.apk_download.succeeded",
|
||||
fctx,
|
||||
meta: {
|
||||
buildId,
|
||||
assetUrl,
|
||||
downloadName: getClientDownloadedApkName(),
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(assetResponse.body, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type":
|
||||
assetResponse.headers.get("content-type") ||
|
||||
"application/vnd.android.package-archive",
|
||||
"content-disposition": `attachment; filename="${getClientDownloadedApkName()}"`,
|
||||
"cache-control": "no-store",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
serve(
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port,
|
||||
hostname: host,
|
||||
},
|
||||
(info) => {
|
||||
console.log(`Server is running on http://${host}:${info.port}`);
|
||||
},
|
||||
);
|
||||
50
apps/front/src/instrumentation.ts
Normal file
50
apps/front/src/instrumentation.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
||||
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto";
|
||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
||||
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
||||
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-proto";
|
||||
import { createAddHookMessageChannel } from "import-in-the-middle";
|
||||
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
||||
import { NodeSDK } from "@opentelemetry/sdk-node";
|
||||
import { settings } from "@pkg/settings";
|
||||
import { register } from "node:module";
|
||||
|
||||
const { registerOptions } = createAddHookMessageChannel();
|
||||
register("import-in-the-middle/hook.mjs", import.meta.url, registerOptions);
|
||||
|
||||
const normalizedEndpoint = settings.otelExporterOtlpHttpEndpoint.startsWith(
|
||||
"http",
|
||||
)
|
||||
? settings.otelExporterOtlpHttpEndpoint
|
||||
: `http://${settings.otelExporterOtlpHttpEndpoint}`;
|
||||
|
||||
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
||||
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = normalizedEndpoint;
|
||||
}
|
||||
|
||||
const sdk = new NodeSDK({
|
||||
serviceName: `${settings.otelServiceName}-processor`,
|
||||
traceExporter: new OTLPTraceExporter(),
|
||||
metricReader: new PeriodicExportingMetricReader({
|
||||
exporter: new OTLPMetricExporter(),
|
||||
exportIntervalMillis: 10_000,
|
||||
}),
|
||||
logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())],
|
||||
instrumentations: [
|
||||
getNodeAutoInstrumentations({
|
||||
"@opentelemetry/instrumentation-winston": {
|
||||
// We add OpenTelemetryTransportV3 explicitly in @pkg/logger.
|
||||
disableLogSending: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
sdk.start();
|
||||
|
||||
const shutdown = () => {
|
||||
void sdk.shutdown();
|
||||
};
|
||||
|
||||
process.on("SIGTERM", shutdown);
|
||||
process.on("SIGINT", shutdown);
|
||||
Reference in New Issue
Block a user