Show provisioning requesters and reserve Maxis for system actions
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
export type UserRole = 'admin' | 'operator' | 'viewer';
|
||||
|
||||
/** Reserved identity for work requested by the controller. */
|
||||
export const systemUsername = 'Maxis';
|
||||
|
||||
export interface AuthUser {
|
||||
id: string;
|
||||
username: string;
|
||||
|
||||
@@ -29,7 +29,7 @@ export function provisioningMatchesSource(row: Pick<ProvisioningListRow, 'source
|
||||
|
||||
export type ProvisioningListRow = Pick<ProvisioningDeployment,
|
||||
'id' | 'server' | 'server_deleted_at' | 'action' | 'state' | 'stage' | 'started_at' |
|
||||
'finished_at' | 'triggered_by' | 'error' | 'warnings' | 'server_exists'> & {
|
||||
'finished_at' | 'triggered_by' | 'requested_by' | 'error' | 'warnings' | 'server_exists'> & {
|
||||
canCancelRemoval: number; canCancel?: number; sources?: ProvisioningSource[];
|
||||
phase?: string | null; phaseStartedAt?: string | null; waitCode?: string | null;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createHash, randomBytes, randomUUID, timingSafeEqual } from 'node:crypt
|
||||
import { db } from '../db/index.js';
|
||||
import { ensureAuthSchema } from './schema.js';
|
||||
import { hashPassword, verifyPassword } from './passwords.js';
|
||||
import type { AuthUser, UserRole } from '../../auth.js';
|
||||
import { systemUsername, type AuthUser, type UserRole } from '../../auth.js';
|
||||
|
||||
export const SESSION_COOKIE = '__Host-ec_session';
|
||||
export const SESSION_LIFETIME_MS = 12 * 60 * 60_000;
|
||||
@@ -25,7 +25,9 @@ export function listUsers(): AuthUser[] { return (database().prepare('SELECT * F
|
||||
export function setupRequired(): boolean { return !(database().prepare('SELECT 1 FROM auth_users LIMIT 1').get()); }
|
||||
function username(value: unknown): string {
|
||||
if (typeof value !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{2,63}$/.test(value)) throw new AuthError('Usernames need 3–64 letters, numbers, dots, underscores or hyphens.');
|
||||
return value.toLowerCase();
|
||||
const normalized = value.toLowerCase();
|
||||
if (normalized === systemUsername.toLowerCase()) throw new AuthError(`${systemUsername} is reserved for system actions. Choose another username.`);
|
||||
return normalized;
|
||||
}
|
||||
function displayName(value: unknown, fallback: string): string {
|
||||
const name = value === undefined ? fallback : value;
|
||||
|
||||
@@ -303,7 +303,7 @@ const reviewDeployment = `d.state IN ('failed', 'interrupted', 'warning') AND NO
|
||||
SELECT 1 FROM retention_controls c WHERE c.category='provisioning' AND c.record_id=d.id
|
||||
AND c.resolved_at >= COALESCE(d.finished_at,d.started_at))`;
|
||||
const deploymentListColumns = `d.id, d.server, d.server_deleted_at, d.action, d.state, d.stage,
|
||||
d.started_at, d.finished_at, d.triggered_by, d.error, d.warnings,
|
||||
d.started_at, d.finished_at, d.triggered_by, d.requested_by, d.error, d.warnings,
|
||||
d.phase_key AS phase, d.phase_started_at AS phaseStartedAt,
|
||||
CASE WHEN json_valid(d.observation_json) THEN json_extract(d.observation_json,'$.blocker.code') END AS waitCode,
|
||||
(d.server_deleted_at IS NULL AND EXISTS(SELECT 1 FROM servers s WHERE s.name = d.server AND s.destroyed_at IS NULL)) AS server_exists,
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { provisioningPhases, type ProvisioningDetail, type ProvisioningEvent, type ProvisioningEventPage, type ProvisioningPhase } from '$lib/provisioning-detail.js';
|
||||
import { provisioningPhases, type ProvisioningDeployment, type ProvisioningDetail, type ProvisioningEvent, type ProvisioningEventPage, type ProvisioningPhase } from '$lib/provisioning-detail.js';
|
||||
import { actionCompleted, actionLabels, provisioningActions, type ProvisioningAction } from '$lib/provisioning.js';
|
||||
import { systemUsername } from '$lib/auth.js';
|
||||
|
||||
export function provisioningRequester(operation: Pick<ProvisioningDeployment, 'requested_by' | 'triggered_by'>): string {
|
||||
return operation.requested_by ?? (operation.triggered_by === 'ui' ? 'Not recorded' : systemUsername);
|
||||
}
|
||||
|
||||
export const activeProvisioningStates = ['queued', 'waiting', 'running', 'awaiting-health'];
|
||||
export type ProgressState = 'done' | 'current' | 'waiting' | 'failed' | 'pending' | 'cancelled' | 'unknown';
|
||||
|
||||
Reference in New Issue
Block a user