feat: implement account settings and API tokens management
- Added SettingsComponent for user profile management, including display name and password change functionality. - Introduced TokensComponent for managing API tokens, including creation and revocation. - Created LayoutComponent for consistent application layout with navigation and user information. - Established main application structure in index.html and main.ts. - Integrated Tailwind CSS for styling and responsive design. - Configured TypeScript settings for strict type checking and module resolution.
This commit is contained in:
195
ts/models/package.ts
Normal file
195
ts/models/package.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Package model for Stack.Gallery Registry
|
||||
*/
|
||||
|
||||
import * as plugins from '../plugins.ts';
|
||||
import type {
|
||||
IPackage,
|
||||
IPackageVersion,
|
||||
IProtocolMetadata,
|
||||
} from '../interfaces/package.interfaces.ts';
|
||||
import type { TRegistryProtocol } from '../interfaces/auth.interfaces.ts';
|
||||
import { getDb } from './db.ts';
|
||||
|
||||
@plugins.smartdata.Collection(() => getDb())
|
||||
export class Package extends plugins.smartdata.SmartDataDbDoc<Package, Package> implements IPackage {
|
||||
@plugins.smartdata.unI()
|
||||
public id: string = ''; // {protocol}:{org}:{name}
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.index()
|
||||
public organizationId: string = '';
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.index()
|
||||
public repositoryId: string = '';
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.index()
|
||||
public protocol: TRegistryProtocol = 'npm';
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.searchable()
|
||||
@plugins.smartdata.index()
|
||||
public name: string = '';
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.searchable()
|
||||
public description?: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public versions: Record<string, IPackageVersion> = {};
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public distTags: Record<string, string> = {}; // e.g., { latest: "1.0.0" }
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public metadata: IProtocolMetadata = { type: 'npm' };
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.index()
|
||||
public isPrivate: boolean = true;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public storageBytes: number = 0;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
@plugins.smartdata.index()
|
||||
public downloadCount: number = 0;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public starCount: number = 0;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public cacheExpiresAt?: Date;
|
||||
|
||||
@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 = '';
|
||||
|
||||
/**
|
||||
* Generate package ID
|
||||
*/
|
||||
public static generateId(protocol: TRegistryProtocol, orgName: string, name: string): string {
|
||||
return `${protocol}:${orgName}:${name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find package by ID
|
||||
*/
|
||||
public static async findById(id: string): Promise<Package | null> {
|
||||
return await Package.getInstance({ id });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find package by protocol, org, and name
|
||||
*/
|
||||
public static async findByName(
|
||||
protocol: TRegistryProtocol,
|
||||
orgName: string,
|
||||
name: string
|
||||
): Promise<Package | null> {
|
||||
const id = Package.generateId(protocol, orgName, name);
|
||||
return await Package.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get packages in an organization
|
||||
*/
|
||||
public static async getOrgPackages(organizationId: string): Promise<Package[]> {
|
||||
return await Package.getInstances({ organizationId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Search packages
|
||||
*/
|
||||
public static async search(
|
||||
query: string,
|
||||
options?: {
|
||||
protocol?: TRegistryProtocol;
|
||||
organizationId?: string;
|
||||
isPrivate?: boolean;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
): Promise<Package[]> {
|
||||
const filter: Record<string, unknown> = {};
|
||||
if (options?.protocol) filter.protocol = options.protocol;
|
||||
if (options?.organizationId) filter.organizationId = options.organizationId;
|
||||
if (options?.isPrivate !== undefined) filter.isPrivate = options.isPrivate;
|
||||
|
||||
// Simple text search - in production, would use MongoDB text index
|
||||
const allPackages = await Package.getInstances(filter);
|
||||
|
||||
// Filter by query
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const filtered = allPackages.filter(
|
||||
(pkg) =>
|
||||
pkg.name.toLowerCase().includes(lowerQuery) ||
|
||||
pkg.description?.toLowerCase().includes(lowerQuery)
|
||||
);
|
||||
|
||||
// Apply pagination
|
||||
const offset = options?.offset || 0;
|
||||
const limit = options?.limit || 50;
|
||||
return filtered.slice(offset, offset + limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new version
|
||||
*/
|
||||
public addVersion(version: IPackageVersion): void {
|
||||
this.versions[version.version] = version;
|
||||
this.storageBytes += version.size;
|
||||
this.updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific version
|
||||
*/
|
||||
public getVersion(version: string): IPackageVersion | undefined {
|
||||
return this.versions[version];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest version
|
||||
*/
|
||||
public getLatestVersion(): IPackageVersion | undefined {
|
||||
const latest = this.distTags['latest'];
|
||||
if (latest) {
|
||||
return this.versions[latest];
|
||||
}
|
||||
// Fallback to most recent
|
||||
const versionList = Object.keys(this.versions);
|
||||
if (versionList.length === 0) return undefined;
|
||||
return this.versions[versionList[versionList.length - 1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment download count
|
||||
*/
|
||||
public async incrementDownloads(version?: string): Promise<void> {
|
||||
this.downloadCount += 1;
|
||||
if (version && this.versions[version]) {
|
||||
this.versions[version].downloads += 1;
|
||||
}
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook
|
||||
*/
|
||||
public async beforeSave(): Promise<void> {
|
||||
this.updatedAt = new Date();
|
||||
if (!this.id) {
|
||||
this.id = await Package.getNewId();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user