Enhance API with OIDC support, including login and callback endpoints. Update environment variables for OIDC configuration in .env.example. Add new features to the catalog service for listing software families, Minecraft versions, and deployment regions. Implement server management actions such as kill, delete, and update in the servers module. Integrate feature flags for maintenance mode in server operations. Update pnpm-lock.yaml with new dependencies and versions.
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 12s
CI / Go — node-agent tests (push) Failing after 9s
CI / Go — edge-gateway build (push) Successful in 17s

This commit is contained in:
TheOnlyMace
2026-07-05 18:39:53 +02:00
parent bf36cb3159
commit 50cd4b3ffd
225 changed files with 17824 additions and 436 deletions

View File

@@ -0,0 +1,42 @@
import { z } from 'zod';
export const abuseReportStatusSchema = z.enum([
'OPEN',
'INVESTIGATING',
'RESOLVED',
'DISMISSED',
]);
export const createAbuseReportSchema = z.object({
reason: z.string().min(1).max(256),
details: z.string().max(4096).optional(),
targetUserId: z.string().uuid().optional(),
serverId: z.string().uuid().optional(),
});
export const abuseReportSchema = z.object({
id: z.string().uuid(),
reporterId: z.string().uuid().nullable(),
targetUserId: z.string().uuid().nullable(),
serverId: z.string().uuid().nullable(),
status: abuseReportStatusSchema,
reason: z.string(),
details: z.string().nullable(),
resolution: z.string().nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
});
export const abuseReportListResponseSchema = z.object({
reports: z.array(abuseReportSchema),
});
export const updateAbuseReportSchema = z.object({
status: abuseReportStatusSchema.optional(),
resolution: z.string().max(4096).optional(),
});
export type CreateAbuseReport = z.infer<typeof createAbuseReportSchema>;
export type AbuseReport = z.infer<typeof abuseReportSchema>;
export type AbuseReportListResponse = z.infer<typeof abuseReportListResponseSchema>;
export type UpdateAbuseReport = z.infer<typeof updateAbuseReportSchema>;

View File

@@ -0,0 +1,25 @@
import { z } from 'zod';
export const dataExportRequestSchema = z.object({
id: z.string().uuid(),
status: z.enum(['PENDING', 'PROCESSING', 'READY', 'FAILED', 'EXPIRED']),
downloadUrl: z.string().url().nullable(),
expiresAt: z.string().datetime().nullable(),
completedAt: z.string().datetime().nullable(),
createdAt: z.string().datetime(),
});
export const accountDeletionRequestSchema = z.object({
id: z.string().uuid(),
status: z.enum(['PENDING', 'SCHEDULED', 'COMPLETED', 'CANCELLED']),
scheduledFor: z.string().datetime(),
completedAt: z.string().datetime().nullable(),
createdAt: z.string().datetime(),
});
export const requestAccountDeletionSchema = z.object({
confirmEmail: z.string().email(),
});
export type DataExportRequest = z.infer<typeof dataExportRequestSchema>;
export type AccountDeletionRequest = z.infer<typeof accountDeletionRequestSchema>;

View File

@@ -21,6 +21,7 @@ export const fileContentSchema = z.object({
path: z.string(),
content: z.string(),
encoding: z.enum(['utf-8', 'base64']).default('utf-8'),
version: z.number().int().optional(),
});
export const readFileContentQuerySchema = z.object({
@@ -31,6 +32,27 @@ export const updateFileContentSchema = z.object({
path: z.string().min(1),
content: z.string(),
encoding: z.enum(['utf-8', 'base64']).default('utf-8'),
version: z.number().int().optional(),
});
export const uploadFileSchema = z.object({
path: z.string().min(1),
content: z.string(),
encoding: z.enum(['utf-8', 'base64']).default('base64'),
});
export const filePathSchema = z.object({
path: z.string().min(1),
});
export const archiveFilesSchema = z.object({
paths: z.array(z.string().min(1)).min(1).max(100),
archiveName: z.string().min(1).max(128).optional(),
});
export const unarchiveFileSchema = z.object({
archivePath: z.string().min(1),
destinationPath: z.string().default('.'),
});
export type ListFilesQuery = z.infer<typeof listFilesQuerySchema>;
@@ -39,3 +61,6 @@ export type FileListResponse = z.infer<typeof fileListResponseSchema>;
export type FileContent = z.infer<typeof fileContentSchema>;
export type ReadFileContentQuery = z.infer<typeof readFileContentQuerySchema>;
export type UpdateFileContent = z.infer<typeof updateFileContentSchema>;
export type UploadFile = z.infer<typeof uploadFileSchema>;
export type ArchiveFiles = z.infer<typeof archiveFilesSchema>;
export type UnarchiveFile = z.infer<typeof unarchiveFileSchema>;

View File

@@ -61,6 +61,7 @@ export {
serverEditionSchema,
softwareFamilySchema,
createServerRequestSchema,
updateServerRequestSchema,
serverResponseSchema,
serverListResponseSchema,
serverActionResponseSchema,
@@ -68,6 +69,7 @@ export {
type ServerEdition,
type SoftwareFamily,
type CreateServerRequest,
type UpdateServerRequest,
type ServerResponse,
type ServerListResponse,
type ServerActionResponse,
@@ -80,12 +82,19 @@ export {
fileContentSchema,
readFileContentQuerySchema,
updateFileContentSchema,
uploadFileSchema,
filePathSchema,
archiveFilesSchema,
unarchiveFileSchema,
type ListFilesQuery,
type FileEntry,
type FileListResponse,
type FileContent,
type ReadFileContentQuery,
type UpdateFileContent,
type UploadFile,
type ArchiveFiles,
type UnarchiveFile,
} from './files';
export {
@@ -108,13 +117,54 @@ export {
onlinePlayerSchema,
whitelistEntrySchema,
operatorEntrySchema,
banEntrySchema,
playerListResponseSchema,
addWhitelistSchema,
addOperatorSchema,
removePlayerEntrySchema,
addBanSchema,
type OnlinePlayer,
type WhitelistEntry,
type OperatorEntry,
type BanEntry,
type PlayerListResponse,
type AddWhitelist,
type AddOperator,
type RemovePlayerEntry,
type AddBan,
} from './players';
export {
scheduleTaskTypeSchema,
scheduleSchema,
createScheduleSchema,
updateScheduleSchema,
scheduleListResponseSchema,
type Schedule,
type CreateSchedule,
type UpdateSchedule,
type ScheduleListResponse,
} from './schedules';
export {
notificationTypeSchema,
notificationChannelSchema,
notificationSchema,
notificationListResponseSchema,
notificationPreferenceSchema,
updateNotificationPreferencesSchema,
type Notification,
type NotificationListResponse,
type UpdateNotificationPreferences,
} from './notifications';
export {
reinstallServerSchema,
reinstallServerResponseSchema,
type ReinstallServer,
type ReinstallServerResponse,
} from './software';
export {
backupStatusSchema,
backupTypeSchema,
@@ -273,3 +323,62 @@ export {
type WhmcsMtlsStatusResponse,
type WhmcsValidatePlanResponse,
} from './whmcs';
export {
dataExportRequestSchema,
accountDeletionRequestSchema,
requestAccountDeletionSchema,
type DataExportRequest,
type AccountDeletionRequest,
} from './compliance';
export {
whmcsServiceActionRequestSchema,
whmcsServiceActionResponseSchema,
type WhmcsServiceActionRequest,
type WhmcsServiceActionResponse,
} from './whmcs-actions';
export {
organizationMemberRoleSchema,
organizationSchema,
organizationListResponseSchema,
createOrganizationSchema,
organizationMemberSchema,
organizationMemberListResponseSchema,
inviteOrganizationMemberSchema,
organizationInviteResponseSchema,
type Organization,
type OrganizationListResponse,
type CreateOrganization,
type OrganizationMember,
type OrganizationMemberListResponse,
type InviteOrganizationMember,
type OrganizationInviteResponse,
} from './organizations';
export {
supportTicketStatusSchema,
supportTicketPrioritySchema,
supportTicketMessageSchema,
supportTicketSchema,
supportTicketListResponseSchema,
createSupportTicketSchema,
addSupportTicketMessageSchema,
type SupportTicket,
type SupportTicketListResponse,
type CreateSupportTicket,
type AddSupportTicketMessage,
} from './support';
export {
abuseReportStatusSchema,
createAbuseReportSchema,
abuseReportSchema,
abuseReportListResponseSchema,
updateAbuseReportSchema,
type CreateAbuseReport,
type AbuseReport,
type AbuseReportListResponse,
type UpdateAbuseReport,
} from './abuse';

View File

@@ -0,0 +1,51 @@
import { z } from 'zod';
export const notificationTypeSchema = z.enum([
'SERVER_READY',
'SERVER_START_FAILED',
'SERVER_STOPPED',
'IDLE_STOP_WARNING',
'BACKUP_SUCCESS',
'BACKUP_FAILED',
'RESTORE_SUCCESS',
'RESTORE_FAILED',
'CREDITS_LOW',
'BILLING_ISSUE',
'INVITE',
'NODE_INCIDENT',
'MAINTENANCE',
'SECURITY',
'NEW_LOGIN',
'TWO_FA_CHANGED',
]);
export const notificationChannelSchema = z.enum(['IN_APP', 'EMAIL', 'WEBHOOK', 'DISCORD']);
export const notificationSchema = z.object({
id: z.string().uuid(),
type: notificationTypeSchema,
channel: notificationChannelSchema,
title: z.string(),
body: z.string(),
readAt: z.string().datetime().nullable(),
createdAt: z.string().datetime(),
});
export const notificationListResponseSchema = z.object({
notifications: z.array(notificationSchema),
unreadCount: z.number().int(),
});
export const notificationPreferenceSchema = z.object({
type: notificationTypeSchema,
channel: notificationChannelSchema,
enabled: z.boolean(),
});
export const updateNotificationPreferencesSchema = z.object({
preferences: z.array(notificationPreferenceSchema),
});
export type Notification = z.infer<typeof notificationSchema>;
export type NotificationListResponse = z.infer<typeof notificationListResponseSchema>;
export type UpdateNotificationPreferences = z.infer<typeof updateNotificationPreferencesSchema>;

View File

@@ -0,0 +1,56 @@
import { z } from 'zod';
export const organizationMemberRoleSchema = z.enum(['OWNER', 'ADMIN', 'MEMBER']);
export const organizationSchema = z.object({
id: z.string().uuid(),
name: z.string(),
slug: z.string(),
role: organizationMemberRoleSchema,
createdAt: z.string().datetime(),
});
export const organizationListResponseSchema = z.object({
organizations: z.array(organizationSchema),
});
export const createOrganizationSchema = z.object({
name: z.string().min(1).max(128),
slug: z.string().min(2).max(64).regex(/^[a-z0-9-]+$/).optional(),
});
export const organizationMemberSchema = z.object({
id: z.string().uuid(),
userId: z.string().uuid(),
role: organizationMemberRoleSchema,
email: z.string().email(),
username: z.string(),
displayName: z.string().nullable(),
createdAt: z.string().datetime(),
});
export const organizationMemberListResponseSchema = z.object({
members: z.array(organizationMemberSchema),
});
export const inviteOrganizationMemberSchema = z.object({
email: z.string().email(),
role: organizationMemberRoleSchema.default('MEMBER'),
});
export const organizationInviteResponseSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
role: organizationMemberRoleSchema,
expiresAt: z.string().datetime(),
});
export type Organization = z.infer<typeof organizationSchema>;
export type OrganizationListResponse = z.infer<typeof organizationListResponseSchema>;
export type CreateOrganization = z.infer<typeof createOrganizationSchema>;
export type OrganizationMember = z.infer<typeof organizationMemberSchema>;
export type OrganizationMemberListResponse = z.infer<
typeof organizationMemberListResponseSchema
>;
export type InviteOrganizationMember = z.infer<typeof inviteOrganizationMemberSchema>;
export type OrganizationInviteResponse = z.infer<typeof organizationInviteResponseSchema>;

View File

@@ -17,13 +17,48 @@ export const operatorEntrySchema = z.object({
bypassesPlayerLimit: z.boolean().optional(),
});
export const banEntrySchema = z.object({
uuid: z.string().uuid().optional(),
name: z.string(),
reason: z.string().optional(),
source: z.string().optional(),
expires: z.string().optional(),
});
export const playerListResponseSchema = z.object({
online: z.array(onlinePlayerSchema),
whitelist: z.array(whitelistEntrySchema),
operators: z.array(operatorEntrySchema),
bans: z.array(banEntrySchema).optional(),
});
export const addWhitelistSchema = z.object({
uuid: z.string().uuid(),
name: z.string().min(1).max(16),
});
export const addOperatorSchema = z.object({
uuid: z.string().uuid(),
name: z.string().min(1).max(16),
level: z.number().int().min(0).max(4).default(4),
bypassesPlayerLimit: z.boolean().default(false),
});
export const removePlayerEntrySchema = z.object({
uuid: z.string().uuid(),
});
export const addBanSchema = z.object({
name: z.string().min(1).max(16),
reason: z.string().max(256).optional(),
});
export type OnlinePlayer = z.infer<typeof onlinePlayerSchema>;
export type WhitelistEntry = z.infer<typeof whitelistEntrySchema>;
export type OperatorEntry = z.infer<typeof operatorEntrySchema>;
export type BanEntry = z.infer<typeof banEntrySchema>;
export type PlayerListResponse = z.infer<typeof playerListResponseSchema>;
export type AddWhitelist = z.infer<typeof addWhitelistSchema>;
export type AddOperator = z.infer<typeof addOperatorSchema>;
export type RemovePlayerEntry = z.infer<typeof removePlayerEntrySchema>;
export type AddBan = z.infer<typeof addBanSchema>;

View File

@@ -0,0 +1,54 @@
import { z } from 'zod';
export const scheduleTaskTypeSchema = z.enum([
'START',
'STOP',
'RESTART',
'BACKUP',
'CONSOLE_COMMAND',
'UPDATE_CHECK',
'MAINTENANCE',
]);
export const scheduledTaskSchema = z.object({
id: z.string().uuid(),
taskType: scheduleTaskTypeSchema,
payload: z.record(z.unknown()).nullable(),
});
export const scheduleSchema = z.object({
id: z.string().uuid(),
name: z.string(),
timezone: z.string(),
cronExpr: z.string(),
enabled: z.boolean(),
nextRunAt: z.string().datetime().nullable(),
lastRunAt: z.string().datetime().nullable(),
tasks: z.array(scheduledTaskSchema),
});
export const createScheduleSchema = z.object({
name: z.string().min(1).max(64),
timezone: z.string().default('UTC'),
cronExpr: z.string().min(5).max(128),
enabled: z.boolean().default(true),
tasks: z
.array(
z.object({
taskType: scheduleTaskTypeSchema,
payload: z.record(z.unknown()).optional(),
}),
)
.min(1),
});
export const updateScheduleSchema = createScheduleSchema.partial();
export const scheduleListResponseSchema = z.object({
schedules: z.array(scheduleSchema),
});
export type Schedule = z.infer<typeof scheduleSchema>;
export type CreateSchedule = z.infer<typeof createScheduleSchema>;
export type UpdateSchedule = z.infer<typeof updateScheduleSchema>;
export type ScheduleListResponse = z.infer<typeof scheduleListResponseSchema>;

View File

@@ -2,6 +2,7 @@ import { z } from 'zod';
export const gameServerStatusSchema = z.enum([
'DRAFT',
'ALLOCATING',
'PROVISIONING',
'INSTALLING',
'STOPPED',
@@ -11,6 +12,9 @@ export const gameServerStatusSchema = z.enum([
'STOPPING',
'BACKING_UP',
'RESTORING',
'MIGRATING',
'SUSPENDED',
'MAINTENANCE',
'UNKNOWN',
'ERROR',
'DELETING',
@@ -42,6 +46,11 @@ export const createServerRequestSchema = z.object({
planId: z.string().uuid().optional(),
});
export const updateServerRequestSchema = z.object({
name: z.string().min(1).max(64).optional(),
idleShutdownEnabled: z.boolean().optional(),
});
export const serverResponseSchema = z.object({
id: z.string().uuid(),
userId: z.string().uuid(),
@@ -82,6 +91,7 @@ export type GameServerStatus = z.infer<typeof gameServerStatusSchema>;
export type ServerEdition = z.infer<typeof serverEditionSchema>;
export type SoftwareFamily = z.infer<typeof softwareFamilySchema>;
export type CreateServerRequest = z.infer<typeof createServerRequestSchema>;
export type UpdateServerRequest = z.infer<typeof updateServerRequestSchema>;
export type ServerResponse = z.infer<typeof serverResponseSchema>;
export type ServerListResponse = z.infer<typeof serverListResponseSchema>;
export type ServerActionResponse = z.infer<typeof serverActionResponseSchema>;

View File

@@ -0,0 +1,18 @@
import { z } from 'zod';
import { softwareFamilySchema } from './servers';
export const reinstallServerSchema = z.object({
softwareFamily: softwareFamilySchema,
minecraftVersion: z.string().min(1).max(32),
createBackup: z.boolean().default(true),
});
export const reinstallServerResponseSchema = z.object({
serverId: z.string().uuid(),
jobId: z.string(),
status: z.string(),
});
export type ReinstallServer = z.infer<typeof reinstallServerSchema>;
export type ReinstallServerResponse = z.infer<typeof reinstallServerResponseSchema>;

View File

@@ -0,0 +1,50 @@
import { z } from 'zod';
export const supportTicketStatusSchema = z.enum([
'OPEN',
'IN_PROGRESS',
'WAITING',
'RESOLVED',
'CLOSED',
]);
export const supportTicketPrioritySchema = z.enum(['LOW', 'NORMAL', 'HIGH', 'URGENT']);
export const supportTicketMessageSchema = z.object({
id: z.string().uuid(),
authorId: z.string().uuid().nullable(),
isInternal: z.boolean(),
body: z.string(),
createdAt: z.string().datetime(),
});
export const supportTicketSchema = z.object({
id: z.string().uuid(),
subject: z.string(),
status: supportTicketStatusSchema,
priority: supportTicketPrioritySchema,
serverId: z.string().uuid().nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
messages: z.array(supportTicketMessageSchema).optional(),
});
export const supportTicketListResponseSchema = z.object({
tickets: z.array(supportTicketSchema),
});
export const createSupportTicketSchema = z.object({
subject: z.string().min(1).max(256),
body: z.string().min(1).max(8192),
priority: supportTicketPrioritySchema.default('NORMAL'),
serverId: z.string().uuid().optional(),
});
export const addSupportTicketMessageSchema = z.object({
body: z.string().min(1).max(8192),
});
export type SupportTicket = z.infer<typeof supportTicketSchema>;
export type SupportTicketListResponse = z.infer<typeof supportTicketListResponseSchema>;
export type CreateSupportTicket = z.infer<typeof createSupportTicketSchema>;
export type AddSupportTicketMessage = z.infer<typeof addSupportTicketMessageSchema>;

View File

@@ -0,0 +1,12 @@
import { z } from 'zod';
import { serverActionResponseSchema } from './servers';
export const whmcsServiceActionRequestSchema = z.object({
reason: z.string().max(256).optional(),
});
export const whmcsServiceActionResponseSchema = serverActionResponseSchema;
export type WhmcsServiceActionRequest = z.infer<typeof whmcsServiceActionRequestSchema>;
export type WhmcsServiceActionResponse = z.infer<typeof whmcsServiceActionResponseSchema>;