Files
registry/ts/models/apitoken.ts
Juergen Kunz ab88ac896f 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.
2025-11-27 22:15:38 +00:00

168 lines
3.8 KiB
TypeScript

/**
* ApiToken model for Stack.Gallery Registry
*/
import * as plugins from '../plugins.ts';
import type { IApiToken, ITokenScope, TRegistryProtocol } from '../interfaces/auth.interfaces.ts';
import { getDb } from './db.ts';
@plugins.smartdata.Collection(() => getDb())
export class ApiToken
extends plugins.smartdata.SmartDataDbDoc<ApiToken, ApiToken>
implements IApiToken
{
@plugins.smartdata.unI()
public id: string = '';
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public userId: string = '';
@plugins.smartdata.svDb()
public name: string = '';
@plugins.smartdata.svDb()
@plugins.smartdata.index({ unique: true })
public tokenHash: string = '';
@plugins.smartdata.svDb()
public tokenPrefix: string = '';
@plugins.smartdata.svDb()
public protocols: TRegistryProtocol[] = [];
@plugins.smartdata.svDb()
public scopes: ITokenScope[] = [];
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public expiresAt?: Date;
@plugins.smartdata.svDb()
public lastUsedAt?: Date;
@plugins.smartdata.svDb()
public lastUsedIp?: string;
@plugins.smartdata.svDb()
public usageCount: number = 0;
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public isRevoked: boolean = false;
@plugins.smartdata.svDb()
public revokedAt?: Date;
@plugins.smartdata.svDb()
public revokedReason?: string;
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public createdAt: Date = new Date();
@plugins.smartdata.svDb()
public createdIp?: string;
/**
* Find token by hash
*/
public static async findByHash(tokenHash: string): Promise<ApiToken | null> {
return await ApiToken.getInstance({
tokenHash,
isRevoked: false,
});
}
/**
* Find token by prefix (for listing)
*/
public static async findByPrefix(tokenPrefix: string): Promise<ApiToken | null> {
return await ApiToken.getInstance({
tokenPrefix,
});
}
/**
* Get all tokens for a user
*/
public static async getUserTokens(userId: string): Promise<ApiToken[]> {
return await ApiToken.getInstances({
userId,
isRevoked: false,
});
}
/**
* Check if token is valid (not expired, not revoked)
*/
public isValid(): boolean {
if (this.isRevoked) return false;
if (this.expiresAt && this.expiresAt < new Date()) return false;
return true;
}
/**
* Record token usage
*/
public async recordUsage(ip?: string): Promise<void> {
this.lastUsedAt = new Date();
this.lastUsedIp = ip;
this.usageCount += 1;
await this.save();
}
/**
* Revoke token
*/
public async revoke(reason?: string): Promise<void> {
this.isRevoked = true;
this.revokedAt = new Date();
this.revokedReason = reason;
await this.save();
}
/**
* Check if token has permission for protocol
*/
public hasProtocol(protocol: TRegistryProtocol): boolean {
return this.protocols.includes(protocol) || this.protocols.includes('*' as TRegistryProtocol);
}
/**
* Check if token has permission for action on resource
*/
public hasScope(
protocol: TRegistryProtocol,
organizationId?: string,
repositoryId?: string,
action?: string
): boolean {
for (const scope of this.scopes) {
// Check protocol
if (scope.protocol !== '*' && scope.protocol !== protocol) continue;
// Check organization
if (scope.organizationId && scope.organizationId !== organizationId) continue;
// Check repository
if (scope.repositoryId && scope.repositoryId !== repositoryId) continue;
// Check action
if (action && !scope.actions.includes('*') && !scope.actions.includes(action as never)) continue;
return true;
}
return false;
}
/**
* Lifecycle hook
*/
public async beforeSave(): Promise<void> {
if (!this.id) {
this.id = await ApiToken.getNewId();
}
}
}