allow adding names to apikeys
This commit is contained in:
parent
ec3e58d1b2
commit
5878f0ad1d
|
@ -20,6 +20,11 @@ export class ApiKeyDbService {
|
||||||
const apikey = new EApiKeyBackend<string>();
|
const apikey = new EApiKeyBackend<string>();
|
||||||
apikey.user = userid;
|
apikey.user = userid;
|
||||||
apikey.created = new Date();
|
apikey.created = new Date();
|
||||||
|
// YYYY-MM-DD- followed by a random number
|
||||||
|
apikey.name =
|
||||||
|
new Date().toISOString().slice(0, 10) +
|
||||||
|
'_' +
|
||||||
|
Math.round(Math.random() * 100);
|
||||||
apikey.key = generateRandomString(32); // Might collide, probably not
|
apikey.key = generateRandomString(32); // Might collide, probably not
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -36,7 +41,7 @@ export class ApiKeyDbService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(
|
async findOne(
|
||||||
key: string,
|
id: string,
|
||||||
userid: string | undefined,
|
userid: string | undefined,
|
||||||
): AsyncFailable<EApiKeyBackend<string>> {
|
): AsyncFailable<EApiKeyBackend<string>> {
|
||||||
try {
|
try {
|
||||||
|
@ -47,7 +52,7 @@ export class ApiKeyDbService {
|
||||||
? // This is stupid, but typeorm do typeorm
|
? // This is stupid, but typeorm do typeorm
|
||||||
({ id: userid } as any)
|
({ id: userid } as any)
|
||||||
: undefined,
|
: undefined,
|
||||||
key,
|
id,
|
||||||
},
|
},
|
||||||
loadRelationIds: true,
|
loadRelationIds: true,
|
||||||
});
|
});
|
||||||
|
@ -92,11 +97,28 @@ export class ApiKeyDbService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteApiKey(
|
async updateApiKey(
|
||||||
key: string,
|
id: string,
|
||||||
|
name: string,
|
||||||
userid: string | undefined,
|
userid: string | undefined,
|
||||||
): AsyncFailable<EApiKeyBackend<string>> {
|
): AsyncFailable<EApiKeyBackend<string>> {
|
||||||
const apikeyToDelete = await this.findOne(key, userid);
|
const apikey = await this.findOne(id, userid);
|
||||||
|
if (HasFailed(apikey)) return apikey;
|
||||||
|
|
||||||
|
try {
|
||||||
|
apikey.name = name;
|
||||||
|
|
||||||
|
return this.apikeyRepo.save(apikey);
|
||||||
|
} catch (e) {
|
||||||
|
return Fail(FT.Database, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteApiKey(
|
||||||
|
id: string,
|
||||||
|
userid: string | undefined,
|
||||||
|
): AsyncFailable<EApiKeyBackend<string>> {
|
||||||
|
const apikeyToDelete = await this.findOne(id, userid);
|
||||||
if (HasFailed(apikeyToDelete)) return apikeyToDelete;
|
if (HasFailed(apikeyToDelete)) return apikeyToDelete;
|
||||||
|
|
||||||
const apiKeyCopy = { ...apikeyToDelete };
|
const apiKeyCopy = { ...apikeyToDelete };
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { EApiKeySchema } from 'picsur-shared/dist/entities/apikey.entity';
|
import { EApiKeySchema } from 'picsur-shared/dist/entities/apikey.entity';
|
||||||
import {
|
import {
|
||||||
Column, Entity,
|
Column, Entity,
|
||||||
ManyToOne,
|
Index,
|
||||||
PrimaryColumn
|
ManyToOne, PrimaryGeneratedColumn
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { EUserBackend } from './user.entity';
|
import { EUserBackend } from './user.entity';
|
||||||
|
@ -19,7 +19,11 @@ export class EApiKeyBackend<
|
||||||
T extends string | EUserBackend = string | EUserBackend,
|
T extends string | EUserBackend = string | EUserBackend,
|
||||||
> implements OverriddenEApiKey
|
> implements OverriddenEApiKey
|
||||||
{
|
{
|
||||||
@PrimaryColumn({
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
})
|
})
|
||||||
|
@ -31,6 +35,9 @@ export class EApiKeyBackend<
|
||||||
})
|
})
|
||||||
user: T;
|
user: T;
|
||||||
|
|
||||||
|
@Column({ nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,9 @@ import {
|
||||||
ApiKeyInfoRequest,
|
ApiKeyInfoRequest,
|
||||||
ApiKeyInfoResponse,
|
ApiKeyInfoResponse,
|
||||||
ApiKeyListRequest,
|
ApiKeyListRequest,
|
||||||
ApiKeyListResponse
|
ApiKeyListResponse,
|
||||||
|
ApiKeyUpdateRequest,
|
||||||
|
ApiKeyUpdateResponse
|
||||||
} from 'picsur-shared/dist/dto/api/apikeys.dto';
|
} from 'picsur-shared/dist/dto/api/apikeys.dto';
|
||||||
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
|
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
|
||||||
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
|
@ -31,7 +33,7 @@ export class ApiKeysController {
|
||||||
@HasPermission(Permission.ApiKeyAdmin) isAdmin: boolean,
|
@HasPermission(Permission.ApiKeyAdmin) isAdmin: boolean,
|
||||||
): Promise<ApiKeyInfoResponse> {
|
): Promise<ApiKeyInfoResponse> {
|
||||||
return ThrowIfFailed(
|
return ThrowIfFailed(
|
||||||
await this.apikeyDB.findOne(body.key, isAdmin ? undefined : userid),
|
await this.apikeyDB.findOne(body.id, isAdmin ? undefined : userid),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +59,22 @@ export class ApiKeysController {
|
||||||
return ThrowIfFailed(await this.apikeyDB.createApiKey(userID));
|
return ThrowIfFailed(await this.apikeyDB.createApiKey(userID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('update')
|
||||||
|
@Returns(ApiKeyUpdateResponse)
|
||||||
|
async updateApiKey(
|
||||||
|
@ReqUserID() userID: string,
|
||||||
|
@Body() body: ApiKeyUpdateRequest,
|
||||||
|
@HasPermission(Permission.ApiKeyAdmin) isAdmin: boolean,
|
||||||
|
): Promise<ApiKeyUpdateResponse> {
|
||||||
|
return ThrowIfFailed(
|
||||||
|
await this.apikeyDB.updateApiKey(
|
||||||
|
body.id,
|
||||||
|
body.name,
|
||||||
|
isAdmin ? undefined : userID,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Post('delete')
|
@Post('delete')
|
||||||
@Returns(ApiKeyDeleteResponse)
|
@Returns(ApiKeyDeleteResponse)
|
||||||
async deleteApiKey(
|
async deleteApiKey(
|
||||||
|
@ -65,7 +83,7 @@ export class ApiKeysController {
|
||||||
@HasPermission(Permission.ApiKeyAdmin) isAdmin: boolean,
|
@HasPermission(Permission.ApiKeyAdmin) isAdmin: boolean,
|
||||||
): Promise<ApiKeyDeleteResponse> {
|
): Promise<ApiKeyDeleteResponse> {
|
||||||
return ThrowIfFailed(
|
return ThrowIfFailed(
|
||||||
await this.apikeyDB.deleteApiKey(body.key, isAdmin ? undefined : userID),
|
await this.apikeyDB.deleteApiKey(body.id, isAdmin ? undefined : userID),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
<h1>Api Keys</h1>
|
<h1>Api Keys</h1>
|
||||||
|
|
||||||
<mat-table [dataSource]="dataSubject" class="mat-elevation-z2">
|
<mat-table [dataSource]="dataSubject" class="mat-elevation-z2">
|
||||||
<ng-container matColumnDef="key">
|
<ng-container matColumnDef="name">
|
||||||
<mat-header-cell *matHeaderCellDef>Key</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let apikey">
|
<mat-cell *matCellDef="let apikey">
|
||||||
<copy-field
|
<mat-form-field class="editfield" appearance="outline">
|
||||||
label="Key"
|
<mat-label>Name</mat-label>
|
||||||
[value]="apikey.key"
|
<input matInput [value]="apikey.name" maxlength="255" (change)="updateKeyName($event, apikey.id)">
|
||||||
[hidden]="true"
|
</mat-form-field>
|
||||||
[showHideButton]="true"
|
|
||||||
></copy-field>
|
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -34,7 +32,12 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<mat-header-cell *matHeaderCellDef>Actions</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>Actions</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let apikey">
|
<mat-cell *matCellDef="let apikey">
|
||||||
<button mat-icon-button (click)="deleteApiKey(apikey.key)">
|
<button mat-icon-button (click)="copyKey(apikey.key)">
|
||||||
|
<mat-icon fontSet="material-icons-outlined" aria-label="Copy Key">
|
||||||
|
content_copy
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="deleteApiKey(apikey.id)">
|
||||||
<mat-icon
|
<mat-icon
|
||||||
fontSet="material-icons-outlined"
|
fontSet="material-icons-outlined"
|
||||||
class="icon-red"
|
class="icon-red"
|
||||||
|
|
|
@ -19,7 +19,7 @@ mat-table {
|
||||||
color: #f44336;
|
color: #f44336;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy-field {
|
.editfield {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class SettingsApiKeysComponent implements OnInit {
|
||||||
private readonly logger = new Logger(SettingsApiKeysComponent.name);
|
private readonly logger = new Logger(SettingsApiKeysComponent.name);
|
||||||
|
|
||||||
public readonly displayedColumns: string[] = [
|
public readonly displayedColumns: string[] = [
|
||||||
'key',
|
'name',
|
||||||
'created',
|
'created',
|
||||||
'last_used',
|
'last_used',
|
||||||
'actions',
|
'actions',
|
||||||
|
@ -82,7 +82,22 @@ export class SettingsApiKeysComponent implements OnInit {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteApiKey(apikey: string) {
|
public copyKey(apikey: string) {
|
||||||
|
const result = this.clipboard.copy(apikey);
|
||||||
|
if (!result) {
|
||||||
|
this.utilService.showSnackBar(
|
||||||
|
'Failed to copy api key to clipboard',
|
||||||
|
SnackBarType.Error,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.utilService.showSnackBar(
|
||||||
|
'Api key copied to clipboard',
|
||||||
|
SnackBarType.Success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteApiKey(apikeyId: string) {
|
||||||
const pressedButton = await this.utilService.showDialog({
|
const pressedButton = await this.utilService.showDialog({
|
||||||
title: `Are you sure you want to delete this api key?`,
|
title: `Are you sure you want to delete this api key?`,
|
||||||
description: 'This action cannot be undone.',
|
description: 'This action cannot be undone.',
|
||||||
|
@ -100,7 +115,7 @@ export class SettingsApiKeysComponent implements OnInit {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (pressedButton === 'delete') {
|
if (pressedButton === 'delete') {
|
||||||
const result = await this.apikeysService.deleteApiKey(apikey);
|
const result = await this.apikeysService.deleteApiKey(apikeyId);
|
||||||
if (HasFailed(result)) {
|
if (HasFailed(result)) {
|
||||||
this.utilService.showSnackBar(
|
this.utilService.showSnackBar(
|
||||||
'Failed to delete api key',
|
'Failed to delete api key',
|
||||||
|
@ -120,6 +135,33 @@ export class SettingsApiKeysComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateKeyName(event: Event, apikeyID: string) {
|
||||||
|
const name = (event.target as HTMLInputElement).value;
|
||||||
|
|
||||||
|
if (name.length < 3) {
|
||||||
|
this.utilService.showSnackBar(
|
||||||
|
'Name must be at least 3 characters long',
|
||||||
|
SnackBarType.Warning,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.apikeysService.updateApiKey(apikeyID, name);
|
||||||
|
|
||||||
|
if (HasFailed(result)) {
|
||||||
|
this.logger.warn(result.print());
|
||||||
|
this.utilService.showSnackBar(
|
||||||
|
'Failed to update api key name',
|
||||||
|
SnackBarType.Error,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.utilService.showSnackBar(
|
||||||
|
'Api key name updated',
|
||||||
|
SnackBarType.Success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AutoUnsubscribe()
|
@AutoUnsubscribe()
|
||||||
private subscribeToUpdate() {
|
private subscribeToUpdate() {
|
||||||
return this.updateSubject
|
return this.updateSubject
|
||||||
|
@ -156,14 +198,9 @@ export class SettingsApiKeysComponent implements OnInit {
|
||||||
this.logger.warn(response.print());
|
this.logger.warn(response.print());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
console.log(response.results);
|
|
||||||
|
|
||||||
if (response.results.length > 0) {
|
|
||||||
this.dataSubject.next(response.results);
|
this.dataSubject.next(response.results);
|
||||||
this.totalApiKeys = response.total;
|
this.totalApiKeys = response.total;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
import { MomentModule } from 'ngx-moment';
|
import { MomentModule } from 'ngx-moment';
|
||||||
import { CopyFieldModule } from 'src/app/components/copy-field/copy-field.module';
|
|
||||||
import { FabModule } from 'src/app/components/fab/fab.module';
|
import { FabModule } from 'src/app/components/fab/fab.module';
|
||||||
import { SettingsApiKeysComponent } from './settings-apikeys.component';
|
import { SettingsApiKeysComponent } from './settings-apikeys.component';
|
||||||
import { SettingsApiKeysRoutingModule } from './settings-apikeys.routing.module';
|
import { SettingsApiKeysRoutingModule } from './settings-apikeys.routing.module';
|
||||||
|
@ -26,7 +25,6 @@ import { SettingsApiKeysRoutingModule } from './settings-apikeys.routing.module'
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
MomentModule,
|
MomentModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
CopyFieldModule,
|
|
||||||
FabModule,
|
FabModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { SidebarResolverService } from 'src/app/services/sidebar-resolver/sideba
|
||||||
import { SettingsApiKeysRouteModule } from './apikeys/settings-apikeys.module';
|
import { SettingsApiKeysRouteModule } from './apikeys/settings-apikeys.module';
|
||||||
import { SettingsGeneralRouteModule } from './general/settings-general.module';
|
import { SettingsGeneralRouteModule } from './general/settings-general.module';
|
||||||
import { SettingsRolesRouteModule } from './roles/settings-roles.module';
|
import { SettingsRolesRouteModule } from './roles/settings-roles.module';
|
||||||
|
import { SettingsShareXRouteModule } from './sharex/settings-sharex.module';
|
||||||
import { SettingsSidebarComponent } from './sidebar/settings-sidebar.component';
|
import { SettingsSidebarComponent } from './sidebar/settings-sidebar.component';
|
||||||
import { SettingsSysprefRouteModule } from './sys-pref/settings-sys-pref.module';
|
import { SettingsSysprefRouteModule } from './sys-pref/settings-sys-pref.module';
|
||||||
import { SettingsUsersRouteModule } from './users/settings-users.module';
|
import { SettingsUsersRouteModule } from './users/settings-users.module';
|
||||||
|
@ -44,6 +45,18 @@ const SettingsRoutes: PRoutes = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'sharex',
|
||||||
|
loadChildren: () => SettingsShareXRouteModule,
|
||||||
|
data: {
|
||||||
|
permissions: [Permission.ApiKey],
|
||||||
|
page: {
|
||||||
|
title: 'ShareX',
|
||||||
|
icon: 'install_desktop',
|
||||||
|
category: 'personal',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'users',
|
path: 'users',
|
||||||
loadChildren: () => SettingsUsersRouteModule,
|
loadChildren: () => SettingsUsersRouteModule,
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>Create a ShareX target config</h1>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { EApiKey } from 'picsur-shared/dist/entities/apikey.entity';
|
||||||
|
import { ApiKeysService } from 'src/app/services/api/apikeys.service';
|
||||||
|
import { Logger } from 'src/app/services/logger/logger.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './settings-sharex.component.html',
|
||||||
|
styleUrls: ['./settings-sharex.component.scss'],
|
||||||
|
})
|
||||||
|
export class SettingsShareXComponent {
|
||||||
|
private readonly logger = new Logger(SettingsShareXComponent.name);
|
||||||
|
|
||||||
|
public apikeys: EApiKey[] = [];
|
||||||
|
|
||||||
|
constructor(private readonly apikeysService: ApiKeysService) {}
|
||||||
|
|
||||||
|
private async loadApiKeys() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { SettingsShareXComponent } from './settings-sharex.component';
|
||||||
|
import { SettingsShareXRoutingModule } from './settings-sharex.routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [SettingsShareXComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SettingsShareXRoutingModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SettingsShareXRouteModule {}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { PRoutes } from 'src/app/models/dto/picsur-routes.dto';
|
||||||
|
import { SettingsShareXComponent } from './settings-sharex.component';
|
||||||
|
|
||||||
|
const routes: PRoutes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: SettingsShareXComponent,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class SettingsShareXRoutingModule {}
|
|
@ -6,7 +6,9 @@ import {
|
||||||
ApiKeyInfoRequest,
|
ApiKeyInfoRequest,
|
||||||
ApiKeyInfoResponse,
|
ApiKeyInfoResponse,
|
||||||
ApiKeyListRequest,
|
ApiKeyListRequest,
|
||||||
ApiKeyListResponse
|
ApiKeyListResponse,
|
||||||
|
ApiKeyUpdateRequest,
|
||||||
|
ApiKeyUpdateResponse,
|
||||||
} from 'picsur-shared/dist/dto/api/apikeys.dto';
|
} from 'picsur-shared/dist/dto/api/apikeys.dto';
|
||||||
import { EApiKey } from 'picsur-shared/dist/entities/apikey.entity';
|
import { EApiKey } from 'picsur-shared/dist/entities/apikey.entity';
|
||||||
import { AsyncFailable } from 'picsur-shared/dist/types';
|
import { AsyncFailable } from 'picsur-shared/dist/types';
|
||||||
|
@ -38,13 +40,13 @@ export class ApiKeysService {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getApiKey(key: string): AsyncFailable<EApiKey> {
|
public async getApiKey(id: string): AsyncFailable<EApiKey> {
|
||||||
return await this.api.post(
|
return await this.api.post(
|
||||||
ApiKeyInfoRequest,
|
ApiKeyInfoRequest,
|
||||||
ApiKeyInfoResponse,
|
ApiKeyInfoResponse,
|
||||||
'/api/apikeys/info',
|
'/api/apikeys/info',
|
||||||
{
|
{
|
||||||
key,
|
id,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -56,13 +58,25 @@ export class ApiKeysService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteApiKey(key: string): AsyncFailable<EApiKey> {
|
public async updateApiKey(id: string, name: string): AsyncFailable<EApiKey> {
|
||||||
|
return await this.api.post(
|
||||||
|
ApiKeyUpdateRequest,
|
||||||
|
ApiKeyUpdateResponse,
|
||||||
|
'/api/apikeys/update',
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteApiKey(id: string): AsyncFailable<Omit<EApiKey, 'id'>> {
|
||||||
return await this.api.post(
|
return await this.api.post(
|
||||||
ApiKeyDeleteRequest,
|
ApiKeyDeleteRequest,
|
||||||
ApiKeyDeleteResponse,
|
ApiKeyDeleteResponse,
|
||||||
'/api/apikeys/delete',
|
'/api/apikeys/delete',
|
||||||
{
|
{
|
||||||
key,
|
id,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { EApiKeySchema } from '../../entities/apikey.entity';
|
import { EApiKeySchema } from '../../entities/apikey.entity';
|
||||||
import { createZodDto } from '../../util/create-zod-dto';
|
import { createZodDto } from '../../util/create-zod-dto';
|
||||||
|
import { IsEntityID } from '../../validators/entity-id.validator';
|
||||||
import { IsPosInt } from '../../validators/positive-int.validator';
|
import { IsPosInt } from '../../validators/positive-int.validator';
|
||||||
|
|
||||||
// ApiKeyInfo
|
// ApiKeyInfo
|
||||||
export const ApiKeyInfoRequestSchema = EApiKeySchema.pick({
|
export const ApiKeyInfoRequestSchema = z.object({
|
||||||
key: true,
|
id: IsEntityID(),
|
||||||
});
|
});
|
||||||
export class ApiKeyInfoRequest extends createZodDto(ApiKeyInfoRequestSchema) {}
|
export class ApiKeyInfoRequest extends createZodDto(ApiKeyInfoRequestSchema) {}
|
||||||
|
|
||||||
|
@ -39,15 +40,31 @@ export class ApiKeyCreateResponse extends createZodDto(
|
||||||
ApiKeyCreateResponseSchema,
|
ApiKeyCreateResponseSchema,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
// ApiKeyUpdate
|
||||||
|
export const ApiKeyUpdateRequestSchema = z.object({
|
||||||
|
id: IsEntityID(),
|
||||||
|
name: z.string().max(255),
|
||||||
|
});
|
||||||
|
export class ApiKeyUpdateRequest extends createZodDto(
|
||||||
|
ApiKeyUpdateRequestSchema,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
export const ApiKeyUpdateResponseSchema = EApiKeySchema;
|
||||||
|
export class ApiKeyUpdateResponse extends createZodDto(
|
||||||
|
ApiKeyUpdateResponseSchema,
|
||||||
|
) {}
|
||||||
|
|
||||||
// ApiKeyDelete
|
// ApiKeyDelete
|
||||||
export const ApiKeyDeleteRequestSchema = EApiKeySchema.pick({
|
export const ApiKeyDeleteRequestSchema = z.object({
|
||||||
key: true,
|
id: IsEntityID(),
|
||||||
});
|
});
|
||||||
export class ApiKeyDeleteRequest extends createZodDto(
|
export class ApiKeyDeleteRequest extends createZodDto(
|
||||||
ApiKeyDeleteRequestSchema,
|
ApiKeyDeleteRequestSchema,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
export const ApiKeyDeleteResponseSchema = EApiKeySchema;
|
export const ApiKeyDeleteResponseSchema = EApiKeySchema.omit({
|
||||||
|
id: true,
|
||||||
|
});
|
||||||
export class ApiKeyDeleteResponse extends createZodDto(
|
export class ApiKeyDeleteResponse extends createZodDto(
|
||||||
ApiKeyDeleteResponseSchema,
|
ApiKeyDeleteResponseSchema,
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -3,9 +3,11 @@ import { IsApiKey } from '../validators/api-key.validator';
|
||||||
import { IsEntityID } from '../validators/entity-id.validator';
|
import { IsEntityID } from '../validators/entity-id.validator';
|
||||||
|
|
||||||
export const EApiKeySchema = z.object({
|
export const EApiKeySchema = z.object({
|
||||||
|
id: IsEntityID(),
|
||||||
key: IsApiKey(),
|
key: IsApiKey(),
|
||||||
user: IsEntityID(),
|
user: IsEntityID(),
|
||||||
|
name: z.string().min(3).max(255),
|
||||||
created: z.preprocess((data: any) => new Date(data), z.date()),
|
created: z.preprocess((data: any) => new Date(data), z.date()),
|
||||||
last_used: z.preprocess((data: any) => new Date(data), z.date()).nullable()
|
last_used: z.preprocess((data: any) => new Date(data), z.date()).nullable(),
|
||||||
});
|
});
|
||||||
export type EApiKey = z.infer<typeof EApiKeySchema>;
|
export type EApiKey = z.infer<typeof EApiKeySchema>;
|
||||||
|
|
Loading…
Reference in a new issue