feat: expose verifiable deployment release
This commit is contained in:
@@ -65,6 +65,7 @@ import { subscribe, list as listSubs, remove as removeSub, deliveryQueue, resolv
|
||||
import { bildirimleriIsle } from './notifier.mjs';
|
||||
import { scanSite } from './site-scan.mjs';
|
||||
import { createDetailCache } from './detail-cache.mjs';
|
||||
import { releaseInfo } from './release-info.mjs';
|
||||
|
||||
// ilan.gov.tr sunucusu ara sertifikayı (GeoTrust TLS RSA CA G1) göndermiyor;
|
||||
// zinciri tamamlamak için PEM'i yerelden ekliyoruz.
|
||||
@@ -75,6 +76,7 @@ const CA_BUNDLE = [...tls.rootCertificates, EXTRA_CA];
|
||||
const PORT = process.env.PORT || 3001;
|
||||
const ILAN_BASE = 'https://www.ilan.gov.tr';
|
||||
const CORS_ORIGINS = String(process.env.CORS_ORIGINS || '').split(',').map(value => value.trim()).filter(Boolean);
|
||||
const RUNTIME_RELEASE = releaseInfo();
|
||||
|
||||
function readiness() {
|
||||
const directories = {
|
||||
@@ -1732,6 +1734,7 @@ const server = http.createServer(async (req, res) => {
|
||||
}
|
||||
|
||||
if (url.pathname === '/api/health') return json(res, 200, { ok: true });
|
||||
if (url.pathname === '/api/version') return json(res, 200, { ok: true, ...RUNTIME_RELEASE });
|
||||
if (url.pathname === '/api/ready') {
|
||||
const status = readiness();
|
||||
return json(res, status.ready ? 200 : 503, { ok: status.ready, ...status });
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const DEFAULT_ASSET_ROOTS = [
|
||||
'/usr/share/nginx/html',
|
||||
path.resolve(process.cwd(), 'dist'),
|
||||
];
|
||||
|
||||
function cleanRelease(value) {
|
||||
const release = String(value || '').trim();
|
||||
if (!release || release === 'unknown' || release.length > 128) return null;
|
||||
return release;
|
||||
}
|
||||
|
||||
export function deployedRelease(env = process.env) {
|
||||
return cleanRelease(env.APP_RELEASE) || cleanRelease(env.SOURCE_COMMIT);
|
||||
}
|
||||
|
||||
export function frontendAssetInfo(roots = DEFAULT_ASSET_ROOTS) {
|
||||
for (const root of roots) {
|
||||
try {
|
||||
const indexPath = path.join(root, 'index.html');
|
||||
const html = fs.readFileSync(indexPath, 'utf8');
|
||||
const match = html.match(/<script[^>]+src=["']([^"']*\/assets\/[^"']+\.js(?:\?[^"']*)?)["']/i);
|
||||
if (!match) continue;
|
||||
const publicPath = new URL(match[1], 'https://ihaleist.invalid/').pathname;
|
||||
const assetPath = path.resolve(root, `.${publicPath}`);
|
||||
const rootPath = path.resolve(root) + path.sep;
|
||||
if (!assetPath.startsWith(rootPath)) continue;
|
||||
const bytes = fs.readFileSync(assetPath);
|
||||
return {
|
||||
path: publicPath,
|
||||
sha256: crypto.createHash('sha256').update(bytes).digest('hex'),
|
||||
bytes: bytes.length,
|
||||
};
|
||||
} catch {
|
||||
// Bu kökte derlenmiş arayüz yoksa sıradaki olası köke bakılır.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function releaseInfo({ env = process.env, roots = DEFAULT_ASSET_ROOTS } = {}) {
|
||||
return {
|
||||
release: deployedRelease(env),
|
||||
version: '1.0.0',
|
||||
frontendAsset: frontendAssetInfo(roots),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user