Compare commits
2 commits
26d2f288e9
...
3f0549bd44
Author | SHA1 | Date | |
---|---|---|---|
3f0549bd44 | |||
ca6dbafaf1 |
9 changed files with 335 additions and 9 deletions
15
src/command/user/api/apiCommand.ts
Normal file
15
src/command/user/api/apiCommand.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export interface CreateApiCommand {
|
||||
title: string;
|
||||
token: string;
|
||||
expiry?: Date;
|
||||
}
|
||||
|
||||
export interface UpdateApiCommand {
|
||||
id: number;
|
||||
title: string;
|
||||
expiry?: Date;
|
||||
}
|
||||
|
||||
export interface DeleteApiCommand {
|
||||
id: number;
|
||||
}
|
69
src/command/user/api/apiCommandHandler.ts
Normal file
69
src/command/user/api/apiCommandHandler.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
import { dataSource } from "../../../data-source";
|
||||
import { api } from "../../../entity/user/api";
|
||||
import InternalException from "../../../exceptions/internalException";
|
||||
import { CreateApiCommand, DeleteApiCommand, UpdateApiCommand } from "./apiCommand";
|
||||
|
||||
export default abstract class ApiCommandHandler {
|
||||
/**
|
||||
* @description create api
|
||||
* @param {CreateApiCommand} createApi
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
static async create(createApi: CreateApiCommand): Promise<number> {
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(api)
|
||||
.values({
|
||||
token: createApi.token,
|
||||
title: createApi.title,
|
||||
expiry: createApi.expiry,
|
||||
})
|
||||
.execute()
|
||||
.then((result) => {
|
||||
return result.identifiers[0].token;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed creating api", err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description update api
|
||||
* @param {UpdateApiCommand} updateApi
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async update(updateApi: UpdateApiCommand): Promise<void> {
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.update(api)
|
||||
.set({
|
||||
title: updateApi.title,
|
||||
expiry: updateApi.expiry,
|
||||
})
|
||||
.where("id = :id", { id: updateApi.id })
|
||||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed updating api", err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description delete api
|
||||
* @param {DeleteApiCommand} deleteApi
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async delete(deleteApi: DeleteApiCommand): Promise<void> {
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from(api)
|
||||
.where("id = :id", { id: deleteApi.id })
|
||||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting api", err);
|
||||
});
|
||||
}
|
||||
}
|
16
src/command/user/api/apiPermissionCommand.ts
Normal file
16
src/command/user/api/apiPermissionCommand.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { PermissionString } from "../../../type/permissionTypes";
|
||||
|
||||
export interface CreateApiPermissionCommand {
|
||||
permission: PermissionString;
|
||||
apiId: number;
|
||||
}
|
||||
|
||||
export interface DeleteApiPermissionCommand {
|
||||
permission: PermissionString;
|
||||
apiId: number;
|
||||
}
|
||||
|
||||
export interface UpdateApiPermissionsCommand {
|
||||
apiId: number;
|
||||
permissions: Array<PermissionString>;
|
||||
}
|
114
src/command/user/api/apiPermissionCommandHandler.ts
Normal file
114
src/command/user/api/apiPermissionCommandHandler.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
import { DeleteResult, EntityManager, InsertResult } from "typeorm";
|
||||
import { dataSource } from "../../../data-source";
|
||||
import { apiPermission } from "../../../entity/user/api_permission";
|
||||
import InternalException from "../../../exceptions/internalException";
|
||||
import ApiService from "../../../service/user/apiService";
|
||||
import {
|
||||
CreateApiPermissionCommand,
|
||||
DeleteApiPermissionCommand,
|
||||
UpdateApiPermissionsCommand,
|
||||
} from "./apiPermissionCommand";
|
||||
import PermissionHelper from "../../../helpers/permissionHelper";
|
||||
import ApiPermissionService from "../../../service/user/apiPermissionService";
|
||||
import { PermissionString } from "../../../type/permissionTypes";
|
||||
|
||||
export default abstract class ApiPermissionCommandHandler {
|
||||
/**
|
||||
* @description update api permissions
|
||||
* @param {UpdateApiPermissionsCommand} updateApiPermissions
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async updatePermissions(updateApiPermissions: UpdateApiPermissionsCommand): Promise<void> {
|
||||
let currentPermissions = (await ApiPermissionService.getByApi(updateApiPermissions.apiId)).map((r) => r.permission);
|
||||
return await dataSource.manager
|
||||
.transaction(async (manager) => {
|
||||
let newPermissions = PermissionHelper.getWhatToAdd(currentPermissions, updateApiPermissions.permissions);
|
||||
let removePermissions = PermissionHelper.getWhatToRemove(currentPermissions, updateApiPermissions.permissions);
|
||||
if (newPermissions.length != 0) {
|
||||
await this.updatePermissionsAdd(manager, updateApiPermissions.apiId, newPermissions);
|
||||
}
|
||||
if (removePermissions.length != 0) {
|
||||
await this.updatePermissionsRemove(manager, updateApiPermissions.apiId, removePermissions);
|
||||
}
|
||||
})
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed saving api permissions", err);
|
||||
});
|
||||
}
|
||||
|
||||
private static async updatePermissionsAdd(
|
||||
manager: EntityManager,
|
||||
apiId: number,
|
||||
permissions: Array<PermissionString>
|
||||
): Promise<InsertResult> {
|
||||
return await manager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(apiPermission)
|
||||
.values(
|
||||
permissions.map((p) => ({
|
||||
permission: p,
|
||||
apiId: apiId,
|
||||
}))
|
||||
)
|
||||
.orIgnore()
|
||||
.execute();
|
||||
}
|
||||
|
||||
private static async updatePermissionsRemove(
|
||||
manager: EntityManager,
|
||||
apiId: number,
|
||||
permissions: Array<PermissionString>
|
||||
): Promise<DeleteResult> {
|
||||
return await manager
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from(apiPermission)
|
||||
.where("apiId = :id", { id: apiId })
|
||||
.andWhere("permission IN (:...permission)", { permission: permissions })
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description grant permission to user
|
||||
* @param {CreateApiPermissionCommand} createPermission
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
static async create(createPermission: CreateApiPermissionCommand): Promise<number> {
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(apiPermission)
|
||||
.values({
|
||||
permission: createPermission.permission,
|
||||
apiId: createPermission.apiId,
|
||||
})
|
||||
.execute()
|
||||
.then((result) => {
|
||||
return result.identifiers[0].id;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed saving api permission", err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description remove permission from api
|
||||
* @param {DeleteApiPermissionCommand} deletePermission
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
static async delete(deletePermission: DeleteApiPermissionCommand): Promise<any> {
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from(apiPermission)
|
||||
.where("apiId = :id", { id: deletePermission.apiId })
|
||||
.andWhere("permission = :permission", { permission: deletePermission.permission })
|
||||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("failed api permission removal", err);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -2,7 +2,10 @@ import { Column, CreateDateColumn, Entity, PrimaryColumn } from "typeorm";
|
|||
|
||||
@Entity()
|
||||
export class api {
|
||||
@PrimaryColumn({ type: "varchar", length: 255 })
|
||||
@PrimaryColumn({ generated: "increment", type: "int" })
|
||||
id: number;
|
||||
|
||||
@Column({ type: "varchar", length: 255, unique: true, select: false })
|
||||
token: string;
|
||||
|
||||
@Column({ type: "varchar", length: 255 })
|
||||
|
|
|
@ -5,7 +5,7 @@ import { api } from "./api";
|
|||
@Entity()
|
||||
export class apiPermission {
|
||||
@PrimaryColumn({ type: "int" })
|
||||
apiToken: number;
|
||||
apiId: number;
|
||||
|
||||
@PrimaryColumn({ type: "varchar", length: 255 })
|
||||
permission: PermissionString;
|
||||
|
|
|
@ -11,11 +11,12 @@ export class AddApiTokens1737453096674 implements MigrationInterface {
|
|||
new Table({
|
||||
name: "api",
|
||||
columns: [
|
||||
{ name: "token", type: "varchar", length: "255", isPrimary: true, isNullable: false },
|
||||
{ name: "id", type: variableType_int, isPrimary: true, isNullable: false },
|
||||
{ name: "token", type: "varchar", length: "255", isUnique: true, isNullable: false },
|
||||
{ name: "title", type: "varchar", length: "255", isNullable: false },
|
||||
{ name: "createdAt", type: "datetime", default: "CURRENT_TIMESTAMP(6)", isNullable: false },
|
||||
{ name: "lastUsage", type: "datetime", isNullable: true },
|
||||
{ name: "expiry", type: "datetime", isNullable: true },
|
||||
{ name: "lastUsage", type: "datetime", isNullable: true, default: null },
|
||||
{ name: "expiry", type: "datetime", isNullable: true, default: null },
|
||||
],
|
||||
}),
|
||||
true
|
||||
|
@ -25,17 +26,18 @@ export class AddApiTokens1737453096674 implements MigrationInterface {
|
|||
new Table({
|
||||
name: "api_permission",
|
||||
columns: [
|
||||
{ name: "apiToken", type: "varchar", length: "255", isPrimary: true, isNullable: false },
|
||||
{ name: "apiId", type: variableType_int, isPrimary: true, isNullable: false },
|
||||
{ name: "permission", type: "varchar", length: "255", isPrimary: true, isNullable: false },
|
||||
],
|
||||
})
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
await queryRunner.createForeignKey(
|
||||
"api_permission",
|
||||
new TableForeignKey({
|
||||
columnNames: ["apiToken"],
|
||||
referencedColumnNames: ["token"],
|
||||
columnNames: ["apiId"],
|
||||
referencedColumnNames: ["id"],
|
||||
referencedTableName: "api",
|
||||
onDelete: "CASCADE",
|
||||
onUpdate: "RESTRICT",
|
||||
|
|
24
src/service/user/apiPermissionService.ts
Normal file
24
src/service/user/apiPermissionService.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { dataSource } from "../../data-source";
|
||||
import { apiPermission } from "../../entity/user/api_permission";
|
||||
import InternalException from "../../exceptions/internalException";
|
||||
|
||||
export default abstract class ApiPermissionService {
|
||||
/**
|
||||
* @description get permission by api
|
||||
* @param apiId number
|
||||
* @returns {Promise<Array<apiPermission>>}
|
||||
*/
|
||||
static async getByApi(apiId: number): Promise<Array<apiPermission>> {
|
||||
return await dataSource
|
||||
.getRepository(apiPermission)
|
||||
.createQueryBuilder("api_permission")
|
||||
.where("api_permission.apiId = :apiId", { apiId: apiId })
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("api permissions not found by api", err);
|
||||
});
|
||||
}
|
||||
}
|
83
src/service/user/apiService.ts
Normal file
83
src/service/user/apiService.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
import { dataSource } from "../../data-source";
|
||||
import { api } from "../../entity/user/api";
|
||||
import InternalException from "../../exceptions/internalException";
|
||||
|
||||
export default abstract class ApiService {
|
||||
/**
|
||||
* @description get apis
|
||||
* @returns {Promise<Array<api>>}
|
||||
*/
|
||||
static async getAll(): Promise<Array<api>> {
|
||||
return await dataSource
|
||||
.getRepository(api)
|
||||
.createQueryBuilder("api")
|
||||
.leftJoinAndSelect("api.permissions", "permissions")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("apis not found", err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get api by id
|
||||
* @param id number
|
||||
* @returns {Promise<api>}
|
||||
*/
|
||||
static async getById(id: number): Promise<api> {
|
||||
return await dataSource
|
||||
.getRepository(api)
|
||||
.createQueryBuilder("api")
|
||||
.leftJoinAndSelect("api.permissions", "permissions")
|
||||
.where("api.id = :id", { id: id })
|
||||
.getOneOrFail()
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("api not found by id", err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get api by token
|
||||
* @param token string
|
||||
* @returns {Promise<api>}
|
||||
*/
|
||||
static async getByToken(token: string): Promise<api> {
|
||||
return await dataSource
|
||||
.getRepository(api)
|
||||
.createQueryBuilder("api")
|
||||
.leftJoinAndSelect("api.permissions", "permissions")
|
||||
.where("api.token = :token", { token: token })
|
||||
.getOneOrFail()
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("api not found by token", err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get api by id
|
||||
* @param id number
|
||||
* @returns {Promise<api>}
|
||||
*/
|
||||
static async getTokenById(id: number): Promise<api> {
|
||||
return await dataSource
|
||||
.getRepository(api)
|
||||
.createQueryBuilder("api")
|
||||
.select("token")
|
||||
.where("api.id = :id", { id: id })
|
||||
.getOneOrFail()
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("api token not found by id", err);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue