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