mc-manager/src/lib/models.ts
2026-02-13 15:16:54 -08:00

200 lines
7.1 KiB
TypeScript

import mongoose, { Schema, Document, Model } from 'mongoose'
// ─── User ────────────────────────────────────────────────────────
export interface IUser extends Document {
username: string
email: string
passwordHash: string
roles: mongoose.Types.ObjectId[]
twoFactorCode: string | null
twoFactorExpiry: Date | null
loginAttempts: number
lockUntil: Date | null
status: 'active' | 'inactive' | 'locked'
lastLogin: Date | null
createdAt: Date
updatedAt: Date
}
const userSchema = new Schema<IUser>(
{
username: { type: String, required: true, unique: true, trim: true },
email: { type: String, required: true, unique: true, trim: true, lowercase: true },
passwordHash: { type: String, required: true },
roles: [{ type: Schema.Types.ObjectId, ref: 'Role' }],
twoFactorCode: { type: String, default: null },
twoFactorExpiry: { type: Date, default: null },
loginAttempts: { type: Number, default: 0 },
lockUntil: { type: Date, default: null },
status: { type: String, enum: ['active', 'inactive', 'locked'], default: 'active' },
lastLogin: { type: Date, default: null },
},
{ timestamps: true }
)
// ─── Role ────────────────────────────────────────────────────────
export interface IPermission {
resource: string
actions: string[]
}
export interface IRole extends Document {
name: string
permissions: IPermission[]
description: string
isDefault: boolean
createdAt: Date
updatedAt: Date
}
const roleSchema = new Schema<IRole>(
{
name: { type: String, required: true, unique: true, trim: true },
permissions: [
{
resource: { type: String, required: true },
actions: [{ type: String, required: true }],
},
],
description: { type: String, default: '' },
isDefault: { type: Boolean, default: false },
},
{ timestamps: true }
)
// ─── Server ──────────────────────────────────────────────────────
export interface IServer extends Document {
name: string
type: 'vanilla' | 'bukkit' | 'forge' | 'fabric'
version: string
dockerImage: string
containerId: string | null
containerName: string
port: number
rconPort: number | null
status: 'online' | 'offline' | 'starting' | 'stopping' | 'crashed'
maxPlayers: number
memory: { min: number; max: number }
jvmArgs: string[]
autoStart: boolean
autoRestart: boolean
backupSchedule: string | null
backupRetention: number
admins: mongoose.Types.ObjectId[]
createdBy: mongoose.Types.ObjectId
createdAt: Date
updatedAt: Date
}
const serverSchema = new Schema<IServer>(
{
name: { type: String, required: true, trim: true },
type: { type: String, required: true, enum: ['vanilla', 'bukkit', 'forge', 'fabric'] },
version: { type: String, required: true },
dockerImage: { type: String, default: 'itzg/minecraft-server' },
containerId: { type: String, default: null },
containerName: { type: String, required: true, unique: true },
port: { type: Number, required: true },
rconPort: { type: Number, default: null },
status: {
type: String,
enum: ['online', 'offline', 'starting', 'stopping', 'crashed'],
default: 'offline',
},
maxPlayers: { type: Number, default: 20 },
memory: {
min: { type: Number, default: 512 },
max: { type: Number, default: 1024 },
},
jvmArgs: [{ type: String }],
autoStart: { type: Boolean, default: false },
autoRestart: { type: Boolean, default: true },
backupSchedule: { type: String, default: null },
backupRetention: { type: Number, default: 5 },
admins: [{ type: Schema.Types.ObjectId, ref: 'User', default: [] }],
createdBy: { type: Schema.Types.ObjectId, ref: 'User', required: true },
},
{ timestamps: true }
)
// ─── Backup ──────────────────────────────────────────────────────
export interface IBackup extends Document {
serverId: mongoose.Types.ObjectId
filename: string
filePath: string
fileSize: number
type: 'manual' | 'scheduled'
status: 'completed' | 'in_progress' | 'failed'
createdBy: mongoose.Types.ObjectId
createdAt: Date
}
const backupSchema = new Schema<IBackup>(
{
serverId: { type: Schema.Types.ObjectId, ref: 'Server', required: true, index: true },
filename: { type: String, required: true },
filePath: { type: String, required: true },
fileSize: { type: Number, default: 0 },
type: { type: String, enum: ['manual', 'scheduled'], required: true },
status: {
type: String,
enum: ['completed', 'in_progress', 'failed'],
default: 'in_progress',
},
createdBy: { type: Schema.Types.ObjectId, ref: 'User' },
},
{ timestamps: true }
)
// ─── Audit Log ───────────────────────────────────────────────────
export interface IAuditLog extends Document {
action: string
entityType: string
entityId?: string
entityName?: string
userId?: string
userName?: string
userEmail?: string
previousValues: Record<string, unknown> | null
newValues: Record<string, unknown> | null
changes: Record<string, string> | null
clientIP: string
status: 'SUCCESS' | 'FAILED'
statusCode: number
errorMessage?: string
createdAt: Date
}
const auditLogSchema = new Schema<IAuditLog>(
{
action: { type: String, required: true, index: true },
entityType: { type: String, required: true, index: true },
entityId: { type: String, default: '' },
entityName: { type: String, default: '' },
userId: { type: String, default: '', index: true },
userName: { type: String, default: '' },
userEmail: { type: String, default: '' },
previousValues: { type: Schema.Types.Mixed, default: null },
newValues: { type: Schema.Types.Mixed, default: null },
changes: { type: Schema.Types.Mixed, default: null },
clientIP: { type: String, default: '' },
status: { type: String, enum: ['SUCCESS', 'FAILED'], required: true },
statusCode: { type: Number, required: true },
errorMessage: { type: String },
},
{ timestamps: true }
)
// ─── Model Exports (prevent re-compilation in dev) ───────────────
export const User: Model<IUser> = mongoose.models.User || mongoose.model<IUser>('User', userSchema)
export const Role: Model<IRole> = mongoose.models.Role || mongoose.model<IRole>('Role', roleSchema)
export const Server: Model<IServer> = mongoose.models.Server || mongoose.model<IServer>('Server', serverSchema)
export const Backup: Model<IBackup> = mongoose.models.Backup || mongoose.model<IBackup>('Backup', backupSchema)
export const AuditLog: Model<IAuditLog> = mongoose.models.AuditLog || mongoose.model<IAuditLog>('AuditLog', auditLogSchema)