Refactor code structure for improved readability and maintainability

This commit is contained in:
2025-11-27 23:47:33 +00:00
parent ab88ac896f
commit 9f5e7e2558
23 changed files with 13024 additions and 109 deletions

View File

@@ -4,9 +4,9 @@
import * as plugins from '../plugins.ts';
import type { IUser, TUserStatus } from '../interfaces/auth.interfaces.ts';
import { getDb } from './db.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => getDb())
@plugins.smartdata.Collection(() => db)
export class User extends plugins.smartdata.SmartDataDbDoc<User, User> implements IUser {
@plugins.smartdata.unI()
public id: string = '';
@@ -112,4 +112,119 @@ export class User extends plugins.smartdata.SmartDataDbDoc<User, User> implement
this.id = await User.getNewId();
}
}
/**
* Check if user is active (status === 'active')
*/
public get isActive(): boolean {
return this.status === 'active';
}
/**
* Alias for isPlatformAdmin for backward compatibility
*/
public get isSystemAdmin(): boolean {
return this.isPlatformAdmin;
}
/**
* Find user by ID
*/
public static async findById(id: string): Promise<User | null> {
return await User.getInstance({ id });
}
/**
* Verify password against stored hash
*/
public async verifyPassword(password: string): Promise<boolean> {
if (!this.passwordHash) return false;
const [saltHex, expectedHash] = this.passwordHash.split(':');
if (!saltHex || !expectedHash) return false;
const encoder = new TextEncoder();
const data = encoder.encode(saltHex + password);
let hash = data;
for (let i = 0; i < 10000; i++) {
hash = new Uint8Array(await crypto.subtle.digest('SHA-256', hash));
}
const hashHex = Array.from(hash)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
return hashHex === expectedHash;
}
/**
* Hash a password for storage
*/
public static async hashPassword(password: string): Promise<string> {
const salt = crypto.getRandomValues(new Uint8Array(16));
const saltHex = Array.from(salt)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
const encoder = new TextEncoder();
const data = encoder.encode(saltHex + password);
let hash = data;
for (let i = 0; i < 10000; i++) {
hash = new Uint8Array(await crypto.subtle.digest('SHA-256', hash));
}
const hashHex = Array.from(hash)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
return `${saltHex}:${hashHex}`;
}
/**
* Create the default admin user if no admin exists
*/
public static async seedDefaultAdmin(): Promise<User | null> {
const adminEmail = Deno.env.get('ADMIN_EMAIL') || 'admin@stack.gallery';
const adminPassword = Deno.env.get('ADMIN_PASSWORD') || 'admin';
// Check if any platform admin exists
const existingAdmin = await User.getInstance({ isPlatformAdmin: true });
if (existingAdmin) {
console.log('[User] Platform admin already exists, skipping seed');
return null;
}
// Check if admin email already exists
const existingUser = await User.findByEmail(adminEmail);
if (existingUser) {
console.log('[User] User with admin email already exists, promoting to admin');
existingUser.isPlatformAdmin = true;
existingUser.status = 'active';
await existingUser.save();
return existingUser;
}
// Create new admin user
console.log('[User] Creating default admin user:', adminEmail);
const passwordHash = await User.hashPassword(adminPassword);
const admin = new User();
admin.id = await User.getNewId();
admin.email = adminEmail.toLowerCase();
admin.username = 'admin';
admin.passwordHash = passwordHash;
admin.displayName = 'System Administrator';
admin.status = 'active';
admin.emailVerified = true;
admin.isPlatformAdmin = true;
admin.createdAt = new Date();
admin.updatedAt = new Date();
await admin.save();
console.log('[User] Default admin user created successfully');
return admin;
}
}