158 lines
4.5 KiB
TypeScript
158 lines
4.5 KiB
TypeScript
|
|
import * as interfaces from '../ts_interfaces/index.js';
|
||
|
|
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
|
||
|
|
|
||
|
|
export class ApiToken {
|
||
|
|
private clientRef: DcRouterApiClient;
|
||
|
|
|
||
|
|
// Data from IApiTokenInfo
|
||
|
|
public id: string;
|
||
|
|
public name: string;
|
||
|
|
public scopes: interfaces.data.TApiTokenScope[];
|
||
|
|
public createdAt: number;
|
||
|
|
public expiresAt: number | null;
|
||
|
|
public lastUsedAt: number | null;
|
||
|
|
public enabled: boolean;
|
||
|
|
|
||
|
|
/** Only set on creation or roll. Not persisted on server side. */
|
||
|
|
public tokenValue?: string;
|
||
|
|
|
||
|
|
constructor(clientRef: DcRouterApiClient, data: interfaces.data.IApiTokenInfo, tokenValue?: string) {
|
||
|
|
this.clientRef = clientRef;
|
||
|
|
this.id = data.id;
|
||
|
|
this.name = data.name;
|
||
|
|
this.scopes = data.scopes;
|
||
|
|
this.createdAt = data.createdAt;
|
||
|
|
this.expiresAt = data.expiresAt;
|
||
|
|
this.lastUsedAt = data.lastUsedAt;
|
||
|
|
this.enabled = data.enabled;
|
||
|
|
this.tokenValue = tokenValue;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async revoke(): Promise<void> {
|
||
|
|
const response = await this.clientRef.request<interfaces.requests.IReq_RevokeApiToken>(
|
||
|
|
'revokeApiToken',
|
||
|
|
this.clientRef.buildRequestPayload({ id: this.id }) as any,
|
||
|
|
);
|
||
|
|
if (!response.success) {
|
||
|
|
throw new Error(response.message || 'Failed to revoke token');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public async roll(): Promise<string> {
|
||
|
|
const response = await this.clientRef.request<interfaces.requests.IReq_RollApiToken>(
|
||
|
|
'rollApiToken',
|
||
|
|
this.clientRef.buildRequestPayload({ id: this.id }) as any,
|
||
|
|
);
|
||
|
|
if (!response.success) {
|
||
|
|
throw new Error(response.message || 'Failed to roll token');
|
||
|
|
}
|
||
|
|
this.tokenValue = response.tokenValue;
|
||
|
|
return response.tokenValue!;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async toggle(enabled: boolean): Promise<void> {
|
||
|
|
const response = await this.clientRef.request<interfaces.requests.IReq_ToggleApiToken>(
|
||
|
|
'toggleApiToken',
|
||
|
|
this.clientRef.buildRequestPayload({ id: this.id, enabled }) as any,
|
||
|
|
);
|
||
|
|
if (!response.success) {
|
||
|
|
throw new Error(response.message || 'Failed to toggle token');
|
||
|
|
}
|
||
|
|
this.enabled = enabled;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export class ApiTokenBuilder {
|
||
|
|
private clientRef: DcRouterApiClient;
|
||
|
|
private tokenName: string = '';
|
||
|
|
private tokenScopes: interfaces.data.TApiTokenScope[] = [];
|
||
|
|
private tokenExpiresInDays?: number | null;
|
||
|
|
|
||
|
|
constructor(clientRef: DcRouterApiClient) {
|
||
|
|
this.clientRef = clientRef;
|
||
|
|
}
|
||
|
|
|
||
|
|
public setName(name: string): this {
|
||
|
|
this.tokenName = name;
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
public setScopes(scopes: interfaces.data.TApiTokenScope[]): this {
|
||
|
|
this.tokenScopes = scopes;
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
public addScope(scope: interfaces.data.TApiTokenScope): this {
|
||
|
|
if (!this.tokenScopes.includes(scope)) {
|
||
|
|
this.tokenScopes.push(scope);
|
||
|
|
}
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
public setExpiresInDays(days: number | null): this {
|
||
|
|
this.tokenExpiresInDays = days;
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async save(): Promise<ApiToken> {
|
||
|
|
const response = await this.clientRef.request<interfaces.requests.IReq_CreateApiToken>(
|
||
|
|
'createApiToken',
|
||
|
|
this.clientRef.buildRequestPayload({
|
||
|
|
name: this.tokenName,
|
||
|
|
scopes: this.tokenScopes,
|
||
|
|
expiresInDays: this.tokenExpiresInDays,
|
||
|
|
}) as any,
|
||
|
|
);
|
||
|
|
if (!response.success) {
|
||
|
|
throw new Error(response.message || 'Failed to create API token');
|
||
|
|
}
|
||
|
|
return new ApiToken(
|
||
|
|
this.clientRef,
|
||
|
|
{
|
||
|
|
id: response.tokenId!,
|
||
|
|
name: this.tokenName,
|
||
|
|
scopes: this.tokenScopes,
|
||
|
|
createdAt: Date.now(),
|
||
|
|
expiresAt: this.tokenExpiresInDays
|
||
|
|
? Date.now() + this.tokenExpiresInDays * 24 * 60 * 60 * 1000
|
||
|
|
: null,
|
||
|
|
lastUsedAt: null,
|
||
|
|
enabled: true,
|
||
|
|
},
|
||
|
|
response.tokenValue,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export class ApiTokenManager {
|
||
|
|
private clientRef: DcRouterApiClient;
|
||
|
|
|
||
|
|
constructor(clientRef: DcRouterApiClient) {
|
||
|
|
this.clientRef = clientRef;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async list(): Promise<ApiToken[]> {
|
||
|
|
const response = await this.clientRef.request<interfaces.requests.IReq_ListApiTokens>(
|
||
|
|
'listApiTokens',
|
||
|
|
this.clientRef.buildRequestPayload() as any,
|
||
|
|
);
|
||
|
|
return response.tokens.map((t) => new ApiToken(this.clientRef, t));
|
||
|
|
}
|
||
|
|
|
||
|
|
public async create(options: {
|
||
|
|
name: string;
|
||
|
|
scopes: interfaces.data.TApiTokenScope[];
|
||
|
|
expiresInDays?: number | null;
|
||
|
|
}): Promise<ApiToken> {
|
||
|
|
return this.build()
|
||
|
|
.setName(options.name)
|
||
|
|
.setScopes(options.scopes)
|
||
|
|
.setExpiresInDays(options.expiresInDays ?? null)
|
||
|
|
.save();
|
||
|
|
}
|
||
|
|
|
||
|
|
public build(): ApiTokenBuilder {
|
||
|
|
return new ApiTokenBuilder(this.clientRef);
|
||
|
|
}
|
||
|
|
}
|