136 lines
3.6 KiB
TypeScript
136 lines
3.6 KiB
TypeScript
/**
|
|
* Organization model for Stack.Gallery Registry
|
|
*/
|
|
|
|
import * as plugins from '../plugins.ts';
|
|
import type {
|
|
IOrganization,
|
|
IOrganizationSettings,
|
|
TOrganizationPlan,
|
|
} from '../interfaces/auth.interfaces.ts';
|
|
import { db } from './db.ts';
|
|
|
|
const DEFAULT_SETTINGS: IOrganizationSettings = {
|
|
requireMfa: false,
|
|
allowPublicRepositories: true,
|
|
defaultRepositoryVisibility: 'private',
|
|
allowedProtocols: ['oci', 'npm', 'maven', 'cargo', 'composer', 'pypi', 'rubygems'],
|
|
};
|
|
|
|
@plugins.smartdata.Collection(() => db)
|
|
export class Organization extends plugins.smartdata.SmartDataDbDoc<Organization, Organization> implements IOrganization {
|
|
@plugins.smartdata.unI()
|
|
public id: string = '';
|
|
|
|
@plugins.smartdata.svDb()
|
|
@plugins.smartdata.searchable()
|
|
@plugins.smartdata.index({ unique: true })
|
|
public name: string = ''; // URL-safe slug
|
|
|
|
@plugins.smartdata.svDb()
|
|
@plugins.smartdata.searchable()
|
|
public displayName: string = '';
|
|
|
|
@plugins.smartdata.svDb()
|
|
public description?: string;
|
|
|
|
@plugins.smartdata.svDb()
|
|
public avatarUrl?: string;
|
|
|
|
@plugins.smartdata.svDb()
|
|
@plugins.smartdata.index()
|
|
public plan: TOrganizationPlan = 'free';
|
|
|
|
@plugins.smartdata.svDb()
|
|
public settings: IOrganizationSettings = DEFAULT_SETTINGS;
|
|
|
|
@plugins.smartdata.svDb()
|
|
public billingEmail?: string;
|
|
|
|
@plugins.smartdata.svDb()
|
|
public isVerified: boolean = false;
|
|
|
|
@plugins.smartdata.svDb()
|
|
public verifiedDomains: string[] = [];
|
|
|
|
@plugins.smartdata.svDb()
|
|
public storageQuotaBytes: number = 5 * 1024 * 1024 * 1024; // 5GB default
|
|
|
|
@plugins.smartdata.svDb()
|
|
public usedStorageBytes: number = 0;
|
|
|
|
@plugins.smartdata.svDb()
|
|
@plugins.smartdata.index()
|
|
public createdAt: Date = new Date();
|
|
|
|
@plugins.smartdata.svDb()
|
|
public updatedAt: Date = new Date();
|
|
|
|
@plugins.smartdata.svDb()
|
|
@plugins.smartdata.index()
|
|
public createdById: string = '';
|
|
|
|
/**
|
|
* Create a new organization
|
|
*/
|
|
public static async createOrganization(data: {
|
|
name: string;
|
|
displayName: string;
|
|
description?: string;
|
|
createdById: string;
|
|
}): Promise<Organization> {
|
|
// Validate name (URL-safe)
|
|
const nameRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
if (!nameRegex.test(data.name)) {
|
|
throw new Error(
|
|
'Organization name must be lowercase alphanumeric with optional hyphens'
|
|
);
|
|
}
|
|
|
|
const org = new Organization();
|
|
org.id = await Organization.getNewId();
|
|
org.name = data.name.toLowerCase();
|
|
org.displayName = data.displayName;
|
|
org.description = data.description;
|
|
org.createdById = data.createdById;
|
|
org.settings = { ...DEFAULT_SETTINGS };
|
|
org.createdAt = new Date();
|
|
org.updatedAt = new Date();
|
|
await org.save();
|
|
return org;
|
|
}
|
|
|
|
/**
|
|
* Find organization by name (slug)
|
|
*/
|
|
public static async findByName(name: string): Promise<Organization | null> {
|
|
return await Organization.getInstance({ name: name.toLowerCase() });
|
|
}
|
|
|
|
/**
|
|
* Check if storage quota is exceeded
|
|
*/
|
|
public hasStorageAvailable(additionalBytes: number): boolean {
|
|
if (this.storageQuotaBytes < 0) return true; // Unlimited
|
|
return this.usedStorageBytes + additionalBytes <= this.storageQuotaBytes;
|
|
}
|
|
|
|
/**
|
|
* Update storage usage
|
|
*/
|
|
public async updateStorageUsage(deltaBytes: number): Promise<void> {
|
|
this.usedStorageBytes = Math.max(0, this.usedStorageBytes + deltaBytes);
|
|
await this.save();
|
|
}
|
|
|
|
/**
|
|
* Lifecycle hook: Update timestamps before save
|
|
*/
|
|
public async beforeSave(): Promise<void> {
|
|
this.updatedAt = new Date();
|
|
if (!this.id) {
|
|
this.id = await Organization.getNewId();
|
|
}
|
|
}
|
|
}
|