UNPKG

recoder-code

Version:

🚀 AI-powered development platform - Chat with 32+ models, build projects, automate workflows. Free models included!

369 lines (302 loc) • 9.46 kB
/** * User Entity * Represents users who can publish and manage packages */ import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany, Index } from 'typeorm'; import { Package } from './Package'; import { Download } from './Download'; import { ApiKey } from './ApiKey'; export enum UserRole { USER = 'user', MAINTAINER = 'maintainer', ADMIN = 'admin', MODERATOR = 'moderator' } export enum UserStatus { ACTIVE = 'active', SUSPENDED = 'suspended', PENDING = 'pending', BANNED = 'banned' } @Entity('users') @Index(['username'], { unique: true }) @Index(['email'], { unique: true }) @Index(['npm_username'], { unique: true }) export class User { @PrimaryGeneratedColumn('uuid') id!: string; @Column({ type: 'varchar', length: 100, unique: true }) username!: string; @Column({ type: 'varchar', length: 255, unique: true }) email!: string; @Column({ type: 'varchar', length: 255, nullable: true }) full_name?: string; @Column({ type: 'text', nullable: true }) bio?: string; @Column({ type: 'varchar', length: 255, nullable: true }) avatar_url?: string; @Column({ type: 'varchar', length: 255, nullable: true }) website?: string; @Column({ type: 'varchar', length: 100, nullable: true }) location?: string; @Column({ type: 'varchar', length: 50, nullable: true }) timezone?: string; @Column({ type: 'varchar', length: 255 }) password_hash!: string; @Column({ type: 'varchar', length: 100, unique: true, nullable: true }) npm_username?: string; @Column({ type: 'varchar', length: 255, nullable: true }) npm_email?: string; @Column({ type: 'enum', enum: UserRole, default: UserRole.USER }) role!: UserRole; @Column({ type: 'enum', enum: UserStatus, default: UserStatus.ACTIVE }) status!: UserStatus; @Column({ type: 'boolean', default: false }) email_verified!: boolean; @Column({ type: 'boolean', default: true }) is_active!: boolean; @Column({ type: 'varchar', length: 255, nullable: true }) github_username?: string; @Column({ type: 'varchar', length: 255, nullable: true }) twitter_username?: string; @Column({ type: 'json', nullable: true }) preferences?: { email_notifications: boolean; weekly_digest: boolean; security_alerts: boolean; marketing_emails: boolean; theme: 'light' | 'dark' | 'auto'; language: string; }; @Column({ type: 'json', nullable: true }) permissions?: { can_publish: boolean; can_unpublish: boolean; can_deprecate: boolean; can_manage_teams: boolean; can_moderate: boolean; max_package_size: number; max_packages: number; }; @Column({ type: 'json', nullable: true }) stats?: { packages_published: number; total_downloads: number; followers: number; following: number; packages_maintained: number; }; @Column({ type: 'json', nullable: true }) organizations?: string[]; @Column({ type: 'varchar', length: 255, nullable: true }) verification_token?: string; @Column({ type: 'timestamp', nullable: true }) verification_expires?: Date; @Column({ type: 'varchar', length: 255, nullable: true }) reset_token?: string; @Column({ type: 'timestamp', nullable: true }) reset_expires?: Date; @Column({ type: 'timestamp', nullable: true }) last_login?: Date; @Column({ type: 'varchar', length: 45, nullable: true }) last_login_ip?: string; @Column({ type: 'int', default: 0 }) login_count!: number; @Column({ type: 'timestamp', nullable: true }) suspended_until?: Date; @Column({ type: 'text', nullable: true }) suspension_reason?: string; @CreateDateColumn() created_at!: Date; @UpdateDateColumn() updated_at!: Date; // Relationships @OneToMany(() => Package, pkg => pkg.owner) packages!: Package[]; @OneToMany(() => Download, download => download.user) downloads!: Download[]; @OneToMany(() => ApiKey, apiKey => apiKey.user, { cascade: true }) api_keys!: ApiKey[]; // Virtual properties get is_admin(): boolean { return this.role === UserRole.ADMIN; } get is_moderator(): boolean { return this.role === UserRole.MODERATOR || this.is_admin; } get is_maintainer(): boolean { return this.role === UserRole.MAINTAINER || this.is_moderator; } get can_publish(): boolean { return this.permissions?.can_publish !== false && this.status === UserStatus.ACTIVE; } get can_manage(): boolean { return this.permissions?.can_manage_teams !== false && this.is_maintainer; } get display_name(): string { return this.full_name || this.username; } get npm_display_name(): string { return this.npm_username || this.username; } // Methods recordLogin(ip?: string) { this.last_login = new Date(); this.login_count += 1; if (ip) { this.last_login_ip = ip; } } suspend(reason: string, until?: Date) { this.status = UserStatus.SUSPENDED; this.suspension_reason = reason; this.suspended_until = until; } unsuspend() { this.status = UserStatus.ACTIVE; this.suspension_reason = undefined; this.suspended_until = undefined; } ban(reason: string) { this.status = UserStatus.BANNED; this.suspension_reason = reason; this.is_active = false; } activate() { this.status = UserStatus.ACTIVE; this.is_active = true; this.suspension_reason = undefined; this.suspended_until = undefined; } verifyEmail() { this.email_verified = true; this.verification_token = undefined; this.verification_expires = undefined; if (this.status === UserStatus.PENDING) { this.status = UserStatus.ACTIVE; } } generateVerificationToken(): string { const token = require('crypto').randomBytes(32).toString('hex'); this.verification_token = token; this.verification_expires = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours return token; } generateResetToken(): string { const token = require('crypto').randomBytes(32).toString('hex'); this.reset_token = token; this.reset_expires = new Date(Date.now() + 60 * 60 * 1000); // 1 hour return token; } clearResetToken() { this.reset_token = undefined; this.reset_expires = undefined; } updateStats(statsUpdate: Partial<User['stats']>) { if (!statsUpdate) return; const currentStats = this.stats || { packages_published: 0, total_downloads: 0, followers: 0, following: 0, packages_maintained: 0 }; this.stats = { packages_published: statsUpdate.packages_published ?? currentStats.packages_published, total_downloads: statsUpdate.total_downloads ?? currentStats.total_downloads, followers: statsUpdate.followers ?? currentStats.followers, following: statsUpdate.following ?? currentStats.following, packages_maintained: statsUpdate.packages_maintained ?? currentStats.packages_maintained }; } addOrganization(org: string) { if (!this.organizations) { this.organizations = []; } if (!this.organizations.includes(org)) { this.organizations.push(org); } } removeOrganization(org: string) { if (this.organizations) { this.organizations = this.organizations.filter(o => o !== org); } } hasPermission(permission: keyof User['permissions']): boolean { if (this.is_admin) return true; return this.permissions?.[permission] === true; } canAccessPackage(pkg: Package): boolean { if (this.is_admin) return true; if (pkg.owner_id === this.id) return true; if (pkg.organization && this.organizations?.includes(pkg.organization)) return true; return false; } canModifyPackage(pkg: Package): boolean { if (this.is_admin) return true; if (pkg.owner_id === this.id) return true; // Check if user is maintainer of the package if (pkg.maintainers) { const maintainer = pkg.maintainers.find(m => m.name === this.username || m.email === this.email ); if (maintainer) return true; } return false; } getRateLimits(): { requests: number; window: number } { if (this.is_admin) { return { requests: 10000, window: 3600 }; // 10k/hour } if (this.is_maintainer) { return { requests: 5000, window: 3600 }; // 5k/hour } return { requests: 1000, window: 3600 }; // 1k/hour } toNpmFormat(): any { return { _id: `org.couchdb.user:${this.npm_username || this.username}`, name: this.npm_username || this.username, email: this.npm_email || this.email, type: 'user', roles: [], date: this.created_at.toISOString() }; } toPublicFormat(): any { return { id: this.id, username: this.username, full_name: this.full_name, bio: this.bio, avatar_url: this.avatar_url, website: this.website, location: this.location, github_username: this.github_username, twitter_username: this.twitter_username, created_at: this.created_at, stats: this.stats, organizations: this.organizations || [] }; } toPrivateFormat(): any { return { ...this.toPublicFormat(), email: this.email, email_verified: this.email_verified, role: this.role, status: this.status, preferences: this.preferences, permissions: this.permissions, last_login: this.last_login }; } }