Add registry module and api .dockerignore to fix production build
- Create api/src/modules/registry/ (service + routes) so server.ts import resolves and TypeScript compiles all 38 modules cleanly - Add api/.dockerignore to exclude stale local dist/ from Docker build context, preventing old compiled output from persisting in images - Registry routes: GET /status (Gitea packages API), POST /build-push and POST /mirror (write trigger files for host watcher, SUPER_ADMIN only) Bunker Admin
This commit is contained in:
parent
be2fa5d80b
commit
f550423c3f
7
api/.dockerignore
Normal file
7
api/.dockerignore
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
*.log
|
||||||
|
test-*.sh
|
||||||
|
test*.ts
|
||||||
|
test*.js
|
||||||
43
api/src/modules/registry/registry.routes.ts
Normal file
43
api/src/modules/registry/registry.routes.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
|
import { authenticate } from '../../middleware/auth.middleware';
|
||||||
|
import { requireRole } from '../../middleware/rbac.middleware';
|
||||||
|
import { registryService } from './registry.service';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.use(authenticate);
|
||||||
|
router.use(requireRole('SUPER_ADMIN'));
|
||||||
|
|
||||||
|
// GET /api/registry/status — list container images in Gitea registry
|
||||||
|
router.get('/status', async (_req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const images = await registryService.getRegistryStatus();
|
||||||
|
res.json({ images });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /api/registry/build-push — trigger build-and-push.sh via trigger file
|
||||||
|
router.post('/build-push', async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const { skipCodeServer, services } = req.body as { skipCodeServer?: boolean; services?: string };
|
||||||
|
registryService.triggerBuildPush(req.user?.email ?? 'api', { skipCodeServer, services });
|
||||||
|
res.json({ ok: true, message: 'Build and push triggered (runs in background)' });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /api/registry/mirror — trigger mirror-images.sh via trigger file
|
||||||
|
router.post('/mirror', async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const { all } = req.body as { all?: boolean };
|
||||||
|
registryService.triggerMirror(req.user?.email ?? 'api', { all });
|
||||||
|
res.json({ ok: true, message: 'Mirror triggered (runs in background)' });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registryRouter = router;
|
||||||
76
api/src/modules/registry/registry.service.ts
Normal file
76
api/src/modules/registry/registry.service.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { env } from '../../config/env';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry service — queries Gitea Packages API for container image status.
|
||||||
|
* Build/mirror triggers are handled via trigger files read by the host watcher
|
||||||
|
* (same pattern as upgrade service).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const UPGRADE_DIR = path.resolve('/app/upgrade');
|
||||||
|
const REGISTRY_TRIGGER_FILE = path.join(UPGRADE_DIR, 'registry-trigger.json');
|
||||||
|
|
||||||
|
export interface RegistryImage {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getRegistryStatus(): Promise<RegistryImage[]> {
|
||||||
|
const registryUrl = env.GITEA_REGISTRY || 'gitea.bnkops.com/admin';
|
||||||
|
const [host, org] = registryUrl.split('/');
|
||||||
|
const user = env.GITEA_REGISTRY_USER;
|
||||||
|
const pass = env.GITEA_REGISTRY_PASS;
|
||||||
|
|
||||||
|
if (!user || !pass) {
|
||||||
|
throw new Error('GITEA_REGISTRY_USER and GITEA_REGISTRY_PASS must be configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
const auth = Buffer.from(`${user}:${pass}`).toString('base64');
|
||||||
|
const res = await fetch(`https://${host}/api/v1/packages/${org}?type=container&limit=50`, {
|
||||||
|
headers: { Authorization: `Basic ${auth}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Gitea API error: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json() as RegistryImage[];
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegistryTrigger {
|
||||||
|
action: 'build-push' | 'mirror';
|
||||||
|
triggeredAt: string;
|
||||||
|
triggeredBy: string;
|
||||||
|
options: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeTrigger(action: RegistryTrigger['action'], triggeredBy: string, options: Record<string, unknown>): void {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(UPGRADE_DIR, { recursive: true });
|
||||||
|
const trigger: RegistryTrigger = {
|
||||||
|
action,
|
||||||
|
triggeredAt: new Date().toISOString(),
|
||||||
|
triggeredBy,
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
fs.writeFileSync(REGISTRY_TRIGGER_FILE, JSON.stringify(trigger, null, 2), 'utf-8');
|
||||||
|
logger.info(`Registry trigger written: ${action} by ${triggeredBy}`);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to write registry trigger:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerBuildPush(triggeredBy: string, options: { skipCodeServer?: boolean; services?: string } = {}): void {
|
||||||
|
writeTrigger('build-push', triggeredBy, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerMirror(triggeredBy: string, options: { all?: boolean } = {}): void {
|
||||||
|
writeTrigger('mirror', triggeredBy, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registryService = { getRegistryStatus, triggerBuildPush, triggerMirror };
|
||||||
Loading…
x
Reference in New Issue
Block a user