148 lines
5.2 KiB
TypeScript
148 lines
5.2 KiB
TypeScript
|
|
import * as plugins from '../../plugins.ts';
|
||
|
|
import type { OpsServer } from '../classes.opsserver.ts';
|
||
|
|
import * as interfaces from '../../../ts_interfaces/index.ts';
|
||
|
|
import { requireValidIdentity } from '../helpers/guards.ts';
|
||
|
|
|
||
|
|
export class RegistryHandler {
|
||
|
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||
|
|
|
||
|
|
constructor(private opsServerRef: OpsServer) {
|
||
|
|
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
||
|
|
this.registerHandlers();
|
||
|
|
}
|
||
|
|
|
||
|
|
private registerHandlers(): void {
|
||
|
|
// Get registry tags
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRegistryTags>(
|
||
|
|
'getRegistryTags',
|
||
|
|
async (dataArg) => {
|
||
|
|
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
|
||
|
|
const tags = await this.opsServerRef.oneboxRef.registry.getImageTags(dataArg.serviceName);
|
||
|
|
return { tags };
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
|
||
|
|
// Get registry tokens
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRegistryTokens>(
|
||
|
|
'getRegistryTokens',
|
||
|
|
async (dataArg) => {
|
||
|
|
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
|
||
|
|
const rawTokens = this.opsServerRef.oneboxRef.database.getAllRegistryTokens();
|
||
|
|
const now = Date.now();
|
||
|
|
|
||
|
|
const tokens = rawTokens.map((token: any) => {
|
||
|
|
const isExpired = token.expiresAt !== null && token.expiresAt < now;
|
||
|
|
let scopeDisplay: string;
|
||
|
|
if (token.scope === 'all') {
|
||
|
|
scopeDisplay = 'All services';
|
||
|
|
} else if (Array.isArray(token.scope)) {
|
||
|
|
scopeDisplay = token.scope.length === 1 ? token.scope[0] : `${token.scope.length} services`;
|
||
|
|
} else {
|
||
|
|
scopeDisplay = 'Unknown';
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
id: token.id!,
|
||
|
|
name: token.name,
|
||
|
|
type: token.type,
|
||
|
|
scope: token.scope,
|
||
|
|
scopeDisplay,
|
||
|
|
expiresAt: token.expiresAt,
|
||
|
|
createdAt: token.createdAt,
|
||
|
|
lastUsedAt: token.lastUsedAt,
|
||
|
|
createdBy: token.createdBy,
|
||
|
|
isExpired,
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
return { tokens };
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
|
||
|
|
// Create registry token
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateRegistryToken>(
|
||
|
|
'createRegistryToken',
|
||
|
|
async (dataArg) => {
|
||
|
|
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
|
||
|
|
const config = dataArg.tokenConfig;
|
||
|
|
|
||
|
|
// Calculate expiration
|
||
|
|
const now = Date.now();
|
||
|
|
let expiresAt: number | null = null;
|
||
|
|
if (config.expiresIn !== 'never') {
|
||
|
|
const daysMap: Record<string, number> = { '30d': 30, '90d': 90, '365d': 365 };
|
||
|
|
const days = daysMap[config.expiresIn];
|
||
|
|
if (days) expiresAt = now + days * 24 * 60 * 60 * 1000;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Generate token
|
||
|
|
const plainToken = crypto.randomUUID() + crypto.randomUUID();
|
||
|
|
const encoder = new TextEncoder();
|
||
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(plainToken));
|
||
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||
|
|
const tokenHash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
||
|
|
|
||
|
|
const token = this.opsServerRef.oneboxRef.database.createRegistryToken({
|
||
|
|
name: config.name,
|
||
|
|
tokenHash,
|
||
|
|
type: config.type,
|
||
|
|
scope: config.scope,
|
||
|
|
expiresAt,
|
||
|
|
createdAt: now,
|
||
|
|
lastUsedAt: null,
|
||
|
|
createdBy: dataArg.identity.username,
|
||
|
|
});
|
||
|
|
|
||
|
|
let scopeDisplay: string;
|
||
|
|
if (token.scope === 'all') {
|
||
|
|
scopeDisplay = 'All services';
|
||
|
|
} else if (Array.isArray(token.scope)) {
|
||
|
|
scopeDisplay = token.scope.length === 1 ? token.scope[0] : `${token.scope.length} services`;
|
||
|
|
} else {
|
||
|
|
scopeDisplay = 'Unknown';
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
result: {
|
||
|
|
token: {
|
||
|
|
id: token.id!,
|
||
|
|
name: token.name,
|
||
|
|
type: token.type,
|
||
|
|
scope: token.scope,
|
||
|
|
scopeDisplay,
|
||
|
|
expiresAt: token.expiresAt,
|
||
|
|
createdAt: token.createdAt,
|
||
|
|
lastUsedAt: token.lastUsedAt,
|
||
|
|
createdBy: token.createdBy,
|
||
|
|
isExpired: false,
|
||
|
|
},
|
||
|
|
plainToken,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
|
||
|
|
// Delete registry token
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteRegistryToken>(
|
||
|
|
'deleteRegistryToken',
|
||
|
|
async (dataArg) => {
|
||
|
|
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
|
||
|
|
const token = this.opsServerRef.oneboxRef.database.getRegistryTokenById(dataArg.tokenId);
|
||
|
|
if (!token) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('Token not found');
|
||
|
|
}
|
||
|
|
this.opsServerRef.oneboxRef.database.deleteRegistryToken(dataArg.tokenId);
|
||
|
|
return { ok: true };
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|