diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 934b282dbb..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,15 +0,0 @@ -# don't ever lint node_modules - -**/node_modules -# don't lint build output (make sure it's set to your correct build folder name) -**/out - -**/build/ - -**/migrations - -**/next-env.d.ts - -**/build - -**/generated diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 29c5b6a5c5..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy - * SCOW is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -const path = require("path"); - -module.exports = { - "extends": "@ddadaal", - "plugins": [ - "license-header", - ], - "rules": { - "license-header/header": [ - "error", - path.join(__dirname, "license-header"), - ], - }, - "overrides": [ - { - "files": ["**/*.js", "apps/**/*.js"], - "rules": { - "@typescript-eslint/no-var-requires": "off", - }, - }, - { - "files": ["./license-header"], - "rules": { "license-header/header": "off" }, - }, - ], -}; diff --git a/.github/workflows/test-build-publish.yaml b/.github/workflows/test-build-publish.yaml index 3265dd3813..15c0217ade 100644 --- a/.github/workflows/test-build-publish.yaml +++ b/.github/workflows/test-build-publish.yaml @@ -45,6 +45,9 @@ jobs: - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Prepare dev libs and code + run: pnpm prepareDev + - name: Lint code run: pnpm lint @@ -60,10 +63,6 @@ jobs: if: vars.RUN_TESTS == 'true' run: docker ps - - name: Prepare dev libs and code - if: vars.RUN_TESTS == 'true' || vars.RUN_RELEASE == 'true' - run: pnpm prepareDev - - name: Wait for ports if: vars.RUN_TESTS == 'true' uses: ifaxity/wait-on-action@v1.2.1 diff --git a/.vscode/settings.json b/.vscode/settings.json index a8fa6aaa3d..4fbf9d1521 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "cSpell.words": [ "trpc" ], + "jest.runMode": "on-demand", "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, diff --git a/apps/ai/.eslintignore b/apps/ai/.eslintignore deleted file mode 100644 index e62fc5850a..0000000000 --- a/apps/ai/.eslintignore +++ /dev/null @@ -1,15 +0,0 @@ -# don't ever lint node_modules - -**/node_modules -# don't lint build output (make sure it's set to your correct build folder name) -**/out - -**/build/ - -**/migrations - -**/server/migrations - -**/next-env.d.ts - -**/build diff --git a/apps/ai/.eslintrc.json b/apps/ai/.eslintrc.json deleted file mode 100644 index 232a18ebdb..0000000000 --- a/apps/ai/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@ddadaal/eslint-config/react" -} diff --git a/apps/ai/eslint.config.js b/apps/ai/eslint.config.js new file mode 100644 index 0000000000..80c003f736 --- /dev/null +++ b/apps/ai/eslint.config.js @@ -0,0 +1,7 @@ +const base = require("../../eslint.config"); +const react = require("@ddadaal/eslint-config/react"); + +module.export = { + ...base, + ...react, +}; diff --git a/apps/ai/package.json b/apps/ai/package.json index a9828f4c1a..18e5f6b004 100644 --- a/apps/ai/package.json +++ b/apps/ai/package.json @@ -22,7 +22,8 @@ "build:ts": "rimraf build && tsc -p tsconfig.server.json && tsc-alias -p tsconfig.server.json", "orm": "dotenv -e env/.env.dev -- npx mikro-orm", "ormCreate": "dotenv -e env/.env.dev -- npx mikro-orm migration:create", - "ormUp": "dotenv -e env/.env.dev -- npx mikro-orm migration:up" + "ormUp": "dotenv -e env/.env.dev -- npx mikro-orm migration:up", + "lint": "eslint ." }, "mikro-orm": { "useTsNode": true, diff --git a/apps/audit-server/package.json b/apps/audit-server/package.json index d53268f3fd..d6448d8699 100644 --- a/apps/audit-server/package.json +++ b/apps/audit-server/package.json @@ -9,7 +9,8 @@ "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", "serve": "node build/index.js", "test": "jest", - "orm": "dotenv -e env/.env.dev -- npx mikro-orm" + "orm": "dotenv -e env/.env.dev -- npx mikro-orm", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "scripts", diff --git a/apps/audit-server/src/entities/OperationLog.ts b/apps/audit-server/src/entities/OperationLog.ts index f88fc9f5b7..41b9c8c429 100644 --- a/apps/audit-server/src/entities/OperationLog.ts +++ b/apps/audit-server/src/entities/OperationLog.ts @@ -25,37 +25,37 @@ export enum OperationResult { export class OperationLog { @PrimaryKey() - id!: number; + id!: number; @Property() - operatorUserId!: string; + operatorUserId!: string; @Property() - operatorIp!: string; + operatorIp!: string; @Property({ columnType: DATETIME_TYPE, defaultRaw: CURRENT_TIMESTAMP }) - operationTime?: Date; + operationTime?: Date; @Enum({ items: () => OperationResult, comment: Object.values(OperationResult).join(", ") }) - operationResult: OperationResult; + operationResult: OperationResult; @Property({ type: "json", nullable: true }) - metaData?: OperationEvent & { targetAccountName?: string }; + metaData?: OperationEvent & { targetAccountName?: string }; // 用户自定义操作类型 @Index({ name: "custom_event" }) @Property({ nullable: true }) - customEventType?: string; + customEventType?: string; constructor(init: { - operationLogId?: number; - operatorUserId: string; - operatorIp: string; - operationTime?: Date; - operationResult: OperationResult; - metaData: OperationEvent & { targetAccountName?: string }; - customEventType?: string; - }) { + operationLogId?: number; + operatorUserId: string; + operatorIp: string; + operationTime?: Date; + operationResult: OperationResult; + metaData: OperationEvent & { targetAccountName?: string }; + customEventType?: string; + }) { if (init.operationLogId) { this.id = init.operationLogId; } diff --git a/apps/audit-server/src/index.ts b/apps/audit-server/src/index.ts index 73dcb19d5a..30092fbccb 100644 --- a/apps/audit-server/src/index.ts +++ b/apps/audit-server/src/index.ts @@ -27,12 +27,12 @@ async function main() { switch (command) { - case "migrationUp": - await migrationUp(server.ext.orm); - break; - default: - logger.error("Unexpected task name %s", command); - process.exit(1); + case "migrationUp": + await migrationUp(server.ext.orm); + break; + default: + logger.error("Unexpected task name %s", command); + process.exit(1); } process.exit(0); @@ -41,4 +41,4 @@ async function main() { await server.start(); } -main(); +void main(); diff --git a/apps/audit-server/src/migrations/Migration20230817054947.ts b/apps/audit-server/src/migrations/Migration20230817054947.ts index 6f40af43af..a8fba6c225 100644 --- a/apps/audit-server/src/migrations/Migration20230817054947.ts +++ b/apps/audit-server/src/migrations/Migration20230817054947.ts @@ -1,13 +1,27 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20230817054947 extends Migration { async up(): Promise { - this.addSql('create table `operation_log` (`id` int unsigned not null auto_increment primary key, `operator_user_id` varchar(255) not null, `operator_ip` varchar(255) not null, `operation_time` DATETIME(6) not null default current_timestamp(6), `operation_result` enum(\'UNKNOWN\', \'SUCCESS\', \'FAIL\') not null comment \'UNKNOWN, SUCCESS, FAIL\', `meta_data` json null) default character set utf8mb4 engine = InnoDB;'); + this.addSql("create table `operation_log` (`id` int unsigned not null auto_increment primary key, `operator_user_id` varchar(255) not null, `operator_ip` varchar(255) not null, `operation_time` DATETIME(6) not null default current_timestamp(6), `operation_result` enum('UNKNOWN', 'SUCCESS', 'FAIL') not null comment 'UNKNOWN, SUCCESS, FAIL', `meta_data` json null) default character set utf8mb4 engine = InnoDB;"); } async down(): Promise { - this.addSql('drop table if exists `operation_log`;'); + this.addSql("drop table if exists `operation_log`;"); } } diff --git a/apps/audit-server/src/migrations/Migration20240218000000.ts b/apps/audit-server/src/migrations/Migration20240218000000.ts index ff99f69ccb..aace3cccee 100644 --- a/apps/audit-server/src/migrations/Migration20240218000000.ts +++ b/apps/audit-server/src/migrations/Migration20240218000000.ts @@ -1,3 +1,17 @@ +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + import { Migration } from "@mikro-orm/migrations"; export class Migration20240129000000 extends Migration { diff --git a/apps/audit-server/src/migrations/Migration20240313015230.ts b/apps/audit-server/src/migrations/Migration20240313015230.ts index 2646c83bca..479c292a0d 100644 --- a/apps/audit-server/src/migrations/Migration20240313015230.ts +++ b/apps/audit-server/src/migrations/Migration20240313015230.ts @@ -1,18 +1,32 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240313015230 extends Migration { async up(): Promise { - this.addSql('alter table `operation_log` add `custom_event_type` varchar(255) null;'); - this.addSql('alter table `operation_log` modify `operation_time` DATETIME(6) not null default current_timestamp(6), modify `operation_result` enum(\'UNKNOWN\', \'SUCCESS\', \'FAIL\') not null comment \'UNKNOWN, SUCCESS, FAIL\';'); - this.addSql('alter table `operation_log` add index `custom_event`(`custom_event_type`);'); + this.addSql("alter table `operation_log` add `custom_event_type` varchar(255) null;"); + this.addSql("alter table `operation_log` modify `operation_time` DATETIME(6) not null default current_timestamp(6), modify `operation_result` enum('UNKNOWN', 'SUCCESS', 'FAIL') not null comment 'UNKNOWN, SUCCESS, FAIL';"); + this.addSql("alter table `operation_log` add index `custom_event`(`custom_event_type`);"); } async down(): Promise { - this.addSql('alter table `operation_log` drop index `custom_event`;'); - this.addSql('alter table `operation_log` drop column `custom_event_type`;'); + this.addSql("alter table `operation_log` drop index `custom_event`;"); + this.addSql("alter table `operation_log` drop column `custom_event_type`;"); - this.addSql('alter table `operation_log` modify `operation_time` DATETIME(6) not null default current_timestamp(0), modify `operation_result` enum(\'UNKNOWN\', \'SUCCESS\', \'FAIL\') not null comment \'UNKNOWN, SUCCESS, FAIL\';'); + this.addSql("alter table `operation_log` modify `operation_time` DATETIME(6) not null default current_timestamp(0), modify `operation_result` enum('UNKNOWN', 'SUCCESS', 'FAIL') not null comment 'UNKNOWN, SUCCESS, FAIL';"); } } diff --git a/apps/audit-server/src/services/operationLog.ts b/apps/audit-server/src/services/operationLog.ts index abf3953e4f..900905b2ba 100644 --- a/apps/audit-server/src/services/operationLog.ts +++ b/apps/audit-server/src/services/operationLog.ts @@ -123,17 +123,18 @@ export const operationLogServiceServer = plugin((server) => { data.push(row); writeTotal += 1; if (data.length === 200 || writeTotal === addAccountNamesRecords.length) { - await new Promise(async (resolve) => { - await writeAsync({ operationLogs: data }); - // 清空暂存 - data = []; - resolve("done"); + await new Promise((resolve) => { + void writeAsync({ operationLogs: data }).then(() => { + // 清空暂存 + data = []; + resolve("done"); + }); }).catch((e) => { - throw { + throw { code: status.INTERNAL, message: "Error when exporting file", details: e?.message, - }; + } as ServiceError; }); } } @@ -144,7 +145,7 @@ export const operationLogServiceServer = plugin((server) => { getCustomEventTypes: async ({ em }) => { const qb = em.createQueryBuilder(OperationLog, "o"); - qb + void qb .select([ raw("DISTINCT o.custom_event_type as customType"), raw("JSON_UNQUOTE(JSON_EXTRACT(meta_data, '$.customEvent.name')) AS name"), diff --git a/apps/audit-server/src/services/statistic.ts b/apps/audit-server/src/services/statistic.ts index 09b9e79916..49dc438e72 100644 --- a/apps/audit-server/src/services/statistic.ts +++ b/apps/audit-server/src/services/statistic.ts @@ -29,7 +29,7 @@ export const statisticServiceServer = plugin((server) => { checkTimeZone(timeZone); const qb = em.createQueryBuilder(OperationLog, "o"); - qb + void qb .select([ raw("DATE(CONVERT_TZ(o.operation_time, 'UTC', ?)) as date", [timeZone]), raw("COUNT(DISTINCT o.operator_user_id) as userCount"), @@ -39,7 +39,7 @@ export const statisticServiceServer = plugin((server) => { .groupBy(raw("date")) .orderBy({ [raw("date")]: QueryOrder.DESC }); - const records: {date: string, userCount: number}[] = await qb.execute(); + const records: { date: string, userCount: number }[] = await qb.execute(); return [{ results: records.map((record) => ({ @@ -72,7 +72,7 @@ export const statisticServiceServer = plugin((server) => { ]; const qb = em.createQueryBuilder(OperationLog, "o"); - qb + void qb .select([raw("JSON_EXTRACT(meta_data, '$.$case') as operationType"), raw("COUNT(*) as count")]) .where({ operationTime: { $gte: startTime } }) .andWhere({ operationTime: { $lte: endTime } }) @@ -80,7 +80,7 @@ export const statisticServiceServer = plugin((server) => { .groupBy(raw("operationType")) .orderBy({ [raw("count")]: QueryOrder.DESC }); - const results: {operationType: string, count: number}[] = await qb.execute(); + const results: { operationType: string, count: number }[] = await qb.execute(); return [{ results, @@ -133,7 +133,7 @@ export const statisticServiceServer = plugin((server) => { ]; const qb = em.createQueryBuilder(OperationLog, "o"); - qb + void qb .select([raw("JSON_EXTRACT(meta_data, '$.$case') as operationType"), raw("COUNT(*) as count")]) .where({ operationTime: { $gte: startTime } }) .andWhere({ operationTime: { $lte: endTime } }) @@ -141,7 +141,7 @@ export const statisticServiceServer = plugin((server) => { .groupBy(raw("operationType")) .orderBy({ [raw("count")]: QueryOrder.DESC }); - const results: {operationType: string, count: number}[] = await qb.execute(); + const results: { operationType: string, count: number }[] = await qb.execute(); return [{ results, diff --git a/apps/audit-server/src/utils/operationLogs.ts b/apps/audit-server/src/utils/operationLogs.ts index dab124880e..4dd011451e 100644 --- a/apps/audit-server/src/utils/operationLogs.ts +++ b/apps/audit-server/src/utils/operationLogs.ts @@ -79,14 +79,14 @@ export const getTargetAccountName = (operationEvent: OperationEvent): string | u const operationType = operationEvent?.$case; if (operationType === "exportChargeRecord" || operationType === "exportPayRecord") { switch (operationEvent[operationType].target.$case) { - case "accountOfTenant" : - return operationEvent[operationType].target.accountOfTenant.accountName; - case "accountsOfTenant" : - return operationEvent[operationType].target.accountsOfTenant.accountNames; - case "accountsOfAllTenants": - return operationEvent[operationType].target.accountsOfAllTenants.accountNames; - default: - return; + case "accountOfTenant" : + return operationEvent[operationType].target.accountOfTenant.accountName; + case "accountsOfTenant" : + return operationEvent[operationType].target.accountsOfTenant.accountNames; + case "accountsOfAllTenants": + return operationEvent[operationType].target.accountsOfAllTenants.accountNames; + default: + return; } } else if (operationType === "exportOperationLog") { const source = operationEvent[operationType].source; @@ -129,9 +129,7 @@ export const checkCustomEventType = async (em: SqlEntityManager, op || !existTypeLog.metaData || !existTypeLog.metaData.$case || existTypeLog.metaData.$case !== "customEvent" - || !existTypeLog.metaData.customEvent - || !existTypeLog.metaData.customEvent.name - || !existTypeLog.metaData.customEvent.name.i18n + || !existTypeLog.metaData.customEvent?.name?.i18n ) { return; } @@ -172,10 +170,10 @@ export const addOperationLogAccountNames = (operationLog: OperationLog): Operati return logCopy; }; switch (targetCase) { - case "accountsOfTenant" : - case "accountsOfAllTenants": - return caseObject.accountNames ? operationLog : addLogAccountNames(); - default: - return operationLog; + case "accountsOfTenant" : + case "accountsOfAllTenants": + return caseObject.accountNames ? operationLog : addLogAccountNames(); + default: + return operationLog; } }; diff --git a/apps/auth/package.json b/apps/auth/package.json index 3874cc8221..ee0c0e9565 100644 --- a/apps/auth/package.json +++ b/apps/auth/package.json @@ -8,7 +8,8 @@ "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", "dev": "node -r ts-node/register -r tsconfig-paths/register --watch src/index.ts", "serve": "node build/index.js", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/apps/auth/src/app.ts b/apps/auth/src/app.ts index 73d4071858..f98a189157 100644 --- a/apps/auth/src/app.ts +++ b/apps/auth/src/app.ts @@ -26,7 +26,7 @@ type PluginOverrides = Map; function applyPlugins(server: FastifyInstance, pluginOverrides?: PluginOverrides) { plugins.forEach((plugin) => { - server.register(pluginOverrides && pluginOverrides.has(plugin) + void server.register(pluginOverrides?.has(plugin) ? pluginOverrides.get(plugin)! : plugin); }); @@ -58,7 +58,7 @@ export function buildApp(pluginOverrides?: PluginOverrides) { applyPlugins(server, pluginOverrides); - routes.forEach((r) => server.register(r)); + routes.forEach((r) => void server.register(r)); if (authConfig.captcha.enabled) { registerCaptchaRoute(server); diff --git a/apps/auth/src/auth/AuthProvider.ts b/apps/auth/src/auth/AuthProvider.ts index 4ebb086d12..4ed0d1b704 100644 --- a/apps/auth/src/auth/AuthProvider.ts +++ b/apps/auth/src/auth/AuthProvider.ts @@ -12,7 +12,7 @@ import type { FastifyReply, FastifyRequest } from "fastify"; -export type ValidationResult = "" | string; +export type ValidationResult = string | undefined; export interface CreateUserInfo { mail: string; @@ -43,7 +43,7 @@ export interface AuthProvider { createUser: undefined | ((info: CreateUserInfo, req: FastifyRequest) => Promise); changePassword: undefined | ((id: string, newPassword: string, req: FastifyRequest) => Promise); checkPassword: undefined | ((identityId: string, password: string, req: FastifyRequest) - => Promise); + => Promise); changeEmail: undefined | ((id: string, newEmail: string, req: FastifyRequest) => Promise); } diff --git a/apps/auth/src/auth/ldap/createUser.ts b/apps/auth/src/auth/ldap/createUser.ts index 9f241ffff8..ec55d39bd6 100644 --- a/apps/auth/src/auth/ldap/createUser.ts +++ b/apps/auth/src/auth/ldap/createUser.ts @@ -92,7 +92,7 @@ export async function createUser( gidNumber: id, }; - userEntry["gidNumber"] = id; + userEntry.gidNumber = id; if (config.extraProps) { applyExtraProps(groupEntry, config.extraProps, userEntry); @@ -115,7 +115,7 @@ export async function createUser( req.log.info("ldap.addUser.groupStrategy is one-group-for-all-users."); req.log.info("Using existing group %s for the user", config.gidNumber); - userEntry["gidNumber"] = config.gidNumber; + userEntry.gidNumber = config.gidNumber; } req.log.info("Adding people %s with entry info %o", userDn, userEntry); @@ -152,9 +152,13 @@ export async function createUser( if (!members) { req.log.error("Didn't find LDAP group %s", addUserToLdapGroup); - throw { code: "INTERNAL_ERROR" }; + class RequestError extends Error { + constructor(public code: string, message: string) { + super(message); + } + } + throw new RequestError("INTERNAL_ERROR", "Didn't find LDAP group " + addUserToLdapGroup); } - // add the dn of the new user to the value const modify = promisify(client.modify.bind(client)); await modify(addUserToLdapGroup, new ldapjs.Change({ diff --git a/apps/auth/src/auth/ldap/helpers.ts b/apps/auth/src/auth/ldap/helpers.ts index a368d00f5a..e99ce77e1c 100644 --- a/apps/auth/src/auth/ldap/helpers.ts +++ b/apps/auth/src/auth/ldap/helpers.ts @@ -36,7 +36,9 @@ export const useLdap = ( return await (async () => { await promisify(client.bind.bind(client))(user.dn, user.password); return await consume(client); - })().finally(unbind); + })().finally(() => { + void unbind(); + }); }; }; @@ -51,7 +53,7 @@ export const searchOne = async ( client.search(searchBase, searchOptions, (err, res) => { - if (err) { rej(err); return; } + if (err) { rej(err as Error); return; } logger.info("Search started"); let found = false; @@ -82,7 +84,7 @@ export const searchOne = async ( res.on("error", (err) => { logger.error("Error. %o", err); - rej(err); + rej(err as Error); }); res.on("end", (result) => { @@ -90,7 +92,7 @@ export const searchOne = async ( if (result?.status === 0) { resolve(undefined); } else { - rej(result?.errorMessage); + rej(new Error(result?.errorMessage)); } }); }); diff --git a/apps/auth/src/auth/ldap/index.ts b/apps/auth/src/auth/ldap/index.ts index 69ff626b00..a53aed3a21 100644 --- a/apps/auth/src/auth/ldap/index.ts +++ b/apps/auth/src/auth/ldap/index.ts @@ -29,7 +29,7 @@ export const createLdapAuthProvider = (f: FastifyInstance) => { registerPostHandler(f, ldap); registerOtpBindPostHandler(f, ldap); - return { + return { serveLoginHtml: (callbackUrl, req, rep) => serveLoginHtml(false, callbackUrl, req, rep), fetchAuthTokenInfo: async () => undefined, getUser: async (identityId, req) => useLdap(req.log, ldap)(async (client) => ( @@ -70,6 +70,6 @@ export const createLdapAuthProvider = (f: FastifyInstance) => { return result ? "OK" : "Wrong"; }); }, - }; + } as AuthProvider; }; diff --git a/apps/auth/src/auth/ldap/password.ts b/apps/auth/src/auth/ldap/password.ts index 8f96eef33e..53c7132d8d 100644 --- a/apps/auth/src/auth/ldap/password.ts +++ b/apps/auth/src/auth/ldap/password.ts @@ -76,12 +76,8 @@ export async function modifyPassword( userDn: string, newPassword: string, ): Promise { - try { - return await useLdap(log, ldap, { dn: ldap.bindDN, password: ldap.bindPassword })(async (client) => { - await modifyPasswordBase(userDn, undefined, newPassword, client); - return true; - }); - } catch (e: any) { - throw e; - } + return await useLdap(log, ldap, { dn: ldap.bindDN, password: ldap.bindPassword })(async (client) => { + await modifyPasswordBase(userDn, undefined, newPassword, client); + return true; + }); } diff --git a/apps/auth/src/auth/ldap/postHandler.ts b/apps/auth/src/auth/ldap/postHandler.ts index 27f09aa120..01366b791d 100644 --- a/apps/auth/src/auth/ldap/postHandler.ts +++ b/apps/auth/src/auth/ldap/postHandler.ts @@ -23,7 +23,7 @@ import { authConfig, LdapConfigSchema } from "src/config/auth"; export function registerPostHandler(f: FastifyInstance, ldapConfig: LdapConfigSchema) { - f.register(formBody); + void f.register(formBody); const bodySchema = Type.Object({ username: Type.String(), diff --git a/apps/auth/src/auth/otp/helper.ts b/apps/auth/src/auth/otp/helper.ts index 9b218d250d..d3d9987985 100644 --- a/apps/auth/src/auth/otp/helper.ts +++ b/apps/auth/src/auth/otp/helper.ts @@ -241,7 +241,7 @@ export async function remoteValidateOtpCode(userId: string, logger: FastifyBaseL userId: userId, }), }).then(async (response) => { - const result: {result: boolean} = await response.json(); + const result: { result: boolean } = await response.json(); return result.result; }).catch((e) => { logger.error(e, "error in verifying otp code in remote"); diff --git a/apps/auth/src/auth/otp/routeHandles.ts b/apps/auth/src/auth/otp/routeHandles.ts index 85e3d9dac6..abd922a10c 100644 --- a/apps/auth/src/auth/otp/routeHandles.ts +++ b/apps/auth/src/auth/otp/routeHandles.ts @@ -57,8 +57,7 @@ export function bindRedirectLoinUIAndBindUIRoute(f: FastifyInstance, otp: OtpCon if (action === "backToLoginUI") { const loginUrl = joinWithUrl(otp.ldap!.scowHost, config.BASE_PATH, config.AUTH_BASE_PATH, `/public/auth?callbackUrl=${encodeURIComponent(callbackUrl)}`); - res.redirect(loginUrl); - return; + return res.redirect(loginUrl); } } if (otp.type === OtpStatusOptions.remote) { @@ -66,8 +65,7 @@ export function bindRedirectLoinUIAndBindUIRoute(f: FastifyInstance, otp: OtpCon if (!otp.remote!.redirectUrl) { throw new Error("otp.remote!.redirectUrl is undefined"); } - res.redirect(otp.remote!.redirectUrl); - return; + return res.redirect(otp.remote!.redirectUrl); } } return; @@ -123,7 +121,7 @@ export function bindClickRequestBindingLinkRoute( emailAddress: Type.String(), callbackUrl: Type.String(), }); - f.post<{Body: Static}>( + f.post<{ Body: Static }>( "/public/otp/sendEmail", { schema: { @@ -152,63 +150,63 @@ export function bindClickAuthLinkInEmailRoute( // 用于处理用户在邮箱中点击确认链接 f.get<{ - Querystring: Static - }>( - "/public/otp/email/validation", - { - schema: { - querystring: QuerystringSchema, - }, + Querystring: Static + }>( + "/public/otp/email/validation", + { + schema: { + querystring: QuerystringSchema, }, - async (req, res) => { - const { token, callbackUrl } = req.query; - const logger = req.log; - const ivAndKey = await getIvAndKey(f); - if (!ivAndKey) { - // 返回用户信息过期 - await renderBindOtpHtml(false, req, res, callbackUrl, - { bindLimitMinutes: otpLdap.bindLimitMinutes, tokenNotFound: true }); - return; - } - const decryptedOtpSessionToken = decryptData(ivAndKey, token); - const otpSession = await getOtpSession(decryptedOtpSessionToken, f); - if (!otpSession) { - // 信息过期 - await renderBindOtpHtml(false, req, res, callbackUrl, - { bindLimitMinutes: otpLdap.bindLimitMinutes, tokenNotFound: true }); - return; - } - // 将secret信息存入ldap; - const secret = speakeasy.generateSecret({ length: 20 }).base32; - await useLdap(logger, ldapConfig)(async (client) => { - logger.info("Binding as %s successful.", otpSession.dn); - const modify = promisify(client.modify.bind(client)); - await modify(otpSession.dn, new ldapjs.Change({ - operation: "replace", - modification: { - [otpLdap.secretAttributeName]: secret, - }, - }), - ); - }); - const uid = otpSession.dn.match(/uid=([^,]+)/i)?.[1]; - const url = speakeasy.otpauthURL({ - secret: secret, - label: `${uid}@${otpLdap.label}`, - issuer: uid as string, - digits: 6, - period: 30, - algorithm: "sha1", - encoding: "base32", - }); - const urlImg = await QRCode.toDataURL(url); - await f.redis.del(decryptedOtpSessionToken); - const renderedFile = await renderLiquidFile("qrcode", { - contentText: otpLdap.qrcodeDescription, - urlImg, - callbackUrl: callbackUrl, - }); - await res.header("Content-Type", "text/html; charset=utf-8").send(renderedFile); + }, + async (req, res) => { + const { token, callbackUrl } = req.query; + const logger = req.log; + const ivAndKey = await getIvAndKey(f); + if (!ivAndKey) { + // 返回用户信息过期 + await renderBindOtpHtml(false, req, res, callbackUrl, + { bindLimitMinutes: otpLdap.bindLimitMinutes, tokenNotFound: true }); + return; + } + const decryptedOtpSessionToken = decryptData(ivAndKey, token); + const otpSession = await getOtpSession(decryptedOtpSessionToken, f); + if (!otpSession) { + // 信息过期 + await renderBindOtpHtml(false, req, res, callbackUrl, + { bindLimitMinutes: otpLdap.bindLimitMinutes, tokenNotFound: true }); + return; + } + // 将secret信息存入ldap; + const secret = speakeasy.generateSecret({ length: 20 }).base32; + await useLdap(logger, ldapConfig)(async (client) => { + logger.info("Binding as %s successful.", otpSession.dn); + const modify = promisify(client.modify.bind(client)); + await modify(otpSession.dn, new ldapjs.Change({ + operation: "replace", + modification: { + [otpLdap.secretAttributeName]: secret, + }, + }), + ); + }); + const uid = (/uid=([^,]+)/i.exec(otpSession.dn))?.[1]; + const url = speakeasy.otpauthURL({ + secret: secret, + label: `${uid}@${otpLdap.label}`, + issuer: uid!, + digits: 6, + period: 30, + algorithm: "sha1", + encoding: "base32", }); + const urlImg = await QRCode.toDataURL(url); + await f.redis.del(decryptedOtpSessionToken); + const renderedFile = await renderLiquidFile("qrcode", { + contentText: otpLdap.qrcodeDescription, + urlImg, + callbackUrl: callbackUrl, + }); + await res.header("Content-Type", "text/html; charset=utf-8").send(renderedFile); + }); } diff --git a/apps/auth/src/auth/ssh/index.ts b/apps/auth/src/auth/ssh/index.ts index 8223f62fd2..64ba2e2845 100644 --- a/apps/auth/src/auth/ssh/index.ts +++ b/apps/auth/src/auth/ssh/index.ts @@ -29,12 +29,13 @@ function checkLoginNode(sshConfig: SshConfigSchema) { if (Object.keys(clusters).length === 0) { throw new Error("No cluster has been set in clusters config"); } - const clusterConfig = Object.values(clusters)[0]; + const clusterId = Object.keys(clusters)[0]; + const clusterConfig = clusters[clusterId]; loginNode = getLoginNode(clusterConfig.loginNodes[0]).address; if (!loginNode) { - throw new Error(`Cluster ${clusterConfig.displayName} has no login node.`); + throw new Error(`Cluster ${clusterId} has no login node.`); } } diff --git a/apps/auth/src/auth/ssh/postHandler.ts b/apps/auth/src/auth/ssh/postHandler.ts index 2b9ea953db..2bba8cea77 100644 --- a/apps/auth/src/auth/ssh/postHandler.ts +++ b/apps/auth/src/auth/ssh/postHandler.ts @@ -22,7 +22,7 @@ import { validateLoginParams } from "src/auth/validateLoginParams"; export function registerPostHandler(f: FastifyInstance, loginNode: string) { - f.register(formBody); + void f.register(formBody); const bodySchema = Type.Object({ username: Type.String(), diff --git a/apps/auth/src/config/auth.ts b/apps/auth/src/config/auth.ts index 1a33c50219..440e17eb16 100644 --- a/apps/auth/src/config/auth.ts +++ b/apps/auth/src/config/auth.ts @@ -18,7 +18,7 @@ import { AuthType } from "./AuthType"; export enum NewUserGroupStrategy { "newGroupPerUser" = "newGroupPerUser", - "oneGroupForAllUsers" = "oneGroupForAllUsers" + "oneGroupForAllUsers" = "oneGroupForAllUsers", } export enum OtpStatusOptions { @@ -32,7 +32,9 @@ export enum ScowLogoType { } // 创建配置文件中显示文字项的配置类型 -export const createI18nStringSchema = ({ description, defaultValue }: {description: string, defaultValue?: string}) => { +export const createI18nStringSchema = ({ description, defaultValue }: { + description: string, defaultValue?: string +}) => { return Type.Union([ Type.String(), Type.Object({ @@ -246,7 +248,7 @@ export const getAuthConfig = () => { const config = getConfigFromFile(AuthConfigSchema, AUTH_CONFIG_FILE, DEFAULT_CONFIG_BASE_PATH); // validate - if (config.authType === "ldap") { + if (config.authType === AuthType.ldap) { if (!config.ldap) { throw new Error("authType is set to ldap, but ldap config is not set"); } diff --git a/apps/auth/src/index.ts b/apps/auth/src/index.ts index f7fc80e0eb..b1055b0921 100644 --- a/apps/auth/src/index.ts +++ b/apps/auth/src/index.ts @@ -14,5 +14,5 @@ import { buildApp, startServer } from "./app"; const server = buildApp(); -startServer(server); +void startServer(server); diff --git a/apps/auth/src/plugins/gracefulShutdown.ts b/apps/auth/src/plugins/gracefulShutdown.ts index 68dfe877fd..a876dc16f4 100644 --- a/apps/auth/src/plugins/gracefulShutdown.ts +++ b/apps/auth/src/plugins/gracefulShutdown.ts @@ -15,7 +15,7 @@ import fp from "fastify-plugin"; export const gracefulShutdownPlugin = fp(async (f) => { - f.register(gracefulShutdown); + await f.register(gracefulShutdown); f.addHook("onClose", () => { process.removeAllListeners("SIGTERM"); diff --git a/apps/auth/src/plugins/static.ts b/apps/auth/src/plugins/static.ts index 6117dd6fc6..6b012038b9 100644 --- a/apps/auth/src/plugins/static.ts +++ b/apps/auth/src/plugins/static.ts @@ -15,7 +15,7 @@ import fp from "fastify-plugin"; import path from "path"; export const staticPlugin = fp(async (fp) => { - fp.register(fastifyStatic, { + await fp.register(fastifyStatic, { root: path.join(process.cwd(), "public"), prefix: "/public/assets/", // optional: default '/' }); diff --git a/apps/auth/src/plugins/view.ts b/apps/auth/src/plugins/view.ts index ec40a04bd6..00480120ad 100644 --- a/apps/auth/src/plugins/view.ts +++ b/apps/auth/src/plugins/view.ts @@ -21,7 +21,7 @@ export const viewPlugin = fp(async (f) => { }); - f.register(view, { + await f.register(view, { engine: { liquid }, root: "views", }); diff --git a/apps/auth/src/utils/validations.ts b/apps/auth/src/utils/validations.ts index 9a78523e19..f136227cab 100644 --- a/apps/auth/src/utils/validations.ts +++ b/apps/auth/src/utils/validations.ts @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -export type RequiredBy = Omit & Required> +export type RequiredBy = Omit & Required>; export function ensureNotUndefined(obj: TObj, keys: TKeys[]): RequiredBy { for (const key of keys) { diff --git a/apps/auth/tests/ldap/ldap.test.ts b/apps/auth/tests/ldap/ldap.test.ts index 6c17ed2dc4..1a77d1f755 100644 --- a/apps/auth/tests/ldap/ldap.test.ts +++ b/apps/auth/tests/ldap/ldap.test.ts @@ -70,6 +70,7 @@ async function removeEvenNotExist(client: Client, dn: string) { if (err instanceof NoSuchObjectError) { console.log("No entity with dn " + dn); } else { + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors rej(err); } } diff --git a/apps/auth/tests/ldap/noAddUser.test.ts b/apps/auth/tests/ldap/noAddUser.test.ts index 9a037b0fb3..d14c9ff198 100644 --- a/apps/auth/tests/ldap/noAddUser.test.ts +++ b/apps/auth/tests/ldap/noAddUser.test.ts @@ -35,6 +35,6 @@ it("should report no createUser capability", async () => { url: "/capabilities", }); - const body = await resp.json() as Capabilities; + const body: Capabilities = await resp.json(); expect(body.createUser).toBeFalse(); }); diff --git a/apps/cli/package.json b/apps/cli/package.json index af5ef27098..977097e89a 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -8,7 +8,8 @@ "scripts": { "dev": "node -r ts-node/register -r tsconfig-paths/register src/index.ts", "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pkg --compress GZip --no-bytecode --public-packages \"*\" --public .", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "dependencies": { "@scow/lib-config": "workspace:*", diff --git a/apps/cli/src/cmd/compose.ts b/apps/cli/src/cmd/compose.ts index 42bfad2ba4..d9332803d9 100644 --- a/apps/cli/src/cmd/compose.ts +++ b/apps/cli/src/cmd/compose.ts @@ -22,5 +22,5 @@ export const runCompose = async (options: Options, ...baseCommands: string[]) => const config = getInstallConfig(options.configPath); - runComposeCommand(config, [...baseCommands, ...options._.slice(1).map((x) => String(x))]); + await runComposeCommand(config, [...baseCommands, ...options._.slice(1).map((x) => String(x))]); }; diff --git a/apps/cli/src/cmd/db.ts b/apps/cli/src/cmd/db.ts index affe2766aa..6ec78d9507 100644 --- a/apps/cli/src/cmd/db.ts +++ b/apps/cli/src/cmd/db.ts @@ -25,5 +25,5 @@ export const enterDb = async (options: Options) => { throw new Error("MIS is not deployed. db is not deployed"); } - runComposeCommand(config, ["exec", "db", "mysql", "-uroot", `-p'${config.mis.dbPassword}'`]); + await runComposeCommand(config, ["exec", "db", "mysql", "-uroot", `-p'${config.mis.dbPassword}'`]); }; diff --git a/apps/cli/src/cmd/enterAiDb.ts b/apps/cli/src/cmd/enterAiDb.ts index 5c06cd5bdf..86f4ef858f 100644 --- a/apps/cli/src/cmd/enterAiDb.ts +++ b/apps/cli/src/cmd/enterAiDb.ts @@ -25,5 +25,5 @@ export const enterAiDb = async (options: Options) => { throw new Error("ai is not deployed. db is not deployed"); } - runComposeCommand(config, ["exec", "ai-db", "mysql", "-uroot", `-p'${config.ai.dbPassword}'`]); + await runComposeCommand(config, ["exec", "ai-db", "mysql", "-uroot", `-p'${config.ai.dbPassword}'`]); }; diff --git a/apps/cli/src/cmd/enterAuditDb.ts b/apps/cli/src/cmd/enterAuditDb.ts index f9a26a9e30..8c204070d7 100644 --- a/apps/cli/src/cmd/enterAuditDb.ts +++ b/apps/cli/src/cmd/enterAuditDb.ts @@ -25,5 +25,5 @@ export const enterAuditDb = async (options: Options) => { throw new Error("audit is not deployed. db is not deployed"); } - runComposeCommand(config, ["exec", "audit-db", "mysql", "-uroot", `-p'${config.audit.dbPassword}'`]); + await runComposeCommand(config, ["exec", "audit-db", "mysql", "-uroot", `-p'${config.audit.dbPassword}'`]); }; diff --git a/apps/cli/src/compose/index.ts b/apps/cli/src/compose/index.ts index 0a4bd135ef..1edc6ec589 100644 --- a/apps/cli/src/compose/index.ts +++ b/apps/cli/src/compose/index.ts @@ -252,7 +252,7 @@ export const createComposeSpec = (config: InstallConfigSchema) => { const scowdSslScowPrivateKeyPath = config.scowd?.ssl?.scowPrivateKeyPath ? join(configPath, config.scowd.ssl.scowPrivateKeyPath) : ""; - composeSpec.volumes["portal_data"] = {}; + composeSpec.volumes.portal_data = {}; addService("portal-server", { image: scowImage, @@ -353,7 +353,7 @@ export const createComposeSpec = (config: InstallConfigSchema) => { }, }); - composeSpec.volumes["db_data"] = {}; + composeSpec.volumes.db_data = {}; addService("db", { image: config.mis.mysqlImage, @@ -386,7 +386,7 @@ export const createComposeSpec = (config: InstallConfigSchema) => { }, }); - composeSpec.volumes["audit_db_data"] = {}; + composeSpec.volumes.audit_db_data = {}; addService("audit-db", { image: config.audit.mysqlImage, @@ -429,7 +429,7 @@ export const createComposeSpec = (config: InstallConfigSchema) => { }, }); - composeSpec.volumes["ai_db_data"] = {}; + composeSpec.volumes.ai_db_data = {}; addService("ai-db", { image: config.ai.mysqlImage, diff --git a/apps/cli/src/compose/spec.ts b/apps/cli/src/compose/spec.ts index 4adaca010f..11bf41164f 100644 --- a/apps/cli/src/compose/spec.ts +++ b/apps/cli/src/compose/spec.ts @@ -12,9 +12,7 @@ export interface LoggingOption { driver: string; - options: { - [key: string]: string; - } + options: Record } export interface ServiceSpec { diff --git a/apps/cli/src/config/install.ts b/apps/cli/src/config/install.ts index 440d3826b2..b7b8f1eb22 100644 --- a/apps/cli/src/config/install.ts +++ b/apps/cli/src/config/install.ts @@ -17,7 +17,7 @@ import { logger } from "src/log"; export enum AuthCustomType { external = "external", - image = "image" + image = "image", } export const InstallConfigSchema = Type.Object({ diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index eeed74b6ad..084e04e6e1 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -32,7 +32,7 @@ import yargs from "yargs/yargs"; const version = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")).version; -yargs(hideBin(process.argv)) +void yargs(hideBin(process.argv)) .options({ configPath: { alias: "c", @@ -86,7 +86,7 @@ yargs(hideBin(process.argv)) }, }); }, (argv) => { - init(argv); + void init(argv); }) .command("update", "Update cli", (yargs) => { return yargs.options({ @@ -112,7 +112,7 @@ yargs(hideBin(process.argv)) }, }); }, (argv) => { - updateCli(argv); + void updateCli(argv); }) .command("generate", "Generate docker-compose.yml", (yargs) => { return yargs @@ -135,13 +135,13 @@ yargs(hideBin(process.argv)) generateDockerComposeYml(argv); }) .command("db", "Enter mis db", (y) => y, (argv) => { - enterDb(argv); + void enterDb(argv); }) .command("audit-db", "Enter audit db", (y) => y, (argv) => { - enterAuditDb(argv); + void enterAuditDb(argv); }) .command("ai-db", "Enter ai db", (y) => y, (argv) => { - enterAiDb(argv); + void enterAiDb(argv); }) .command("compose", "Run arbitrary compose commands", (y) => { return y.strict(false).parserConfiguration({ "unknown-options-as-args": true }); diff --git a/apps/cli/tests/compose.test.ts b/apps/cli/tests/compose.test.ts index 67a8fe87bd..8bb7772e09 100644 --- a/apps/cli/tests/compose.test.ts +++ b/apps/cli/tests/compose.test.ts @@ -44,7 +44,7 @@ it("generate correct paths", async () => { expect(composeConfig.services["portal-web"].environment).toContain("MIS_URL=/mis"); expect(composeConfig.services["portal-web"].environment).toContain("MIS_SERVER_URL=mis-server:5000"); expect(composeConfig.services["mis-web"].environment).toContain("PORTAL_URL=/"); - expect(composeConfig.services["ai"].environment).toContain("MIS_URL=/mis"); + expect(composeConfig.services.ai.environment).toContain("MIS_URL=/mis"); }); it("sets proxy_read_timeout", async () => { @@ -53,7 +53,7 @@ it("sets proxy_read_timeout", async () => { const composeSpec = createComposeSpec(config); - expect(composeSpec.services["gateway"].environment) + expect(composeSpec.services.gateway.environment) .toInclude(`PROXY_READ_TIMEOUT=${config.gateway.proxyReadTimeout}`); }); @@ -77,7 +77,7 @@ describe("sets custom auth environment", () => { const spec = createComposeSpec(getInstallConfig(configPath)); - expect(spec.services["auth"].environment) + expect(spec.services.auth.environment) .toInclude("CUSTOM_AUTH_KEY=CUSTOM_AUTH_VALUE"); }); @@ -98,7 +98,7 @@ describe("sets custom auth environment", () => { const spec = createComposeSpec(getInstallConfig(configPath)); - expect(spec.services["auth"].environment) + expect(spec.services.auth.environment) .toInclude("CUSTOM_AUTH_KEY=CUSTOM_AUTH_VALUE"); }); }); diff --git a/apps/gateway/package.json b/apps/gateway/package.json index 5b894f32e2..fe86f69477 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -10,7 +10,8 @@ "scripts": { "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", "serve": "node build/index.js", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "dependencies": { "@scow/lib-config": "workspace:*", diff --git a/apps/mis-server/package.json b/apps/mis-server/package.json index 2dd474b01c..596dd65c2e 100644 --- a/apps/mis-server/package.json +++ b/apps/mis-server/package.json @@ -9,7 +9,8 @@ "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", "serve": "node build/index.js", "test": "jest --forceExit", - "orm": "dotenv -e env/.env.dev -- npx mikro-orm" + "orm": "dotenv -e env/.env.dev -- npx mikro-orm", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "scripts", diff --git a/apps/mis-server/src/bl/block.ts b/apps/mis-server/src/bl/block.ts index 3808dc3799..c135ad9987 100644 --- a/apps/mis-server/src/bl/block.ts +++ b/apps/mis-server/src/bl/block.ts @@ -68,6 +68,7 @@ export async function updateBlockStatusInSlurm( ); blockedAccounts.push(account.accountName); } catch (error) { + logger.warn("Failed to block account %s in slurm: %o", account.accountName, error); blockedFailedAccounts.push(account.accountName); } } @@ -87,6 +88,8 @@ export async function updateBlockStatusInSlurm( ); blockedUserAccounts.push([ua.user.getProperty("userId"), ua.account.getProperty("accountName")]); } catch (error) { + logger.warn("Failed to block user accounts (userid: %s, account_name: %s) in slurm: %o", + ua.user.$.userId, ua.account.$.accountName, error); blockedFailedUserAccounts.push({ userId: ua.user.$.userId, accountName: ua.account.$.accountName, @@ -151,6 +154,7 @@ export async function updateUnblockStatusInSlurm( ); unblockedAccounts.push(account.accountName); } catch (error) { + logger.warn("Failed to unblock account %s in slurm: %o", account.accountName, error); unblockedFailedAccounts.push(account.accountName); } } diff --git a/apps/mis-server/src/bl/charging.ts b/apps/mis-server/src/bl/charging.ts index 0fccb1f43e..3cb5a2c329 100644 --- a/apps/mis-server/src/bl/charging.ts +++ b/apps/mis-server/src/bl/charging.ts @@ -111,14 +111,14 @@ export async function pay( }; } -type ChargeRequest = { +interface ChargeRequest { target: Loaded | Tenant; amount: Decimal; comment: string; type: string; userId?: string; metadata?: AnyJson; -}; +} export async function charge( request: ChargeRequest, em: SqlEntityManager, diff --git a/apps/mis-server/src/bl/importUsers.ts b/apps/mis-server/src/bl/importUsers.ts index e64be4f641..eeeb0c0af3 100644 --- a/apps/mis-server/src/bl/importUsers.ts +++ b/apps/mis-server/src/bl/importUsers.ts @@ -28,7 +28,7 @@ import { toRef } from "src/utils/orm"; export interface ImportUsersData { accounts: { accountName: string; - users: {userId: string; userName: string; blocked: boolean}[]; + users: { userId: string; userName: string; blocked: boolean }[]; owner: string; blocked: boolean; }[]; @@ -56,9 +56,9 @@ export async function importUsers(data: ImportUsersData, em: SqlEntityManager, const existingUsers = await em.find(User, { userId: { $in: Object.keys(usersMap) } }, { populate: ["tenant"]}); existingUsers.forEach((u) => { if (u.tenant.$.name !== DEFAULT_TENANT_NAME) { - throw { + throw { code: Status.INVALID_ARGUMENT, message: `user ${u.userId} has existing and belongs to ${u.tenant.$.name}`, - }; + } as ServiceError; } usersMap[u.userId] = u; }); @@ -69,7 +69,7 @@ export async function importUsers(data: ImportUsersData, em: SqlEntityManager, // 导入账户时,如果在集群中的账户状态为正常,则scow同步正常状态 accountMap[account.accountName] = new Account({ accountName: account.accountName, comment: "", blockedInCluster: Boolean(account.blocked), - tenant, state: Boolean(account.blocked) ? AccountState.BLOCKED_BY_ADMIN : AccountState.NORMAL, + tenant, state: account.blocked ? AccountState.BLOCKED_BY_ADMIN : AccountState.NORMAL, }); }); const existingAccounts = await em.find(Account, @@ -78,10 +78,10 @@ export async function importUsers(data: ImportUsersData, em: SqlEntityManager, ); existingAccounts.forEach((a) => { if (a.tenant.$.name !== DEFAULT_TENANT_NAME) { - throw { + throw { code: Status.INVALID_ARGUMENT, message: `account ${a.accountName} has existing and belongs to ${a.tenant.$.name}`, - }; + } as ServiceError; } accountMap[a.accountName] = a; }); diff --git a/apps/mis-server/src/entities/Account.ts b/apps/mis-server/src/entities/Account.ts index 862a977aa5..4552e28806 100644 --- a/apps/mis-server/src/entities/Account.ts +++ b/apps/mis-server/src/entities/Account.ts @@ -30,39 +30,39 @@ export enum AccountState { @Entity() export class Account { @PrimaryKey() - id!: number; + id!: number; @Property({ unique: true }) - accountName: string; + accountName: string; @ManyToOne(() => Tenant, { ref: true }) - tenant: Ref; + tenant: Ref; @Property() - blockedInCluster: boolean; + blockedInCluster: boolean; @OneToMany(() => UserAccount, (u) => u.account) - users = new Collection(this); + users = new Collection(this); @OneToOne(() => AccountWhitelist, (u) => u.account, { nullable: true, ref: true, unique: true, owner: true, }) - whitelist?: Ref; + whitelist?: Ref; @Property({ default: "" }) - comment: string; + comment: string; @Property({ type: DecimalType, defaultRaw: DECIMAL_DEFAULT_RAW }) - balance: Decimal = new Decimal(0); + balance: Decimal = new Decimal(0); @Property({ type: DecimalType, nullable: true }) - blockThresholdAmount: Decimal | undefined; + blockThresholdAmount: Decimal | undefined; @Enum({ items: () => AccountState, default: AccountState.NORMAL, comment: Object.values(AccountState).join(", ") }) - state: AccountState; + state: AccountState; @Property({ columnType: DATETIME_TYPE, nullable: true }) - createTime: Date; + createTime: Date; constructor(init: { accountName: string; @@ -79,8 +79,8 @@ export class Account { if (init.whitelist) { this.whitelist = toRef(init.whitelist); } - this.comment = init.comment || ""; - this.state = init.state || AccountState.NORMAL; + this.comment = init.comment ?? ""; + this.state = init.state ?? AccountState.NORMAL; this.createTime = init.createTime ?? new Date(); } } diff --git a/apps/mis-server/src/entities/AccountWhitelist.ts b/apps/mis-server/src/entities/AccountWhitelist.ts index 84213b1090..331f29e892 100644 --- a/apps/mis-server/src/entities/AccountWhitelist.ts +++ b/apps/mis-server/src/entities/AccountWhitelist.ts @@ -17,23 +17,23 @@ import { EntityOrRef, toRef } from "src/utils/orm"; @Entity() export class AccountWhitelist { @PrimaryKey() - id!: number; + id!: number; @OneToOne(() => Account, (a) => a.whitelist, { ref: true, nullable: false, unique: true }) - account: Ref; + account: Ref; @Property() - time: Date; + time: Date; @Property() - comment: string; + comment: string; @Property() - operatorId: string; + operatorId: string; // 当expirationTime为undefined时,即为永久有效 @Property({ nullable: true }) - expirationTime?: Date; + expirationTime?: Date; constructor(init: { account: EntityOrRef, diff --git a/apps/mis-server/src/entities/ChargeRecord.ts b/apps/mis-server/src/entities/ChargeRecord.ts index c90b54317f..cffeba58c4 100644 --- a/apps/mis-server/src/entities/ChargeRecord.ts +++ b/apps/mis-server/src/entities/ChargeRecord.ts @@ -22,36 +22,36 @@ import { type AnyJson } from "src/utils/types"; @Index({ name: "static_info", properties: ["time", "accountName", "amount"] }) export class ChargeRecord { @PrimaryKey() - id!: number; + id!: number; @Index({ name: "time" }) @Property() - time: Date; + time: Date; @Index() @Property() - tenantName: string; + tenantName: string; @Index() @Property({ nullable: true }) - accountName?: string; + accountName?: string; @Index() @Property({ nullable: true }) - userId?: string; + userId?: string; @Index() @Property() - type: string; + type: string; @Property({ type: DecimalType }) - amount: Decimal = new Decimal(0); + amount: Decimal = new Decimal(0); @Property() - comment: string; + comment: string; @Property({ type: "json", nullable: true }) - metadata?: AnyJson; + metadata?: AnyJson; constructor(init: { id?: number; diff --git a/apps/mis-server/src/entities/Cluster.ts b/apps/mis-server/src/entities/Cluster.ts index 080978bce5..786d2c1867 100644 --- a/apps/mis-server/src/entities/Cluster.ts +++ b/apps/mis-server/src/entities/Cluster.ts @@ -28,23 +28,23 @@ export interface LastActivationOperation { @Entity() export class Cluster { @PrimaryKey() - id!: number; + id!: number; @Property({ unique: true }) - clusterId: string; + clusterId: string; @Enum({ items: () => ClusterActivationStatus, default: ClusterActivationStatus.ACTIVATED, comment: Object.values(ClusterActivationStatus).join(", ") }) - activationStatus: ClusterActivationStatus; + activationStatus: ClusterActivationStatus; @Property({ type: "json", nullable: true }) - lastActivationOperation?: LastActivationOperation; + lastActivationOperation?: LastActivationOperation; @Property({ columnType: DATETIME_TYPE, nullable: true }) - createTime: Date; + createTime: Date; @Property({ columnType: DATETIME_TYPE, nullable: true, onUpdate: () => new Date() }) - updateTime: Date; + updateTime: Date; constructor(init: { clusterId: string; @@ -54,7 +54,7 @@ export class Cluster { updateTime?: Date; }) { this.clusterId = init.clusterId; - this.activationStatus = init.activationStatus || ClusterActivationStatus.ACTIVATED; + this.activationStatus = init.activationStatus ?? ClusterActivationStatus.ACTIVATED; this.lastActivationOperation = init.lastActivationOperation; this.createTime = init.createTime ?? new Date(); this.updateTime = init.updateTime ?? new Date(); diff --git a/apps/mis-server/src/entities/JobInfo.ts b/apps/mis-server/src/entities/JobInfo.ts index 6cf9084ec7..0ce5a0cd45 100644 --- a/apps/mis-server/src/entities/JobInfo.ts +++ b/apps/mis-server/src/entities/JobInfo.ts @@ -26,98 +26,98 @@ export interface JobPriceInfo { export class JobInfo { @PrimaryKey() - biJobIndex!: number; + biJobIndex!: number; @Property({ index: "idJob" }) - idJob!: number; + idJob!: number; @Property({ length: 255, comment: "账户", index: "account" }) - account!: string; + account!: string; @Property({ length: 127, comment: "用户名", index: "user" }) - user!: string; + user!: string; @Property({ length: 255, columnType: "tinytext", comment: "分区" }) - partition!: string; + partition!: string; @Property({ columnType: "text", comment: "使用节点列表" }) - nodelist!: string; + nodelist!: string; @Property({ length: 255, columnType: "tinytext", comment: "作业名" }) - jobName!: string; + jobName!: string; // 这里存的是scow中的集群名 @Property({ length: 50, comment: "集群名" }) - cluster!: string; + cluster!: string; @Index({ name: "time_submit" }) @Property({ comment: "提交时间" }) - timeSubmit!: Date; + timeSubmit!: Date; @Index({ name: "time_start" }) @Property({ comment: "开始时间" }) - timeStart!: Date; + timeStart!: Date; @Property({ comment: "结束时间", index: "time_end" }) - timeEnd!: Date; + timeEnd!: Date; @Property({ columnType: "int(10)", comment: "使用GPU数。来自gres_req字段" }) - gpu!: number; + gpu!: number; @Property({ columnType: "int unsigned", comment: "申请CPU数tres_req" }) - cpusReq!: number; + cpusReq!: number; @Property({ columnType: "int unsigned", comment: "申请的内存,单位MB,来自tres_req" }) - memReq!: number; + memReq!: number; @Property({ columnType: "int unsigned", comment: "申请节点数,tres_req" }) - nodesReq!: number; + nodesReq!: number; @Property({ columnType: "int unsigned", comment: "分配CPU数tres_alloc" }) - cpusAlloc!: number; + cpusAlloc!: number; @Property({ columnType: "int unsigned", comment: "分配的内存,单位MB,来自tres_alloc" }) - memAlloc!: number; + memAlloc!: number; @Property({ columnType: "int unsigned", comment: "分配节点数tres_alloc" }) - nodesAlloc!: number; + nodesAlloc!: number; @Property({ columnType: "int unsigned", comment: "作业时间限制" }) - timelimit!: number; + timelimit!: number; @Index({ name: "time_used" }) @Property({ columnType: "bigint unsigned", comment: "作业执行时间" }) - timeUsed!: number; + timeUsed!: number; @Index({ name: "time_wait" }) @Property({ columnType: "bigint unsigned", comment: "作业等待时间" }) - timeWait!: number; + timeWait!: number; @Property({ length: 255, comment: "QOS" }) - qos!: string; + qos!: string; @Index({ name: "record_time" }) @Property({ columnType: "timestamp", defaultRaw: "CURRENT_TIMESTAMP", comment: "记录时间" }) - recordTime!: Date; + recordTime!: Date; @Property() - tenant: string; + tenant: string; @Property({ default: UNKNOWN_PRICE_ITEM }) - accountBillingItemId: string; + accountBillingItemId: string; @Property({ default: UNKNOWN_PRICE_ITEM }) - tenantBillingItemId: string; + tenantBillingItemId: string; @Property({ type: DecimalType, defaultRaw: DECIMAL_DEFAULT_RAW }) - tenantPrice: Decimal = new Decimal(0); + tenantPrice: Decimal = new Decimal(0); @Property({ type: DecimalType, defaultRaw: DECIMAL_DEFAULT_RAW }) - accountPrice: Decimal = new Decimal(0); + accountPrice: Decimal = new Decimal(0); constructor( - job: {cluster: string} & ClusterJobInfo, + job: { cluster: string } & ClusterJobInfo, tenant: string | undefined, jobPriceInfo: JobPriceInfo, ) { diff --git a/apps/mis-server/src/entities/JobPriceChange.ts b/apps/mis-server/src/entities/JobPriceChange.ts index 7c2ac211c9..1c3ba9b04a 100644 --- a/apps/mis-server/src/entities/JobPriceChange.ts +++ b/apps/mis-server/src/entities/JobPriceChange.ts @@ -19,31 +19,31 @@ import { DATETIME_TYPE } from "src/utils/orm"; @Entity() export class JobPriceChange { @PrimaryKey() - id!: number; + id!: number; @Property({ type: "json", comment: "{ biJobIndex: number; tenantPrice: tenantPrice.toFixed(4), accountPrice: accountPrice.toFixed(4) }[]", }) - jobs: { biJobIndex: number; tenantPrice: string; accountPrice: string; }[]; + jobs: { biJobIndex: number; tenantPrice: string; accountPrice: string; }[]; @Property() - reason: string; + reason: string; @Property({ type: DecimalType, nullable: true }) - newTenantPrice?: Decimal; + newTenantPrice?: Decimal; @Property({ type: DecimalType, nullable: true }) - newAccountPrice?: Decimal; + newAccountPrice?: Decimal; @Property() - operatorId: string; + operatorId: string; @Property() - ipAddress: string; + ipAddress: string; @Property({ columnType: DATETIME_TYPE }) - time: Date; + time: Date; constructor(init: { jobs: JobInfo[]; diff --git a/apps/mis-server/src/entities/JobPriceItem.ts b/apps/mis-server/src/entities/JobPriceItem.ts index d18d2fa98b..303652ed09 100644 --- a/apps/mis-server/src/entities/JobPriceItem.ts +++ b/apps/mis-server/src/entities/JobPriceItem.ts @@ -20,34 +20,34 @@ export enum AmountStrategy { MAX_CPUSALLOC_MEM = "max-cpusAlloc-mem", MAX_GPU_CPUSALLOC = "max-gpu-cpusAlloc", GPU = "gpu", - CPUS_ALLOC = "cpusAlloc" + CPUS_ALLOC = "cpusAlloc", } @Entity() export class JobPriceItem { @PrimaryKey() - id!: number; + id!: number; @Property({ unique: true }) - itemId: string; + itemId: string; @Property({ type: ArrayType, comment: "集群,分区[,qos]" }) - path: string[]; + path: string[]; @Property() - description: string; + description: string; @ManyToOne(() => Tenant, { ref: true, nullable: true }) - tenant?: Ref; + tenant?: Ref; @Property({ type: DecimalType }) - price: Decimal; + price: Decimal; @Property({ comment: Object.values(AmountStrategy).join(", ") }) - amount: string; + amount: string; @Property({ columnType: DATETIME_TYPE }) - createTime: Date; + createTime: Date; constructor(init: { itemId: string; diff --git a/apps/mis-server/src/entities/PayRecord.ts b/apps/mis-server/src/entities/PayRecord.ts index aacb9e8f06..23ce915658 100644 --- a/apps/mis-server/src/entities/PayRecord.ts +++ b/apps/mis-server/src/entities/PayRecord.ts @@ -21,31 +21,31 @@ import { DATETIME_TYPE } from "src/utils/orm"; @Index({ name: "static_info", properties: ["time", "accountName", "amount"] }) export class PayRecord { @PrimaryKey() - id!: number; + id!: number; @Property({ columnType: DATETIME_TYPE }) - time: Date; + time: Date; @Property() - tenantName: string; + tenantName: string; @Property({ nullable: true }) - accountName?: string; + accountName?: string; @Property() - type: string; + type: string; @Property({ type: DecimalType }) - amount: Decimal = new Decimal(0); + amount: Decimal = new Decimal(0); @Property() - operatorId: string; + operatorId: string; @Property() - ipAddress: string; + ipAddress: string; @Property() - comment: string; + comment: string; constructor(init: { id?: number; diff --git a/apps/mis-server/src/entities/QueryCache.ts b/apps/mis-server/src/entities/QueryCache.ts index 17d4800404..7c32bd8595 100644 --- a/apps/mis-server/src/entities/QueryCache.ts +++ b/apps/mis-server/src/entities/QueryCache.ts @@ -16,16 +16,16 @@ import { DATETIME_TYPE } from "src/utils/orm"; @Entity() export class QueryCache { @PrimaryKey() - id!: number; + id!: number; @Property() - queryKey: string; + queryKey: string; @Property({ type: "json" }) - queryResult: any; + queryResult: any; @Property({ columnType: DATETIME_TYPE }) - timestamp: Date; + timestamp: Date; constructor(init: { id?: number; diff --git a/apps/mis-server/src/entities/StorageQuota.ts b/apps/mis-server/src/entities/StorageQuota.ts index a9224684bf..a17b88bc03 100644 --- a/apps/mis-server/src/entities/StorageQuota.ts +++ b/apps/mis-server/src/entities/StorageQuota.ts @@ -18,17 +18,17 @@ import { EntityOrRef, toRef } from "src/utils/orm"; @Entity() export class StorageQuota { @PrimaryKey() - id!: number; + id!: number; @ManyToOne(() => User, { deleteRule: "cascade", ref: true, nullable: false }) - user: Ref; + user: Ref; @Property() - cluster: string; + cluster: string; // 和后台统一,为B。1PB=10^15B,应该一个int就够用了 @Property({ columnType: "int" }) - storageQuota: number; + storageQuota: number; constructor(init: { user: EntityOrRef, diff --git a/apps/mis-server/src/entities/SystemState.ts b/apps/mis-server/src/entities/SystemState.ts index 7bec86e627..8934f646f0 100644 --- a/apps/mis-server/src/entities/SystemState.ts +++ b/apps/mis-server/src/entities/SystemState.ts @@ -15,10 +15,10 @@ import { Entity, PrimaryKey, Property } from "@mikro-orm/core"; @Entity() export class SystemState { @PrimaryKey() - key: string; + key: string; @Property() - value: string; + value: string; public static KEYS = { INITIALIZATION_TIME: "INITIALIZATION_TIME", diff --git a/apps/mis-server/src/entities/Tenant.ts b/apps/mis-server/src/entities/Tenant.ts index 4afbe31101..0792463cb5 100644 --- a/apps/mis-server/src/entities/Tenant.ts +++ b/apps/mis-server/src/entities/Tenant.ts @@ -19,19 +19,19 @@ import { CURRENT_TIMESTAMP, DATETIME_TYPE } from "src/utils/orm"; export class Tenant { @PrimaryKey() - id!: number; + id!: number; @Property({ unique: true }) - name: string; + name: string; @Property({ type: DecimalType, defaultRaw: DECIMAL_DEFAULT_RAW }) - balance: Decimal = new Decimal(0); + balance: Decimal = new Decimal(0); @Property({ type: DecimalType, defaultRaw: DECIMAL_DEFAULT_RAW }) - defaultAccountBlockThreshold: Decimal = new Decimal(0); + defaultAccountBlockThreshold: Decimal = new Decimal(0); @Property({ columnType: DATETIME_TYPE, defaultRaw: CURRENT_TIMESTAMP }) - createTime: Date; + createTime: Date; constructor(init: { name: string, diff --git a/apps/mis-server/src/entities/User.ts b/apps/mis-server/src/entities/User.ts index c83f619f1b..467c82f1ba 100644 --- a/apps/mis-server/src/entities/User.ts +++ b/apps/mis-server/src/entities/User.ts @@ -29,34 +29,34 @@ export enum TenantRole { @Entity() export class User { @PrimaryKey() - id!: number; + id!: number; @ManyToOne(() => Tenant, { ref: true }) - tenant: Ref; + tenant: Ref; @Property({ unique: true }) - userId: string; + userId: string; @OneToMany(() => StorageQuota, (u) => u.user) - storageQuotas = new Collection(this); + storageQuotas = new Collection(this); @Property() - name: string; + name: string; @Property() - email: string; + email: string; @Property({ columnType: DATETIME_TYPE, defaultRaw: CURRENT_TIMESTAMP }) - createTime: Date; + createTime: Date; @OneToMany(() => UserAccount, (u) => u.user) - accounts = new Collection(this); + accounts = new Collection(this); @Enum({ items: () => TenantRole, array: true, comment: Object.values(TenantRole).join(", ") }) - tenantRoles: TenantRole[]; + tenantRoles: TenantRole[]; @Enum({ items: () => PlatformRole, array: true, comment: Object.values(PlatformRole).join(", ") }) - platformRoles: PlatformRole[]; + platformRoles: PlatformRole[]; constructor(init: { userId: string; diff --git a/apps/mis-server/src/entities/UserAccount.ts b/apps/mis-server/src/entities/UserAccount.ts index 98936c3270..ea5a83c63c 100644 --- a/apps/mis-server/src/entities/UserAccount.ts +++ b/apps/mis-server/src/entities/UserAccount.ts @@ -40,29 +40,29 @@ export enum UserRole { @Entity() export class UserAccount { @PrimaryKey() - id!: number; + id!: number; @ManyToOne(() => User, { deleteRule: "cascade", ref: true, nullable: false }) - user: Ref; + user: Ref; @ManyToOne(() => Account, { deleteRule: "cascade", ref: true, nullable: false }) - account: Ref; + account: Ref; @Property({ columnType: "varchar(10)", comment: Object.values(UserStatus).join(", ") }) - blockedInCluster: UserStatus; + blockedInCluster: UserStatus; @Property({ columnType: "varchar(10)", comment: Object.values(UserRole).join(", ") }) - role: UserRole; + role: UserRole; @Property({ type: DecimalType, nullable: true }) - usedJobCharge?: Decimal; + usedJobCharge?: Decimal; @Property({ type: DecimalType, nullable: true }) - jobChargeLimit?: Decimal; + jobChargeLimit?: Decimal; @Enum({ items: () => UserStateInAccount, default: UserStateInAccount.NORMAL, comment: Object.values(UserStateInAccount).join(", ") }) - state?: UserStateInAccount; + state?: UserStateInAccount; constructor(init: { user: EntityOrRef, @@ -79,6 +79,6 @@ export class UserAccount { this.role = init.role; this.jobChargeLimit = init.jobChargeLimit; this.usedJobCharge = init.usedJobCharge; - this.state = init.state || UserStateInAccount.NORMAL; + this.state = init.state ?? UserStateInAccount.NORMAL; } } diff --git a/apps/mis-server/src/index.ts b/apps/mis-server/src/index.ts index c05b345b34..20d35f955e 100644 --- a/apps/mis-server/src/index.ts +++ b/apps/mis-server/src/index.ts @@ -31,20 +31,20 @@ async function main() { switch (command) { - case "fetchJobs": - await fetchJobs(em, logger, server.ext); - break; - - case "createPriceItems": - await createPriceItems(em, logger); - break; - - case "migrationUp": - await migrationUp(server.ext.orm); - break; - default: - logger.error("Unexpected task name %s", command); - process.exit(1); + case "fetchJobs": + await fetchJobs(em, logger, server.ext); + break; + + case "createPriceItems": + await createPriceItems(em, logger); + break; + + case "migrationUp": + await migrationUp(server.ext.orm); + break; + default: + logger.error("Unexpected task name %s", command); + process.exit(1); } process.exit(0); @@ -53,4 +53,4 @@ async function main() { await server.start(); } -main(); +void main(); diff --git a/apps/mis-server/src/migrations/Migration20220124035021.ts b/apps/mis-server/src/migrations/Migration20220124035021.ts index 687ec40412..a80f84d7f7 100644 --- a/apps/mis-server/src/migrations/Migration20220124035021.ts +++ b/apps/mis-server/src/migrations/Migration20220124035021.ts @@ -1,3 +1,17 @@ +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + import { Migration } from "@mikro-orm/migrations"; export class Migration20220124035021 extends Migration { diff --git a/apps/mis-server/src/migrations/Migration20220209032454.ts b/apps/mis-server/src/migrations/Migration20220209032454.ts index 38c6fa25be..5ed01d22b6 100644 --- a/apps/mis-server/src/migrations/Migration20220209032454.ts +++ b/apps/mis-server/src/migrations/Migration20220209032454.ts @@ -1,3 +1,17 @@ +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + import { Migration } from "@mikro-orm/migrations"; export class Migration20220209032454 extends Migration { diff --git a/apps/mis-server/src/migrations/Migration20220505073454.ts b/apps/mis-server/src/migrations/Migration20220505073454.ts index e536ff04fc..cc00bb4707 100644 --- a/apps/mis-server/src/migrations/Migration20220505073454.ts +++ b/apps/mis-server/src/migrations/Migration20220505073454.ts @@ -1,3 +1,17 @@ +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + import { Migration } from "@mikro-orm/migrations"; export class Migration20220505073454 extends Migration { diff --git a/apps/mis-server/src/migrations/Migration20220624145706.ts b/apps/mis-server/src/migrations/Migration20220624145706.ts index 128c3635fc..11aca11921 100644 --- a/apps/mis-server/src/migrations/Migration20220624145706.ts +++ b/apps/mis-server/src/migrations/Migration20220624145706.ts @@ -1,3 +1,17 @@ +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + import { Migration } from "@mikro-orm/migrations"; export class Migration20220624145706 extends Migration { diff --git a/apps/mis-server/src/migrations/Migration20230108135024.ts b/apps/mis-server/src/migrations/Migration20230108135024.ts index 5628d968cc..c07ca91235 100644 --- a/apps/mis-server/src/migrations/Migration20230108135024.ts +++ b/apps/mis-server/src/migrations/Migration20230108135024.ts @@ -1,13 +1,28 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + + +import { Migration } from "@mikro-orm/migrations"; export class Migration20230108135024 extends Migration { async up(): Promise { - this.addSql('alter table `job_info` modify `account` varchar(255) not null comment \'账户\', modify `user` varchar(127) not null comment \'用户名\';'); + this.addSql("alter table `job_info` modify `account` varchar(255) not null comment '账户', modify `user` varchar(127) not null comment '用户名';"); } async down(): Promise { - this.addSql('alter table `job_info` modify `account` varchar(20) not null comment \'账户\', modify `user` varchar(20) not null comment \'用户名\';'); + this.addSql("alter table `job_info` modify `account` varchar(20) not null comment '账户', modify `user` varchar(20) not null comment '用户名';"); } } diff --git a/apps/mis-server/src/migrations/Migration20230116122732.ts b/apps/mis-server/src/migrations/Migration20230116122732.ts index 11cf09d107..1d984a6d80 100644 --- a/apps/mis-server/src/migrations/Migration20230116122732.ts +++ b/apps/mis-server/src/migrations/Migration20230116122732.ts @@ -1,13 +1,25 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20230116122732 extends Migration { async up(): Promise { - this.addSql('alter table `tenant` add `create_time` DATETIME(6) not null default current_timestamp(6);'); + this.addSql("alter table `tenant` add `create_time` DATETIME(6) not null default current_timestamp(6);"); } async down(): Promise { - this.addSql('alter table `tenant` drop `create_time`;'); + this.addSql("alter table `tenant` drop `create_time`;"); } } diff --git a/apps/mis-server/src/migrations/Migration20231026095224.ts b/apps/mis-server/src/migrations/Migration20231026095224.ts index 3c6b697eb0..51bfd1621d 100644 --- a/apps/mis-server/src/migrations/Migration20231026095224.ts +++ b/apps/mis-server/src/migrations/Migration20231026095224.ts @@ -1,13 +1,25 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20231026095224 extends Migration { async up(): Promise { - this.addSql('alter table `charge_record` add index `query_info`(`time`, `tenant_name`, `account_name`, `type`);'); + this.addSql("alter table `charge_record` add index `query_info`(`time`, `tenant_name`, `account_name`, `type`);"); } async down(): Promise { - this.addSql('alter table `charge_record` drop index `query_info`;'); + this.addSql("alter table `charge_record` drop index `query_info`;"); } } diff --git a/apps/mis-server/src/migrations/Migration20231108022748.ts b/apps/mis-server/src/migrations/Migration20231108022748.ts index 3a7a428e6f..abc746a3e1 100644 --- a/apps/mis-server/src/migrations/Migration20231108022748.ts +++ b/apps/mis-server/src/migrations/Migration20231108022748.ts @@ -1,25 +1,39 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20231108022748 extends Migration { async up(): Promise { - this.addSql('create table `query_cache` (`id` int unsigned not null auto_increment primary key, `query_key` varchar(255) not null, `query_result` json not null, `timestamp` DATETIME(6) not null) default character set utf8mb4 engine = InnoDB;'); + this.addSql("create table `query_cache` (`id` int unsigned not null auto_increment primary key, `query_key` varchar(255) not null, `query_result` json not null, `timestamp` DATETIME(6) not null) default character set utf8mb4 engine = InnoDB;"); - this.addSql('alter table `charge_record` add index `static_info`(`time`, `account_name`, `amount`);'); + this.addSql("alter table `charge_record` add index `static_info`(`time`, `account_name`, `amount`);"); - this.addSql('alter table `pay_record` add index `static_info`(`time`, `account_name`, `amount`);'); + this.addSql("alter table `pay_record` add index `static_info`(`time`, `account_name`, `amount`);"); - this.addSql('alter table `account` add `create_time` DATETIME(6) null;'); + this.addSql("alter table `account` add `create_time` DATETIME(6) null;"); } async down(): Promise { - this.addSql('drop table if exists `query_cache`;'); + this.addSql("drop table if exists `query_cache`;"); - this.addSql('alter table `charge_record` drop index `static_info`;'); + this.addSql("alter table `charge_record` drop index `static_info`;"); - this.addSql('alter table `pay_record` drop index `static_info`;'); + this.addSql("alter table `pay_record` drop index `static_info`;"); - this.addSql('alter table `account` drop `create_time`;'); + this.addSql("alter table `account` drop `create_time`;"); } } diff --git a/apps/mis-server/src/migrations/Migration20240118021127.ts b/apps/mis-server/src/migrations/Migration20240118021127.ts index 0d513f3837..3e11d41094 100644 --- a/apps/mis-server/src/migrations/Migration20240118021127.ts +++ b/apps/mis-server/src/migrations/Migration20240118021127.ts @@ -1,32 +1,44 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240118021127 extends Migration { async up(): Promise { - this.addSql('alter table `charge_record` add `user_id` varchar(255) null, add `metadata` json null;'); - this.addSql('alter table `charge_record` add index `time`(`time`);'); - this.addSql('alter table `charge_record` add index `charge_record_tenant_name_index`(`tenant_name`);'); - this.addSql('alter table `charge_record` add index `charge_record_account_name_index`(`account_name`);'); - this.addSql('alter table `charge_record` add index `charge_record_user_id_index`(`user_id`);'); - this.addSql('alter table `charge_record` add index `charge_record_type_index`(`type`);'); + this.addSql("alter table `charge_record` add `user_id` varchar(255) null, add `metadata` json null;"); + this.addSql("alter table `charge_record` add index `time`(`time`);"); + this.addSql("alter table `charge_record` add index `charge_record_tenant_name_index`(`tenant_name`);"); + this.addSql("alter table `charge_record` add index `charge_record_account_name_index`(`account_name`);"); + this.addSql("alter table `charge_record` add index `charge_record_user_id_index`(`user_id`);"); + this.addSql("alter table `charge_record` add index `charge_record_type_index`(`type`);"); - this.addSql('alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(6);'); + this.addSql("alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(6);"); - this.addSql('alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(6);'); + this.addSql("alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(6);"); } async down(): Promise { - this.addSql('alter table `charge_record` drop index `time`;'); - this.addSql('alter table `charge_record` drop index `charge_record_tenant_name_index`;'); - this.addSql('alter table `charge_record` drop index `charge_record_account_name_index`;'); - this.addSql('alter table `charge_record` drop index `charge_record_user_id_index`;'); - this.addSql('alter table `charge_record` drop index `charge_record_type_index`;'); - this.addSql('alter table `charge_record` drop `user_id`;'); - this.addSql('alter table `charge_record` drop `metadata`;'); + this.addSql("alter table `charge_record` drop index `time`;"); + this.addSql("alter table `charge_record` drop index `charge_record_tenant_name_index`;"); + this.addSql("alter table `charge_record` drop index `charge_record_account_name_index`;"); + this.addSql("alter table `charge_record` drop index `charge_record_user_id_index`;"); + this.addSql("alter table `charge_record` drop index `charge_record_type_index`;"); + this.addSql("alter table `charge_record` drop `user_id`;"); + this.addSql("alter table `charge_record` drop `metadata`;"); - this.addSql('alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(0);'); + this.addSql("alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(0);"); - this.addSql('alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(0);'); + this.addSql("alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(0);"); } } diff --git a/apps/mis-server/src/migrations/Migration20240129075557.ts b/apps/mis-server/src/migrations/Migration20240129075557.ts index 08bdde7ec5..474eb5d712 100644 --- a/apps/mis-server/src/migrations/Migration20240129075557.ts +++ b/apps/mis-server/src/migrations/Migration20240129075557.ts @@ -1,17 +1,29 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240129075557 extends Migration { async up(): Promise { - this.addSql('alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(6);'); + this.addSql("alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(6);"); - this.addSql('alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(6);'); + this.addSql("alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(6);"); } async down(): Promise { - this.addSql('alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(0);'); + this.addSql("alter table `tenant` modify `create_time` DATETIME(6) not null default current_timestamp(0);"); - this.addSql('alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(0);'); + this.addSql("alter table `user` modify `create_time` DATETIME(6) not null default current_timestamp(0);"); } } diff --git a/apps/mis-server/src/migrations/Migration20240204081801.ts b/apps/mis-server/src/migrations/Migration20240204081801.ts index 3051ab4b55..e887ae5717 100644 --- a/apps/mis-server/src/migrations/Migration20240204081801.ts +++ b/apps/mis-server/src/migrations/Migration20240204081801.ts @@ -1,17 +1,29 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240204081801 extends Migration { async up(): Promise { - this.addSql('alter table `tenant` add `default_account_block_threshold` DECIMAL(19,4) not null default 0.0000;'); + this.addSql("alter table `tenant` add `default_account_block_threshold` DECIMAL(19,4) not null default 0.0000;"); - this.addSql('alter table `account` add `block_threshold_amount` DECIMAL(19,4) null;'); + this.addSql("alter table `account` add `block_threshold_amount` DECIMAL(19,4) null;"); } async down(): Promise { - this.addSql('alter table `tenant` drop column `default_account_block_threshold`;'); + this.addSql("alter table `tenant` drop column `default_account_block_threshold`;"); - this.addSql('alter table `account` drop column `block_threshold_amount`;'); + this.addSql("alter table `account` drop column `block_threshold_amount`;"); } } diff --git a/apps/mis-server/src/migrations/Migration20240304064259.ts b/apps/mis-server/src/migrations/Migration20240304064259.ts index 87c494b511..541d1cf2f5 100644 --- a/apps/mis-server/src/migrations/Migration20240304064259.ts +++ b/apps/mis-server/src/migrations/Migration20240304064259.ts @@ -1,10 +1,23 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ +import { Migration } from "@mikro-orm/migrations"; export class Migration20240304064259 extends Migration { async up(): Promise { - this.addSql('alter table `account` add `state` enum(\'FROZEN\', \'BLOCKED_BY_ADMIN\', \'NORMAL\') not null comment \'FROZEN, BLOCKED_BY_ADMIN, NORMAL\';'); - this.addSql('alter table `account` change `blocked` `blocked_in_cluster` tinyint(1) not null;'); + this.addSql("alter table `account` add `state` enum('FROZEN', 'BLOCKED_BY_ADMIN', 'NORMAL') not null comment 'FROZEN, BLOCKED_BY_ADMIN, NORMAL';"); + this.addSql("alter table `account` change `blocked` `blocked_in_cluster` tinyint(1) not null;"); this.addSql(` UPDATE account a @@ -19,9 +32,9 @@ export class Migration20240304064259 extends Migration { } async down(): Promise { - this.addSql('alter table `account` drop column `state`;'); + this.addSql("alter table `account` drop column `state`;"); - this.addSql('alter table `account` change `blocked_in_cluster` `blocked` tinyint(1) not null;'); + this.addSql("alter table `account` change `blocked_in_cluster` `blocked` tinyint(1) not null;"); } } diff --git a/apps/mis-server/src/migrations/Migration20240314075417.ts b/apps/mis-server/src/migrations/Migration20240314075417.ts index 9922469ead..a8317c983a 100644 --- a/apps/mis-server/src/migrations/Migration20240314075417.ts +++ b/apps/mis-server/src/migrations/Migration20240314075417.ts @@ -1,11 +1,25 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240314075417 extends Migration { async up(): Promise { - this.addSql('alter table `user_account` add `state` enum(\'NORMAL\', \'BLOCKED_BY_ADMIN\') not null default \'NORMAL\' comment \'NORMAL, BLOCKED_BY_ADMIN\';'); + this.addSql("alter table `user_account` add `state` enum('NORMAL', 'BLOCKED_BY_ADMIN') not null default 'NORMAL' comment 'NORMAL, BLOCKED_BY_ADMIN';"); - this.addSql('alter table `user_account` change `status` `blocked_in_cluster` varchar(10) not null comment \'UNBLOCKED, BLOCKED\';'); + this.addSql("alter table `user_account` change `status` `blocked_in_cluster` varchar(10) not null comment 'UNBLOCKED, BLOCKED';"); this.addSql(` UPDATE user_account ua @@ -21,9 +35,9 @@ export class Migration20240314075417 extends Migration { } async down(): Promise { - this.addSql('alter table `user_account` drop column `state`;'); + this.addSql("alter table `user_account` drop column `state`;"); - this.addSql('alter table `user_account` change `blocked_in_cluster` `status` varchar(10) not null comment \'UNBLOCKED, BLOCKED\';'); + this.addSql("alter table `user_account` change `blocked_in_cluster` `status` varchar(10) not null comment 'UNBLOCKED, BLOCKED';"); } } diff --git a/apps/mis-server/src/migrations/Migration20240328071020.ts b/apps/mis-server/src/migrations/Migration20240328071020.ts index 2c2c6ef782..01f1d77b46 100644 --- a/apps/mis-server/src/migrations/Migration20240328071020.ts +++ b/apps/mis-server/src/migrations/Migration20240328071020.ts @@ -1,13 +1,27 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240328071020 extends Migration { async up(): Promise { - this.addSql('alter table `account` modify `state` enum(\'NORMAL\', \'FROZEN\', \'BLOCKED_BY_ADMIN\') not null default \'NORMAL\' comment \'NORMAL, FROZEN, BLOCKED_BY_ADMIN\';'); + this.addSql("alter table `account` modify `state` enum('NORMAL', 'FROZEN', 'BLOCKED_BY_ADMIN') not null default 'NORMAL' comment 'NORMAL, FROZEN, BLOCKED_BY_ADMIN';"); } async down(): Promise { - this.addSql('alter table `account` modify `state` enum(\'FROZEN\', \'BLOCKED_BY_ADMIN\', \'NORMAL\') not null comment \'FROZEN, BLOCKED_BY_ADMIN, NORMAL\';'); + this.addSql("alter table `account` modify `state` enum('FROZEN', 'BLOCKED_BY_ADMIN', 'NORMAL') not null comment 'FROZEN, BLOCKED_BY_ADMIN, NORMAL';"); } } diff --git a/apps/mis-server/src/migrations/Migration20240507034022.ts b/apps/mis-server/src/migrations/Migration20240507034022.ts index 9e1cf4924b..f1dda27a35 100644 --- a/apps/mis-server/src/migrations/Migration20240507034022.ts +++ b/apps/mis-server/src/migrations/Migration20240507034022.ts @@ -1,14 +1,27 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/* eslint-disable @stylistic/max-len */ +import { Migration } from "@mikro-orm/migrations"; export class Migration20240507034022 extends Migration { async up(): Promise { - this.addSql('create table `cluster` (`id` int unsigned not null auto_increment primary key, `cluster_id` varchar(255) not null, `activation_status` enum(\'ACTIVATED\', \'DEACTIVATED\') not null default \'ACTIVATED\' comment \'ACTIVATED, DEACTIVATED\', `last_activation_operation` json null, `create_time` DATETIME(6) null, `update_time` DATETIME(6) null) default character set utf8mb4 engine = InnoDB;'); - this.addSql('alter table `cluster` add unique `cluster_cluster_id_unique`(`cluster_id`);'); + this.addSql("create table `cluster` (`id` int unsigned not null auto_increment primary key, `cluster_id` varchar(255) not null, `activation_status` enum('ACTIVATED', 'DEACTIVATED') not null default 'ACTIVATED' comment 'ACTIVATED, DEACTIVATED', `last_activation_operation` json null, `create_time` DATETIME(6) null, `update_time` DATETIME(6) null) default character set utf8mb4 engine = InnoDB;"); + this.addSql("alter table `cluster` add unique `cluster_cluster_id_unique`(`cluster_id`);"); } async down(): Promise { - this.addSql('drop table if exists `cluster`;'); + this.addSql("drop table if exists `cluster`;"); } } diff --git a/apps/mis-server/src/migrations/Migration20240515090037.ts b/apps/mis-server/src/migrations/Migration20240515090037.ts index d324fb505f..dc29b23ae0 100644 --- a/apps/mis-server/src/migrations/Migration20240515090037.ts +++ b/apps/mis-server/src/migrations/Migration20240515090037.ts @@ -1,13 +1,25 @@ -import { Migration } from '@mikro-orm/migrations'; +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Migration } from "@mikro-orm/migrations"; export class Migration20240515090037 extends Migration { async up(): Promise { - this.addSql('alter table `account_whitelist` add `expiration_time` datetime null;'); + this.addSql("alter table `account_whitelist` add `expiration_time` datetime null;"); } async down(): Promise { - this.addSql('alter table `account_whitelist` drop column `expiration_time`;'); + this.addSql("alter table `account_whitelist` drop column `expiration_time`;"); } } diff --git a/apps/mis-server/src/plugins/cachePlugin.ts b/apps/mis-server/src/plugins/cachePlugin.ts index 38f2a4aadf..1b4821f8bb 100644 --- a/apps/mis-server/src/plugins/cachePlugin.ts +++ b/apps/mis-server/src/plugins/cachePlugin.ts @@ -60,7 +60,7 @@ export const clearCachePlugin = plugin(async (f) => { const task = cron.schedule( schedule, - trigger, + () => { void trigger(); }, { timezone: "Asia/Shanghai", scheduled: true, @@ -74,7 +74,7 @@ export const clearCachePlugin = plugin(async (f) => { logger.info("Cache clear scheduled task stopped."); }); - f.addExtension("cache", { + f.addExtension("cache", ({ start: () => { if (cacheClearStarted) { logger.info("Cache clear task is requested to start but already started"); @@ -95,5 +95,5 @@ export const clearCachePlugin = plugin(async (f) => { }, schedule, clear: trigger, - }); + } as ClearCachePlugin["cache"])); }); diff --git a/apps/mis-server/src/plugins/clusters.ts b/apps/mis-server/src/plugins/clusters.ts index 61bf3c2030..f54c73fca8 100644 --- a/apps/mis-server/src/plugins/clusters.ts +++ b/apps/mis-server/src/plugins/clusters.ts @@ -24,7 +24,7 @@ import { rootKeyPair } from "src/config/env"; type CallOnAllResult = { cluster: string; result: T -}[] +}[]; // Throw ServiceError if failed. type CallOnAll = ( @@ -40,12 +40,12 @@ type CallOnOne = ( call: (client: SchedulerAdapterClient) => Promise, ) => Promise; -export type ClusterPlugin = { +export interface ClusterPlugin { clusters: { callOnAll: CallOnAll; callOnOne: CallOnOne; } -} +}; export const CLUSTEROPS_ERROR_CODE = "CLUSTEROPS_ERROR"; export const ADAPTER_CALL_ON_ONE_ERROR = "ADAPTER_CALL_ON_ONE_ERROR"; @@ -98,7 +98,7 @@ export const clustersPlugin = plugin(async (f) => { const clustersPlugin = { - callOnOne: (async (cluster, logger, call) => { + callOnOne: (async (cluster, logger, call) => { const client = getAdapterClient(cluster); @@ -113,7 +113,7 @@ export const clustersPlugin = plugin(async (f) => { const errorDetail = e instanceof Error ? e : JSON.stringify(e); - const reason = "Cluster ID : " + cluster + ", Details : " + errorDetail; + const reason = "Cluster ID : " + cluster + ", Details : " + errorDetail.toString(); const clusterErrorDetails = [{ clusterId: cluster, details: errorDetail, @@ -133,10 +133,10 @@ export const clustersPlugin = plugin(async (f) => { } }); - }), + }) as CallOnOne, // throws error if failed. - callOnAll: (async (clusters, logger, call) => { + callOnAll: (async (clusters, logger, call) => { const adapterClientForActivatedClusters = getAdapterClientForActivatedClusters(clusters); @@ -151,8 +151,8 @@ export const clustersPlugin = plugin(async (f) => { }); })); - type SuccessResponse = { cluster: string; success: boolean; result: T; }; - type ErrorResponse = { cluster: string; success: boolean; error: any; }; + interface SuccessResponse { cluster: string; success: boolean; result: T; } + interface ErrorResponse { cluster: string; success: boolean; error: any; } function isSuccessResponse(response: SuccessResponse | ErrorResponse): response is SuccessResponse { return response.success === true; @@ -183,7 +183,7 @@ export const clustersPlugin = plugin(async (f) => { return results; - }), + }) as CallOnAll, }; f.addExtension("clusters", clustersPlugin); diff --git a/apps/mis-server/src/plugins/fetch.ts b/apps/mis-server/src/plugins/fetch.ts index bad2c6507c..a4507ce450 100644 --- a/apps/mis-server/src/plugins/fetch.ts +++ b/apps/mis-server/src/plugins/fetch.ts @@ -42,7 +42,7 @@ export const fetchPlugin = plugin(async (f) => { const task = cron.schedule( misConfig.fetchJobs.periodicFetch.cron, - trigger, + () => { void trigger(); }, { timezone: "Asia/Shanghai", scheduled: misConfig.fetchJobs.periodicFetch.enabled, @@ -56,7 +56,7 @@ export const fetchPlugin = plugin(async (f) => { logger.info("Fetch info stopped."); }); - f.addExtension("fetch", { + f.addExtension("fetch", ({ started: () => fetchStarted, start: () => { if (fetchStarted) { @@ -79,5 +79,5 @@ export const fetchPlugin = plugin(async (f) => { schedule: misConfig.fetchJobs.periodicFetch.cron, lastFetched: () => lastFetched, fetch: trigger, - }); + } as FetchPlugin["fetch"])); }); diff --git a/apps/mis-server/src/plugins/price.ts b/apps/mis-server/src/plugins/price.ts index 761c986ddb..be6a9e628e 100644 --- a/apps/mis-server/src/plugins/price.ts +++ b/apps/mis-server/src/plugins/price.ts @@ -34,6 +34,6 @@ export const pricePlugin = plugin(async (s) => { logger.info("Platform price items are complete. "); } - s.addExtension("price", {}); + s.addExtension("price", ({} as PricePlugin["price"])); }); diff --git a/apps/mis-server/src/plugins/syncBlockStatus.ts b/apps/mis-server/src/plugins/syncBlockStatus.ts index 2097a9d6e4..7680c20294 100644 --- a/apps/mis-server/src/plugins/syncBlockStatus.ts +++ b/apps/mis-server/src/plugins/syncBlockStatus.ts @@ -45,7 +45,7 @@ export const syncBlockStatusPlugin = plugin(async (f) => { const task = cron.schedule( synchronizeCron, - trigger, + () => { void trigger(); }, { timezone: "Asia/Shanghai", scheduled: misConfig.periodicSyncUserAccountBlockStatus?.enabled, @@ -59,7 +59,7 @@ export const syncBlockStatusPlugin = plugin(async (f) => { logger.info("Sync block status stopped."); }); - f.addExtension("syncBlockStatus", { + f.addExtension("syncBlockStatus", ({ started: () => synchronizeStarted, start: () => { if (synchronizeStarted) { @@ -82,5 +82,5 @@ export const syncBlockStatusPlugin = plugin(async (f) => { schedule: synchronizeCron, lastSyncTime: () => lastSyncTime, sync: trigger, - }); + } as SyncBlockStatusPlugin["syncBlockStatus"])); }); diff --git a/apps/mis-server/src/services/account.ts b/apps/mis-server/src/services/account.ts index 0424b8c917..7fd5408267 100644 --- a/apps/mis-server/src/services/account.ts +++ b/apps/mis-server/src/services/account.ts @@ -43,9 +43,9 @@ export const accountServiceServer = plugin((server) => { }, { lockMode: LockMode.PESSIMISTIC_WRITE, populate: ["tenant"]}); if (!account) { - throw { + throw { code: Status.NOT_FOUND, message: `Account ${accountName} is not found`, - }; + } as ServiceError; } const currentActivatedClusters = await getActivatedClusters(em, logger); @@ -65,10 +65,10 @@ export const accountServiceServer = plugin((server) => { ); if (jobs.filter((i) => i.result.jobs.length > 0).length > 0) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `Account ${accountName} has jobs running and cannot be blocked. `, - }; + } as ServiceError; } const blockThresholdAmount = @@ -82,26 +82,26 @@ export const accountServiceServer = plugin((server) => { // 如果账户已被手动冻结,提示账户已被冻结 if (account.state === AccountState.FROZEN) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `Account ${accountName} has been frozen. `, - }; + } as ServiceError; } // 如果是未欠费(余额大于封锁阈值)账户,提示账户已被封锁 if (account.balance.gt(blockThresholdAmount)) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `Account ${accountName} has been blocked. `, - }; + } as ServiceError; } } if (result === "Whitelisted") { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `The account ${accountName} has been added to the whitelist. `, - }; + } as ServiceError; } // 更改数据库中状态值 @@ -120,15 +120,15 @@ export const accountServiceServer = plugin((server) => { }, { lockMode: LockMode.PESSIMISTIC_WRITE, populate: [ "tenant"]}); if (!account) { - throw { + throw { code: Status.NOT_FOUND, message: `Account ${accountName} is not found`, - }; + } as ServiceError; } if (!account.blockedInCluster) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `Account ${accountName} is unblocked`, - }; + } as ServiceError; } // 将账户从被上级封锁或冻结状态变更为正常 account.state = AccountState.NORMAL; @@ -176,9 +176,9 @@ export const accountServiceServer = plugin((server) => { const owner = x.users.getItems().find((x) => x.role === EntityUserRole.OWNER); if (!owner) { - throw { + throw { code: Status.INTERNAL, message: `Account ${x.accountName} does not have an owner`, - }; + } as ServiceError; } const ownerUser = owner.user.getEntity(); @@ -193,7 +193,7 @@ export const accountServiceServer = plugin((server) => { blocked: Boolean(x.blockedInCluster), state: account_AccountStateFromJSON(x.state), displayedState: displayedAccountState, - isInWhitelist: Boolean(!!x.whitelist?.id), + isInWhitelist: Boolean(x.whitelist?.id), ownerId: ownerUser.userId, ownerName: ownerUser.name, comment: x.comment, @@ -212,16 +212,16 @@ export const accountServiceServer = plugin((server) => { const user = await em.findOne(User, { userId: ownerId, tenant: { name: tenantName } }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${user} under tenant ${tenantName} does not exist`, - }; + } as ServiceError; } const tenant = await em.findOne(Tenant, { name: tenantName }); if (!tenant) { - throw { + throw { code: Status.NOT_FOUND, message: `Tenant ${tenantName} is not found`, - }; + } as ServiceError; } // 新建账户时比较租户默认封锁阈值,如果租户默认封锁阈值小于0则保持账户为在集群中可用状态 @@ -239,9 +239,9 @@ export const accountServiceServer = plugin((server) => { await em.persistAndFlush([account, userAccount]); } catch (e) { if (e instanceof UniqueConstraintViolationException) { - throw { + throw { code: Status.ALREADY_EXISTS, message: `Account ${accountName} already exists.`, - }; + } as ServiceError; } } @@ -267,9 +267,9 @@ export const accountServiceServer = plugin((server) => { accountName, }).catch((e) => { if (e.code === Status.NOT_FOUND) { - throw { + throw { code: Status.INTERNAL, message: `Account ${accountName} hasn't been created. Block failed`, - }; + } as ServiceError; } else { throw e; } @@ -280,9 +280,9 @@ export const accountServiceServer = plugin((server) => { accountName, }).catch((e) => { if (e.code === Status.NOT_FOUND) { - throw { + throw { code: Status.INTERNAL, message: `Account ${accountName} hasn't been created. Unblock failed`, - }; + } as ServiceError; } else { throw e; } @@ -363,9 +363,9 @@ export const accountServiceServer = plugin((server) => { { populate: [ "tenant"]}); if (!account) { - throw { + throw { code: Status.NOT_FOUND, message: `Account ${accountName} is not found`, - }; + } as ServiceError; } if (account.whitelist) { @@ -413,9 +413,9 @@ export const accountServiceServer = plugin((server) => { const account = await em.findOne(Account, { accountName, tenant: { name: tenantName } }, { populate: ["tenant"]}); if (!account) { - throw { + throw { code: Status.NOT_FOUND, message: `Account ${accountName} is not found`, - }; + } as ServiceError; } if (!account.whitelist) { return [{ executed: false }]; @@ -458,9 +458,9 @@ export const accountServiceServer = plugin((server) => { }); if (!account) { - throw { + throw { code: Status.NOT_FOUND, message: `Account ${accountName} is not found`, - }; + } as ServiceError; } account.blockThresholdAmount = blockThresholdAmount ? new Decimal(moneyToNumber(blockThresholdAmount)) diff --git a/apps/mis-server/src/services/admin.ts b/apps/mis-server/src/services/admin.ts index 7401747cf3..10863ae754 100644 --- a/apps/mis-server/src/services/admin.ts +++ b/apps/mis-server/src/services/admin.ts @@ -32,7 +32,7 @@ import { UserAccount, UserRole } from "src/entities/UserAccount"; export const adminServiceServer = plugin((server) => { server.addService(AdminServiceService, { - changeStorageQuota: async ({ }) => { + changeStorageQuota: async () => { // const { cluster, mode, userId, value } = request; // const quota = await em.findOne(StorageQuota, { @@ -80,9 +80,9 @@ export const adminServiceServer = plugin((server) => { }); if (!quota) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} or cluster ${cluster} is not found`, - }; + } as ServiceError; } return [{ currentQuota: quota.storageQuota }]; @@ -92,17 +92,17 @@ export const adminServiceServer = plugin((server) => { const { data, whitelist } = request; if (!data) { - throw { + throw { code: Status.INVALID_ARGUMENT, message: "Submitted data is empty", - }; + } as ServiceError; } const ownerNotInAccount = data.accounts.find((x) => x.owner && !x.users.find((user) => user.userId === x.owner)); if (ownerNotInAccount) { - throw { + throw { code: Status.INVALID_ARGUMENT, message: `Owner ${ownerNotInAccount.owner} is not in ${ownerNotInAccount.accountName}`, - }; + } as ServiceError; } const currentActivatedClusters = await getActivatedClusters(em, logger); diff --git a/apps/mis-server/src/services/charging.ts b/apps/mis-server/src/services/charging.ts index 71af15a5d2..8a178920e6 100644 --- a/apps/mis-server/src/services/charging.ts +++ b/apps/mis-server/src/services/charging.ts @@ -50,13 +50,13 @@ export const chargingServiceServer = plugin((server) => { if (!entity) { if (accountName === undefined) { - throw { + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found`, - }; + } as ServiceError; } else { - throw { + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} or account ${accountName} is not found`, - }; + } as ServiceError; } } @@ -81,13 +81,13 @@ export const chargingServiceServer = plugin((server) => { if (!target) { if (accountName === undefined) { - throw { + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found`, - }; + } as ServiceError; } else { - throw { + throw { code: status.NOT_FOUND, message: `Account ${accountName} or tenant ${tenantName} is not found`, - }; + } as ServiceError; } } @@ -128,13 +128,13 @@ export const chargingServiceServer = plugin((server) => { if (!target) { if (accountName === undefined) { - throw { + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found`, - }; + } as ServiceError; } else { - throw { + throw { code: status.NOT_FOUND, message: `Account ${accountName} or tenant ${tenantName} is not found`, - }; + } as ServiceError; } } @@ -221,27 +221,27 @@ export const chargingServiceServer = plugin((server) => { switch (target?.$case) { // 当前租户的租户消费记录 - case "tenant": - searchParam = { tenantName: target[target.$case].tenantName, accountName: undefined }; - break; + case "tenant": + searchParam = { tenantName: target[target.$case].tenantName, accountName: undefined }; + break; // 所有租户的租户消费记录 - case "allTenants": - searchParam = { accountName: undefined }; - break; + case "allTenants": + searchParam = { accountName: undefined }; + break; // 当前租户下当前账户的消费记录 - case "accountOfTenant": - searchParam = { tenantName: target[target.$case].tenantName, accountName: target[target.$case].accountName }; - break; + case "accountOfTenant": + searchParam = { tenantName: target[target.$case].tenantName, accountName: target[target.$case].accountName }; + break; // 当前租户下所有账户的消费记录 - case "accountsOfTenant": - searchParam = { tenantName: target[target.$case].tenantName, accountName: { $ne:null } }; - break; + case "accountsOfTenant": + searchParam = { tenantName: target[target.$case].tenantName, accountName: { $ne:null } }; + break; // 所有租户下所有账户的消费记录 - case "accountsOfAllTenants": - searchParam = { accountName: { $ne:null } }; - break; - default: - searchParam = {}; + case "accountsOfAllTenants": + searchParam = { accountName: { $ne:null } }; + break; + default: + searchParam = {}; } // 可查询的types类型 @@ -290,7 +290,7 @@ export const chargingServiceServer = plugin((server) => { const knex = em.getKnex(); // 查询消费记录 - const results: {account_name: string, user_name: string, chargedAmount: number}[] = + const results: { account_name: string, user_name: string, chargedAmount: number }[] = // 从pay_record表中查询 await knex("charge_record as cr") // 选择account_name字段 @@ -328,7 +328,7 @@ export const chargingServiceServer = plugin((server) => { const qb = em.createQueryBuilder(ChargeRecord, "cr"); - qb + void qb .select([ raw("DATE(CONVERT_TZ(cr.time, 'UTC', ?)) as date", [timeZone]), raw("SUM(cr.amount) as totalAmount"), @@ -339,7 +339,7 @@ export const chargingServiceServer = plugin((server) => { .groupBy(raw("date")) .orderBy({ [raw("date")]: QueryOrder.DESC }); - const records: {date: string, totalAmount: number}[] = await queryWithCache({ + const records: { date: string, totalAmount: number }[] = await queryWithCache({ em, queryKeys: ["get_daily_charge", `${startTime}`, `${endTime}`, `${timeZone}`], queryQb: qb, @@ -362,7 +362,7 @@ export const chargingServiceServer = plugin((server) => { const knex = em.getKnex(); // 查询支付记录 - const results: {account_name: string, user_name: string, totalAmount: number}[] = + const results: { account_name: string, user_name: string, totalAmount: number }[] = // 从pay_record表中查询 await knex("pay_record as pr") // 选择account_name字段 @@ -405,7 +405,7 @@ export const chargingServiceServer = plugin((server) => { const qb = em.createQueryBuilder(PayRecord, "pr"); - qb + void qb .select([ raw("DATE(CONVERT_TZ(pr.time, 'UTC', ?)) as date", [timeZone]), raw("SUM(pr.amount) as totalAmount"), @@ -416,7 +416,7 @@ export const chargingServiceServer = plugin((server) => { .groupBy(raw("date")) .orderBy({ [raw("date")]: QueryOrder.DESC }); - const records: {date: string, totalAmount: number}[] = await queryWithCache({ + const records: { date: string, totalAmount: number }[] = await queryWithCache({ em, queryKeys: ["get_daily_pay", `${startTime}`, `${endTime}`, `${timeZone}`], queryQb: qb, @@ -458,28 +458,29 @@ export const chargingServiceServer = plugin((server) => { // 排序 if (sortBy !== undefined && sortOrder !== undefined) { const order = SortOrder[sortOrder] == "DESCEND" ? "desc" : "asc"; - qb.orderBy({ [mapChargesSortField[sortBy]]: order }); + void qb.orderBy({ [mapChargesSortField[sortBy]]: order }); } - let records; - - // 如果存在userIdsOrNames字段,则用knex - if (userIdsOrNames && userIdsOrNames.length > 0) { - const sql = qb.getKnexQuery().andWhere(function() { - this.whereIn("cr.user_id", function() { - this.select("user_id") - .from("user"); - for (const idOrName of userIdsOrNames) { - this.orWhereRaw("user_id like " + `'%${idOrName}%'`) - .orWhereRaw("name like " + `'%${idOrName}%'`); - } + const records = await (async () => { + + // 如果存在userIdsOrNames字段,则用knex + if (userIdsOrNames && userIdsOrNames.length > 0) { + const sql = qb.getKnexQuery().andWhere(function() { + void this.whereIn("cr.user_id", function() { + void this.select("user_id") + .from("user"); + for (const idOrName of userIdsOrNames) { + void this.orWhereRaw("user_id like " + `'%${idOrName}%'`) + .orWhereRaw("name like " + `'%${idOrName}%'`); + } + }); }); - }); - records = await em.getConnection().execute(sql); - } else { - records = await qb.getResult(); - } + return await em.getConnection().execute(sql); + } else { + return await qb.getResult(); + } + })(); return [{ results: records.map((x) => { @@ -530,11 +531,11 @@ export const chargingServiceServer = plugin((server) => { // 如果存在userIdsOrNames字段,则用knex if (userIdsOrNames && userIdsOrNames.length > 0) { const sql = qb.getKnexQuery().andWhere(function() { - this.whereIn("c.user_id", function() { - this.select("user_id") + void this.whereIn("c.user_id", function() { + void this.select("user_id") .from("user"); for (const idOrName of userIdsOrNames) { - this.orWhereRaw("user_id like " + `'%${idOrName}%'`) + void this.orWhereRaw("user_id like " + `'%${idOrName}%'`) .orWhereRaw("name like " + `'%${idOrName}%'`); } }); diff --git a/apps/mis-server/src/services/export.ts b/apps/mis-server/src/services/export.ts index d9b1b87454..41b256c648 100644 --- a/apps/mis-server/src/services/export.ts +++ b/apps/mis-server/src/services/export.ts @@ -124,17 +124,17 @@ export const exportServiceServer = plugin((server) => { writeTotal += 1; // 每两百条传一次 if (data.length === 200 || writeTotal === records.length) { - await new Promise(async (resolve) => { - await writeAsync({ users: data }); + await new Promise((resolve) => { + void writeAsync({ users: data }); // 清空暂存 data = []; resolve("done"); }).catch((e) => { - throw { + throw { code: status.INTERNAL, message: "Error when exporting file", details: e?.message, - }; + } as ServiceError; }); } } @@ -160,9 +160,9 @@ export const exportServiceServer = plugin((server) => { const owner = x.users.getItems().find((x) => x.role === UserRole.OWNER); if (!owner) { - throw { + throw { code: status.INTERNAL, message: `Account ${x.accountName} does not have an owner`, - }; + } as ServiceError; } const ownerUser = owner.user.getEntity(); @@ -199,30 +199,30 @@ export const exportServiceServer = plugin((server) => { .leftJoinAndSelect("a.tenant", "t"); if (tenantName !== undefined) { - qb.andWhere({ "t.name": tenantName }); + void qb.andWhere({ "t.name": tenantName }); } if (accountName !== undefined) { - qb.andWhere({ "a.accountName": { $like: `%${accountName}%` } }); + void qb.andWhere({ "a.accountName": { $like: `%${accountName}%` } }); } if (blocked) { - qb.andWhere({ "a.state": AccountState.BLOCKED_BY_ADMIN, "a.blockedInCluster": true }); + void qb.andWhere({ "a.state": AccountState.BLOCKED_BY_ADMIN, "a.blockedInCluster": true }); } if (debt) { - qb.andWhere({ "a.state": AccountState.NORMAL }) + void qb.andWhere({ "a.state": AccountState.NORMAL }) .andWhere("a.whitelist_id IS NULL") .andWhere("CASE WHEN a.block_threshold_amount IS NOT NULL" + " THEN a.balance <= a.block_threshold_amount ELSE a.balance <= t.default_account_block_threshold END"); } if (frozen) { - qb.andWhere({ "a.state": AccountState.FROZEN }); + void qb.andWhere({ "a.state": AccountState.FROZEN }); } if (normal) { - qb.andWhere({ "a.blockedInCluster": false }); + void qb.andWhere({ "a.blockedInCluster": false }); } while (offset < count) { @@ -248,17 +248,17 @@ export const exportServiceServer = plugin((server) => { writeTotal += 1; // 每两百条传一次 if (data.length === 200 || writeTotal === records.length) { - await new Promise(async (resolve) => { - await writeAsync({ accounts: data }); + await new Promise((resolve) => { + void writeAsync({ accounts: data }); // 清空暂存 data = []; resolve("done"); }).catch((e) => { - throw { + throw { code: status.INTERNAL, message: "Error when exporting file", details: e?.message, - }; + } as ServiceError; }); } } @@ -323,17 +323,17 @@ export const exportServiceServer = plugin((server) => { writeTotal += 1; // 每两百条传一次 if (data.length === 200 || writeTotal === records.length) { - await new Promise(async (resolve) => { - await writeAsync({ chargeRecords: data }); + await new Promise((resolve) => { + void writeAsync({ chargeRecords: data }); // 清空暂存 data = []; resolve("done"); }).catch((e) => { - throw { + throw { code: status.INTERNAL, message: "Error when exporting file", details: e?.message, - }; + } as ServiceError; }); } } @@ -353,7 +353,7 @@ export const exportServiceServer = plugin((server) => { const searchParam = getPaymentsTargetSearchParam(target); const searchTypes = getPaymentsSearchType(types); const query: { time: { $gte: string; $lte: string }; [key: string]: any } = { - time: { $gte: startTime as string, $lte: endTime as string }, + time: { $gte: startTime!, $lte: endTime! }, ...searchParam, ...searchTypes, }; @@ -394,17 +394,17 @@ export const exportServiceServer = plugin((server) => { data.push(row); writeTotal += 1; if (data.length === 200 || writeTotal === records.length) { - await new Promise(async (resolve) => { - await writeAsync({ payRecords: data }); + await new Promise((resolve) => { + void writeAsync({ payRecords: data }); // 清空暂存 data = []; resolve("done"); }).catch((e) => { - throw { + throw { code: status.INTERNAL, message: "Error when exporting file", details: e?.message, - }; + } as ServiceError; }); } } diff --git a/apps/mis-server/src/services/init.ts b/apps/mis-server/src/services/init.ts index 7ede1b7e19..72c6bb86f8 100644 --- a/apps/mis-server/src/services/init.ts +++ b/apps/mis-server/src/services/init.ts @@ -51,15 +51,15 @@ export const initServiceServer = plugin((server) => { await createUserInDatabase(userId, name, email, DEFAULT_TENANT_NAME, server.logger, em) .catch((e) => { if (e.code === Status.ALREADY_EXISTS) { - throw { + throw { code: Status.ALREADY_EXISTS, message:`User with userId ${userId} already exists in scow.`, details: "EXISTS_IN_SCOW", - }; + } as ServiceError; } - throw { + throw { code: Status.INTERNAL, - message: `Error creating user with userId ${userId} in database.` }; + message: `Error creating user with userId ${userId} in database.` } as ServiceError; }); user.platformRoles.push(PlatformRole.PLATFORM_ADMIN); @@ -83,9 +83,9 @@ export const initServiceServer = plugin((server) => { return false; } // 回滚数据库 - await em.removeAndFlush(user), + await em.removeAndFlush(user); server.logger.error("Error creating user in auth.", e); - throw { code: Status.INTERNAL, message: `Error creating user ${user.id} in auth.` }; + throw { code: Status.INTERNAL, message: `Error creating user ${user.id} in auth.` } as ServiceError; }); return [{ createdInAuth: createdInAuth }]; @@ -99,10 +99,10 @@ export const initServiceServer = plugin((server) => { }); if (!user) { - throw { + throw { code: status.NOT_FOUND, message: `User ${request.userId} is not found in default tenant.`, - }; + } as ServiceError; } if (!user.platformRoles.includes(PlatformRole.PLATFORM_ADMIN)) { @@ -125,10 +125,10 @@ export const initServiceServer = plugin((server) => { }); if (!user) { - throw { + throw { code: status.NOT_FOUND, message: `User ${request.userId} is not found in default tenant.`, - }; + } as ServiceError; } user.platformRoles = user.platformRoles.filter((x) => x !== PlatformRole.PLATFORM_ADMIN); @@ -151,9 +151,9 @@ export const initServiceServer = plugin((server) => { await em.persistAndFlush(initializationTime); } catch (e) { if (e instanceof UniqueConstraintViolationException) { - throw { + throw { code: status.ALREADY_EXISTS, message: "already initialized", - }; + } as ServiceError; } else { throw e; } diff --git a/apps/mis-server/src/services/job.ts b/apps/mis-server/src/services/job.ts index ea1027e020..e3b38334f6 100644 --- a/apps/mis-server/src/services/job.ts +++ b/apps/mis-server/src/services/job.ts @@ -14,7 +14,7 @@ import { asyncClientCall } from "@ddadaal/tsgrpc-client"; import { ensureNotUndefined, plugin } from "@ddadaal/tsgrpc-server"; import { ServiceError, status } from "@grpc/grpc-js"; import { Status } from "@grpc/grpc-js/build/src/constants"; -import { FilterQuery, QueryOrder, raw, UniqueConstraintViolationException } from "@mikro-orm/core"; +import { FilterQuery, Loaded, QueryOrder, raw, UniqueConstraintViolationException } from "@mikro-orm/core"; import { Decimal, decimalToMoney, moneyToNumber } from "@scow/lib-decimal"; import { jobInfoToRunningjob } from "@scow/lib-scheduler-adapter"; import { checkTimeZone, convertToDateMessage } from "@scow/lib-server/build/date"; @@ -78,7 +78,7 @@ export const jobServiceServer = plugin((server) => { const sqlFilter = filterJobs(filter); logger.info("getJobs sqlFilter %s", JSON.stringify(sqlFilter)); - let jobs, count; + let jobs: Loaded[], count: number; // 处理排序参数 if (sortBy !== undefined && sortOrder !== undefined) { @@ -156,10 +156,10 @@ export const jobServiceServer = plugin((server) => { const account = accountMap[x.account]; if (!account) { - throw { + throw { code: status.INTERNAL, message: `Unknown account ${x.account} of job ${x.biJobIndex}`, - }; + } as ServiceError; } const comment = `Record id ${record.id}, job biJobIndex ${x.biJobIndex}`; @@ -226,9 +226,9 @@ export const jobServiceServer = plugin((server) => { const job = await em.findOne(JobInfoEntity, { biJobIndex: +biJobIndex }); if (!job) { - throw { + throw { code: Status.NOT_FOUND, message: `Job ${biJobIndex} is not found`, - }; + } as ServiceError; } return [{ info: toGrpc(job) }]; @@ -325,7 +325,7 @@ export const jobServiceServer = plugin((server) => { tenant = await em.findOne(Tenant, { name: tenantName }); if (!tenant) { - throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` }; + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` } as ServiceError; } } @@ -335,14 +335,14 @@ export const jobServiceServer = plugin((server) => { orderBy: { createTime: "ASC" }, }); logger.info("billingItems :%o", billingItems); - const priceItemToGrpc = (item: JobPriceItem) => ({ + const priceItemToGrpc = (item: JobPriceItem) => ({ id: item.itemId, path: item.path.join("."), tenantName: item.tenant?.getProperty("name"), price: decimalToMoney(item.price), createTime: item.createTime.toISOString(), amountStrategy: item.amount, - }); + } as JobBillingItem); const { defaultPrices, tenantSpecificPrices } = getActiveBillingItems(billingItems); @@ -376,16 +376,16 @@ export const jobServiceServer = plugin((server) => { tenant = await em.findOne(Tenant, { name: tenantName }) ?? undefined; if (!tenant) { - throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` }; + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` } as ServiceError; } } const customAmountStrategies = misConfig.customAmountStrategies?.map((i) => i.id) || []; if (![...(Object.values(AmountStrategy) as string[]), ...customAmountStrategies].includes(amountStrategy)) { - throw { + throw { code: status.INVALID_ARGUMENT, message: `Amount strategy ${amountStrategy} is not valid.`, - }; + } as ServiceError; } const item = new JobPriceItem({ @@ -402,7 +402,7 @@ export const jobServiceServer = plugin((server) => { return [{}]; } catch (e) { if (e instanceof UniqueConstraintViolationException) { - throw { code: status.ALREADY_EXISTS, message: `${itemId} already exists.` }; + throw { code: status.ALREADY_EXISTS, message: `${itemId} already exists.` } as ServiceError; } else { throw e; } @@ -414,7 +414,8 @@ export const jobServiceServer = plugin((server) => { const { startTime, endTime, topRank = 10 } = ensureNotUndefined(request, ["startTime", "endTime"]); const qb = em.createQueryBuilder(JobInfoEntity, "j"); - qb + + void qb .select([raw("j.user as userId"), raw("COUNT(*) as count")]) .where({ timeSubmit: { $gte: startTime } }) .andWhere({ timeSubmit: { $lte: endTime } }) @@ -422,7 +423,7 @@ export const jobServiceServer = plugin((server) => { .orderBy({ [raw("COUNT(*)")]: QueryOrder.DESC }) .limit(topRank); - const results: {userId: string, count: number}[] = await queryWithCache({ + const results: { userId: string, count: number }[] = await queryWithCache({ em, queryKeys: ["top_submit_job_users", `${startTime}`, `${endTime}`, `${topRank}`], queryQb: qb, @@ -441,12 +442,12 @@ export const jobServiceServer = plugin((server) => { // 控制topNUsers的数量 if (typeof topNUsers == "number" && (topNUsers > 10 || topNUsers < 0)) { - throw { code: status.INVALID_ARGUMENT, message:"topNUsers must be between 0 and 10" }; + throw { code: status.INVALID_ARGUMENT, message:"topNUsers must be between 0 and 10" } as ServiceError; } // 直接使用Knex查询构建器 const knex = em.getKnex(); - const results: {userName: string, userId: string, count: number}[] = await knex("job_info as j") + const results: { userName: string, userId: string, count: number }[] = await knex("job_info as j") .select([ "u.name as userName", "j.user as userId", @@ -474,7 +475,7 @@ export const jobServiceServer = plugin((server) => { checkTimeZone(timeZone); const qb = em.createQueryBuilder(JobInfoEntity, "j"); - qb + void qb .select([ raw("DATE(CONVERT_TZ(j.time_submit, 'UTC', ?)) as date", [timeZone]), raw("COUNT(*) as count")]) @@ -483,7 +484,7 @@ export const jobServiceServer = plugin((server) => { .groupBy(raw("date")) .orderBy({ [raw("date")]: QueryOrder.DESC }); - const results: {date: string, count: number}[] = await queryWithCache({ + const results: { date: string, count: number }[] = await queryWithCache({ em, queryKeys: ["new_job_count", `${startTime}`, `${endTime}`], queryQb: qb, diff --git a/apps/mis-server/src/services/jobChargeLimit.ts b/apps/mis-server/src/services/jobChargeLimit.ts index 0bb95a18eb..87348dcbd1 100644 --- a/apps/mis-server/src/services/jobChargeLimit.ts +++ b/apps/mis-server/src/services/jobChargeLimit.ts @@ -35,17 +35,17 @@ export const jobChargeLimitServer = plugin((server) => { }, { populate: ["user", "account"]}); if (!userAccount) { - throw { + throw { code: Status.NOT_FOUND, details: `User ${userId} is not found in account`, - }; + } as ServiceError; } if (!userAccount.jobChargeLimit) { - throw { + throw { code: Status.NOT_FOUND, details: `The user ${userId} in account ${accountName} has no limit`, - }; + } as ServiceError; } logger.info("Job Charge Limit %s/%s of user %s account %s has been canceled.", @@ -92,20 +92,20 @@ export const jobChargeLimitServer = plugin((server) => { const limitNumber = moneyToNumber(limit); // 如果设置的限额小于等于0或者小于当前已用额度则报错 if (limitNumber <= 0 || - (userAccount?.usedJobCharge && userAccount.usedJobCharge.isGreaterThan(limitNumber))) { - throw { + (userAccount?.usedJobCharge?.isGreaterThan(limitNumber))) { + throw { code: Status.INVALID_ARGUMENT, message: userAccount?.usedJobCharge ? `The set quota ${limitNumber} is invalid , - it must be greater than or equal to the used job charge ${userAccount?.usedJobCharge}` : + it must be greater than or equal to the used job charge ${userAccount?.usedJobCharge.toNumber()}` : `The set quota ${limitNumber} is invalid , it must be greater than 0`, - }; + } as ServiceError; } if (!userAccount) { - throw { + throw { code: Status.NOT_FOUND, details: `User ${userId} is not found in account.`, - }; + } as ServiceError; } const currentActivatedClusters = await getActivatedClusters(em, logger); diff --git a/apps/mis-server/src/services/misConfig.ts b/apps/mis-server/src/services/misConfig.ts index c6021d9208..c9bb764602 100644 --- a/apps/mis-server/src/services/misConfig.ts +++ b/apps/mis-server/src/services/misConfig.ts @@ -81,9 +81,9 @@ export const misConfigServiceServer = plugin((server) => { const cluster = await em.findOne(Cluster, { clusterId }); if (!cluster) { - throw { + throw { code: status.NOT_FOUND, message: `Cluster( Cluster ID: ${clusterId}) is not found`, - }; + } as ServiceError; } // check current scheduler adapter connection state @@ -94,10 +94,10 @@ export const misConfigServiceServer = plugin((server) => { async (client) => await asyncClientCall(client.config, "getClusterConfig", {}), ).catch((e) => { logger.info("Cluster Connection Error ( Cluster ID : %s , Details: %s ) .", cluster, e); - throw { + throw { code: status.FAILED_PRECONDITION, message: `Activate cluster failed, Cluster( Cluster ID: ${clusterId}) is currently unreachable.`, - }; + } as ServiceError; }); // when the cluster has already been activated @@ -113,7 +113,7 @@ export const misConfigServiceServer = plugin((server) => { // save operator userId in lastActivationOperation const lastActivationOperationMap: ClusterRuntimeInfo_LastActivationOperation = {}; - lastActivationOperationMap["operatorId"] = operatorId; + lastActivationOperationMap.operatorId = operatorId; cluster.lastActivationOperation = lastActivationOperationMap; await em.persistAndFlush(cluster); @@ -133,9 +133,9 @@ export const misConfigServiceServer = plugin((server) => { const cluster = await em.findOne(Cluster, { clusterId }); if (!cluster) { - throw { + throw { code: status.NOT_FOUND, message: `Cluster( Cluster ID: ${clusterId}) is not found`, - }; + } as ServiceError; } if (cluster.activationStatus === ClusterActivationStatus.DEACTIVATED) { @@ -149,10 +149,10 @@ export const misConfigServiceServer = plugin((server) => { // save operator userId and deactivation in lastActivationOperation const lastActivationOperationMap: ClusterRuntimeInfo_LastActivationOperation = {}; - lastActivationOperationMap["operatorId"] = operatorId; + lastActivationOperationMap.operatorId = operatorId; if (deactivationComment) { - lastActivationOperationMap["deactivationComment"] = deactivationComment; + lastActivationOperationMap.deactivationComment = deactivationComment; } cluster.lastActivationOperation = lastActivationOperationMap; diff --git a/apps/mis-server/src/services/tenant.ts b/apps/mis-server/src/services/tenant.ts index 661a185ba4..3e62552074 100644 --- a/apps/mis-server/src/services/tenant.ts +++ b/apps/mis-server/src/services/tenant.ts @@ -37,7 +37,7 @@ export const tenantServiceServer = plugin((server) => { const tenant = await em.findOne(Tenant, { name: tenantName }); if (!tenant) { - throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` }; + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` } as ServiceError; } const accountCount = await em.count(Account, { tenant }); const userCount = await em.count(User, { tenant }); @@ -104,9 +104,9 @@ export const tenantServiceServer = plugin((server) => { const tenant = await em.findOne(Tenant, { name: tenantName }); if (tenant) { - throw { + throw { code: Status.ALREADY_EXISTS, message: "The tenant already exists", details: "TENANT_ALREADY_EXISTS", - }; + } as ServiceError; } logger.info(`start to create tenant: ${tenantName} `); const newTenant = new Tenant({ name: tenantName }); @@ -115,11 +115,11 @@ export const tenantServiceServer = plugin((server) => { // 在数据库中创建租户 await em.persistAndFlush(newTenant).catch((e) => { if (e instanceof UniqueConstraintViolationException) { - throw { + throw { code: Status.ALREADY_EXISTS, message: "The tenant already exists", details: "TENANT_ALREADY_EXISTS", - }; + } as ServiceError; } - throw { code: Status.INTERNAL, message: "Error creating tenant in database." }; + throw { code: Status.INTERNAL, message: "Error creating tenant in database." } as ServiceError; }); // 在数据库中创建user @@ -131,16 +131,16 @@ export const tenantServiceServer = plugin((server) => { return user; }).catch((e) => { if (e.code === Status.ALREADY_EXISTS) { - throw { + throw { code: Status.ALREADY_EXISTS, message: `User with userId ${userId} already exists in scow.`, details: "USER_ALREADY_EXISTS", - }; + } as ServiceError; } - throw { + throw { code: Status.INTERNAL, message: `Error creating user with userId ${userId} in database.`, - }; + } as ServiceError; }); // call auth const createdInAuth = await createUser(authUrl, @@ -157,10 +157,10 @@ export const tenantServiceServer = plugin((server) => { return false; } else { logger.error("Error creating user in auth.", e); - throw { + throw { code: Status.INTERNAL, message: `Error creating user with userId ${userId} in auth.`, - }; + } as ServiceError; } }); await callHook("userCreated", { tenantName, userId: user.userId }, logger); @@ -175,7 +175,7 @@ export const tenantServiceServer = plugin((server) => { const tenant = await em.findOne(Tenant, { name: tenantName }); if (!tenant) { - throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` }; + throw { code: status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` } as ServiceError; } tenant.defaultAccountBlockThreshold = new Decimal(moneyToNumber(blockThresholdAmount)); @@ -229,9 +229,9 @@ export const tenantServiceServer = plugin((server) => { const tenant = await em.findOne(Tenant, { name: tenantName }); if (tenant) { - throw { + throw { code: Status.ALREADY_EXISTS, message: "The tenant already exists", details: "TENANT_ALREADY_EXISTS", - }; + } as ServiceError; } const newTenant = new Tenant({ name: tenantName }); @@ -240,17 +240,17 @@ export const tenantServiceServer = plugin((server) => { const user = await em.findOne(User, { userId, name: userName }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User with userId ${userId} and name ${userName} is not found.`, - }; + } as ServiceError; } const userAccount = await em.findOne(UserAccount, { user: user }); if (userAccount) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} still maintains account relationship.`, - }; + } as ServiceError; } // 修改该用户的租户, 并且作为租户管理员 diff --git a/apps/mis-server/src/services/user.ts b/apps/mis-server/src/services/user.ts index ba6835ec68..490113dea7 100644 --- a/apps/mis-server/src/services/user.ts +++ b/apps/mis-server/src/services/user.ts @@ -101,14 +101,14 @@ export const userServiceServer = plugin((server) => { }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId}, tenant ${tenantName} is not found`, - }; + } as ServiceError; } const tenant = await em.findOne(Tenant, { name: tenantName }); if (!tenant) { - throw { code:Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` }; + throw { code:Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` } as ServiceError; } return [{ @@ -133,7 +133,7 @@ export const userServiceServer = plugin((server) => { }]; }, - queryUsedStorageQuota: async ({}) => { + queryUsedStorageQuota: async () => { // const { cluster, userId } = request; // const reply = await server.ext.clusters.callOnOne( @@ -167,25 +167,25 @@ export const userServiceServer = plugin((server) => { }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} or tenant ${tenantName} is not found.`, details:"USER_OR_TENANT_NOT_FOUND", - }; + } as ServiceError; } if (!account) { - throw { + throw { code: Status.NOT_FOUND, message: `Account ${accountName} or tenant ${tenantName} is not found.`, details:"ACCOUNT_OR_TENANT_NOT_FOUND", - }; + } as ServiceError; } if (account.users.getItems().some((x) => x.user.getEntity().userId === userId)) { - throw { + throw { code: Status.ALREADY_EXISTS, message: `User ${userId} already in the account ${accountName}.`, - }; + } as ServiceError; } const currentActivatedClusters = await getActivatedClusters(em, logger); @@ -228,16 +228,16 @@ export const userServiceServer = plugin((server) => { }, { populate: ["user", "account"]}); if (!userAccount) { - throw { + throw { code: Status.NOT_FOUND, message:`User ${userId} or account ${accountName} is not found.`, - }; + } as ServiceError; } if (userAccount.role === UserRole.OWNER) { - throw { + throw { code: Status.OUT_OF_RANGE, message: `User ${userId} is the owner of the account ${accountName}。`, - }; + } as ServiceError; } const currentActivatedClusters = await getActivatedClusters(em, logger); @@ -263,11 +263,11 @@ export const userServiceServer = plugin((server) => { ); if (jobs.filter((i) => i.result.jobs.length > 0).length > 0) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} has jobs running or pending and cannot remove. Please wait for the job to end or end the job manually before moving out.`, - }; + } as ServiceError; } @@ -301,16 +301,16 @@ export const userServiceServer = plugin((server) => { }, { populate: ["user", "account"]}); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} or account ${accountName} is not found.`, - }; + } as ServiceError; } // 如果已经在集群下为封锁,且状态值为被账户管理员或拥有者手动封锁 if (user.blockedInCluster === UserStatus.BLOCKED && user.state === UserStateInAccount.BLOCKED_BY_ADMIN) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already blocked.`, - }; + } as ServiceError; } const currentActivatedClusters = await getActivatedClusters(em, logger); @@ -332,15 +332,15 @@ export const userServiceServer = plugin((server) => { }, { populate: ["user", "account"]}); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message:`User ${userId} or account ${accountName} is not found.`, - }; + } as ServiceError; } if (user.blockedInCluster === UserStatus.UNBLOCKED) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already unblocked.`, - }; + } as ServiceError; } // 判断如果限额和已用额度存在的情况是否可以解封 @@ -371,15 +371,15 @@ export const userServiceServer = plugin((server) => { }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message:`User ${userId} or account ${accountName} is not found.`, - }; + } as ServiceError; } if (user.role === UserRole.ADMIN) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already admin.`, - }; + } as ServiceError; } user.role = UserRole.ADMIN; @@ -397,15 +397,15 @@ export const userServiceServer = plugin((server) => { }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message:`User ${userId} or account ${accountName} is not found.`, - }; + } as ServiceError; } if (user.role === UserRole.USER) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already not admin.`, - }; + } as ServiceError; } user.role = UserRole.USER; @@ -424,15 +424,15 @@ export const userServiceServer = plugin((server) => { await createUserInDatabase(identityId, name, email, tenantName, server.logger, em) .catch((e) => { if (e.code === Status.ALREADY_EXISTS) { - throw { + throw { code: Status.ALREADY_EXISTS, message: `User with userId ${identityId} already exists in scow.`, details: "EXISTS_IN_SCOW", - }; + } as ServiceError; } - throw { + throw { code: Status.INTERNAL, - message: `Error creating user with userId ${identityId} in database.` }; + message: `Error creating user with userId ${identityId} in database.` } as ServiceError; }); // call auth const createdInAuth = await createUser(authUrl, @@ -453,9 +453,9 @@ export const userServiceServer = plugin((server) => { // 回滚数据库 await em.removeAndFlush(user); server.logger.error("Error creating user in auth.", e); - throw { + throw { code: Status.INTERNAL, - message: `Error creating user with userId ${identityId} in auth.` }; + message: `Error creating user with userId ${identityId} in auth.` } as ServiceError; } }); @@ -477,15 +477,15 @@ export const userServiceServer = plugin((server) => { = await createUserInDatabase(identityId, name, email, tenantName, server.logger, em) .catch((e) => { if (e.code === Status.ALREADY_EXISTS) { - throw { + throw { code: Status.ALREADY_EXISTS, message: `User with userId ${identityId} already exists in scow.`, details: "EXISTS_IN_SCOW", - }; + } as ServiceError; } - throw { + throw { code: Status.INTERNAL, - message: `Error creating user with userId ${identityId} in database.` }; + message: `Error creating user with userId ${identityId} in database.` } as ServiceError; }); await callHook("userAdded", { tenantName, userId: user.userId }, logger); @@ -500,7 +500,7 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId, tenant: { name: tenantName } }); if (!user) { - throw { code: Status.NOT_FOUND, message:`User ${userId} is not found.` }; + throw { code: Status.NOT_FOUND, message:`User ${userId} is not found.` } as ServiceError; } // find if the user is an owner of any account @@ -510,10 +510,10 @@ export const userServiceServer = plugin((server) => { }); if (accountUser) { - throw { + throw { code: Status.FAILED_PRECONDITION, details: `User ${userId} is an owner of an account.`, - }; + } as ServiceError; } await em.removeAndFlush(user); @@ -528,7 +528,7 @@ export const userServiceServer = plugin((server) => { const authUser = await getUser(authUrl, { identityId: userId }, server.logger); if (!authUser) { - throw { code: Status.NOT_FOUND, message:`User ${userId} is not found from auth` }; + throw { code: Status.NOT_FOUND, message:`User ${userId} is not found from auth` } as ServiceError; } if (authUser.name !== undefined) { @@ -541,7 +541,7 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId }, { fields: ["name"]}); if (!user) { - throw { code: Status.NOT_FOUND, message:`User ${userId} is not found in mis db` }; + throw { code: Status.NOT_FOUND, message:`User ${userId} is not found in mis db` } as ServiceError; } return [{ match: user.name === name }]; @@ -578,7 +578,7 @@ export const userServiceServer = plugin((server) => { }, { populate: ["accounts", "accounts.account", "tenant", "email"]}); if (!user) { - throw { code: Status.NOT_FOUND, message:`User ${userId} is not found.` }; + throw { code: Status.NOT_FOUND, message:`User ${userId} is not found.` } as ServiceError; } return [{ @@ -681,15 +681,15 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId: userId }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} is not found.`, - }; + } as ServiceError; } if (user.platformRoles.includes(dbRoleType)) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already this role.`, - }; + } as ServiceError; } user.platformRoles.push(dbRoleType); @@ -705,15 +705,15 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId: userId }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} is not found.`, - }; + } as ServiceError; } if (!user.platformRoles.includes(dbRoleType)) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already not this role.`, - }; + } as ServiceError; } user.platformRoles = user.platformRoles.filter((item) => @@ -730,15 +730,15 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId: userId }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} is not found.`, - }; + } as ServiceError; } if (user.tenantRoles.includes(dbRoleType)) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already this role.`, - }; + } as ServiceError; } user.tenantRoles.push(dbRoleType); @@ -754,15 +754,15 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId: userId }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} is not found.`, - }; + } as ServiceError; } if (!user.tenantRoles.includes(dbRoleType)) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} is already not this role.`, - }; + } as ServiceError; } user.tenantRoles = user.tenantRoles.filter((item) => @@ -777,9 +777,9 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne(User, { userId: userId }); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} is not found.`, - }; + } as ServiceError; } user.email = newEmail; @@ -796,20 +796,20 @@ export const userServiceServer = plugin((server) => { .catch(async (e) => { switch (e.status) { - case "NOT_FOUND": - throw { - code: Status.NOT_FOUND, message: `User ${userId} is not found.`, - }; + case "NOT_FOUND": + throw { + code: Status.NOT_FOUND, message: `User ${userId} is not found.`, + } as ServiceError; - case "NOT_SUPPORTED": - throw { - code: Status.UNIMPLEMENTED, message: "Changing email is not supported ", - }; + case "NOT_SUPPORTED": + throw { + code: Status.UNIMPLEMENTED, message: "Changing email is not supported ", + } as ServiceError; - default: - throw { - code: Status.UNKNOWN, message: "LDAP failed to change email", - }; + default: + throw { + code: Status.UNKNOWN, message: "LDAP failed to change email", + } as ServiceError; } }); } @@ -823,14 +823,14 @@ export const userServiceServer = plugin((server) => { checkTimeZone(timeZone); const qb = em.createQueryBuilder(User, "u"); - qb + void qb .select([raw("DATE(CONVERT_TZ(u.create_time, 'UTC', ?)) as date", [timeZone]), raw("count(*) as count")]) .where({ createTime: { $gte: startTime } }) .andWhere({ createTime: { $lte: endTime } }) .groupBy(raw("date")) .orderBy({ [raw("date")]: QueryOrder.DESC }); - const results: {date: string, count: number}[] = await qb.execute(); + const results: { date: string, count: number }[] = await qb.execute(); return [ { @@ -848,33 +848,33 @@ export const userServiceServer = plugin((server) => { const user = await em.findOne (User, { userId }, { populate: ["tenant"]}); if (!user) { - throw { + throw { code: Status.NOT_FOUND, message: `User ${userId} is not found.`, details: "USER_NOT_FOUND", - }; + } as ServiceError; } const userAccount = await em.findOne(UserAccount, { user: user }); if (userAccount) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: `User ${userId} still maintains account relationship.`, - }; + } as ServiceError; } const oldTenant = user.tenant.getEntity(); if (oldTenant.name === tenantName) { - throw { + throw { code: Status.ALREADY_EXISTS, message: `User ${userId} is already in tenant ${tenantName}.`, - }; + } as ServiceError; } const newTenant = await em.findOne(Tenant, { name: tenantName }); if (!newTenant) { - throw { + throw { code: Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.`, details: "TENANT_NOT_FOUND", - }; + } as ServiceError; } em.assign(user, { tenant: newTenant }); diff --git a/apps/mis-server/src/tasks/fetch.ts b/apps/mis-server/src/tasks/fetch.ts index 335a6ed0b0..aba6609f89 100644 --- a/apps/mis-server/src/tasks/fetch.ts +++ b/apps/mis-server/src/tasks/fetch.ts @@ -38,13 +38,13 @@ async function getClusterLatestDate(em: SqlEntityManager, cluster: string, logge const { timeEnd = undefined } = (await query.execute("get")) ?? {}; - logger.info(`Latest fetched job's end_time is ${timeEnd}.`); + logger.info(`Latest fetched job's end_time is ${timeEnd?.toISOString() ?? "undefined"}.`); return timeEnd; } const processGetJobsResult = (cluster: string, result: GetJobsResponse) => { - const jobs: ({cluster: string} & ClusterJobInfo)[] = []; + const jobs: ({ cluster: string } & ClusterJobInfo)[] = []; result.jobs.forEach((job) => { jobs.push({ cluster, ...job }); }); @@ -75,7 +75,7 @@ export async function fetchJobs( const accountTenantMap = new Map(accounts.map((x) => [x.accountName, x.tenant.$.name])); - const priceMap = await createPriceMap(em, clusterPlugin["clusters"], logger); + const priceMap = await createPriceMap(em, clusterPlugin.clusters, logger); const persistJobAndCharge = async (jobs: ({ cluster: string } & ClusterJobInfo)[]) => { const result = await em.transactional(async (em) => { @@ -207,7 +207,8 @@ export async function fetchJobs( ? (nextDate > configDate ? nextDate : configDate) : (nextDate || configDate); const endFetchDate = new Date(); - logger.info(`Fetching new info which end_time is from ${startFetchDate} to ${endFetchDate}`); + logger.info(`Fetching new info which end_time is from + ${startFetchDate?.toISOString()} to ${endFetchDate.toISOString()}`); const fields: string[] = [ "job_id", "name", "user", "account", "cpus_alloc", "gpus_alloc", "mem_alloc_mb", "mem_req_mb", @@ -244,7 +245,7 @@ export async function fetchJobs( let savedJobsCount = 0; for (const job of jobsInfo) { - if (job.endTime! === previousDate) { + if (job.endTime === previousDate) { currentJobsGroup.push(job); } else { savedJobsCount += await persistJobAndCharge(currentJobsGroup); diff --git a/apps/mis-server/src/utils/accountUserState.ts b/apps/mis-server/src/utils/accountUserState.ts index 14934df5dc..c5f3d35fa0 100644 --- a/apps/mis-server/src/utils/accountUserState.ts +++ b/apps/mis-server/src/utils/accountUserState.ts @@ -96,7 +96,7 @@ export const getUserStateInfo = ( } // 限额与已用额度存在且不为时,显示状态为封锁,表示用户不可以使用集群资源 - if (currentLimit && currentUsed && currentUsed.gte(currentLimit)) { + if (currentLimit && currentUsed?.gte(currentLimit)) { return { displayedState: DisplayedUserState.DISPLAYED_QUOTA_EXCEEDED, shouldBlockInCluster: true, diff --git a/apps/mis-server/src/utils/array.ts b/apps/mis-server/src/utils/array.ts index a4514a78e1..26f654c294 100644 --- a/apps/mis-server/src/utils/array.ts +++ b/apps/mis-server/src/utils/array.ts @@ -18,10 +18,10 @@ export function range(start = 1, end = 0, step = 1): number[] { return r; } -type ObjectTypeWithType = { +interface ObjectTypeWithType { type: string; [key: string]: any; -}; +} export function extractTypesFromObjects(array: ObjectTypeWithType[]): string[] { const types = new Set(); diff --git a/apps/mis-server/src/utils/chargesQuery.ts b/apps/mis-server/src/utils/chargesQuery.ts index a786153384..212152f2e2 100644 --- a/apps/mis-server/src/utils/chargesQuery.ts +++ b/apps/mis-server/src/utils/chargesQuery.ts @@ -38,39 +38,39 @@ export const getChargesTargetSearchParam = ( ): { tenantName?: string | { $ne: null }, accountName?: string | { $ne: null } | { $in: string[] } } => { let searchParam: { tenantName?: string | { $ne: null }, - accountName?: string | { $ne: null } | { $in: string[] } } = {}; + accountName?: string | { $ne: null } | { $in: string[] } } = {}; switch (target?.$case) { // 当前租户的租户消费记录 - case "tenant": - searchParam = { tenantName: target[target.$case].tenantName, accountName: undefined }; - break; - // 所有租户的租户消费记录 - case "allTenants": - searchParam = { tenantName: { $ne:null }, accountName: undefined }; - break; - // 当前租户下当前账户的消费记录 - case "accountOfTenant": - searchParam = { tenantName: target[target.$case].tenantName, accountName: target[target.$case].accountName }; - break; - // 当前租户下多个账户的消费记录 - case "accountsOfTenant": - { - const { accountNames } = target.accountsOfTenant; - searchParam = { tenantName: target[target.$case].tenantName, - accountName:accountNames.length ? { $in: accountNames } : { $ne:null } }; + case "tenant": + searchParam = { tenantName: target[target.$case].tenantName, accountName: undefined }; break; - } ; - // 所有租户下多个账户的消费记录 - case "accountsOfAllTenants": - { - const { accountNames } = target.accountsOfAllTenants; - searchParam = { tenantName: { $ne: null }, accountName:accountNames.length ? - { $in: accountNames } : { $ne:null } }; + // 所有租户的租户消费记录 + case "allTenants": + searchParam = { tenantName: { $ne:null }, accountName: undefined }; break; - }; - default: - searchParam = {}; + // 当前租户下当前账户的消费记录 + case "accountOfTenant": + searchParam = { tenantName: target[target.$case].tenantName, accountName: target[target.$case].accountName }; + break; + // 当前租户下多个账户的消费记录 + case "accountsOfTenant": + { + const { accountNames } = target.accountsOfTenant; + searchParam = { tenantName: target[target.$case].tenantName, + accountName:accountNames.length ? { $in: accountNames } : { $ne:null } }; + break; + } ; + // 所有租户下多个账户的消费记录 + case "accountsOfAllTenants": + { + const { accountNames } = target.accountsOfAllTenants; + searchParam = { tenantName: { $ne: null }, accountName:accountNames.length ? + { $in: accountNames } : { $ne:null } }; + break; + }; + default: + searchParam = {}; } return searchParam; }; @@ -120,25 +120,29 @@ export const getPaymentsTargetSearchParam = (target: | { $case: "accountsOfTenant"; accountsOfTenant: AccountsOfTenantTarget } | { $case: "tenant"; tenant: TenantTarget } | { $case: "allTenants"; allTenants: AllTenantsTarget }): - { tenantName?: string | { $ne: null }, accountName?: { $in: string[] } | string | { $ne: null }} => { +{ tenantName?: string | { $ne: null }, accountName?: { $in: string[] } | string | { $ne: null } } => { + + let searchParam: { + tenantName?: string | { $ne: null }, + accountName?: { $in: string[] } | string | { $ne: null } + } = {}; - let searchParam: { tenantName?: string | { $ne: null }, - accountName?: { $in: string[] } | string | { $ne: null }} = {}; const { accountNames, tenantName } = target[target.$case]; switch (target?.$case) { - case "tenant": - searchParam = { tenantName, accountName:undefined }; - break; - case "allTenants": - searchParam = { accountName:undefined }; - break; - case "accountsOfTenant": - const accountName = accountNames.length === 0 ? { $ne:null } : { $in:accountNames }; - searchParam = { tenantName, accountName }; - break; - default: - break; + case "tenant": + searchParam = { tenantName, accountName:undefined }; + break; + case "allTenants": + searchParam = { accountName:undefined }; + break; + case "accountsOfTenant": { + const accountName = accountNames.length === 0 ? { $ne:null } : { $in:accountNames }; + searchParam = { tenantName, accountName }; + break; + } + default: + break; } return searchParam; }; diff --git a/apps/mis-server/src/utils/createUser.ts b/apps/mis-server/src/utils/createUser.ts index 76369d411a..2ce8d85861 100644 --- a/apps/mis-server/src/utils/createUser.ts +++ b/apps/mis-server/src/utils/createUser.ts @@ -33,7 +33,7 @@ export async function createUserInDatabase( // get default tenant const tenant = await em.findOne(Tenant, { name: tenantName }); if (!tenant) { - throw { code: Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` }; + throw { code: Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` } as ServiceError; } // new the user @@ -44,17 +44,17 @@ export async function createUserInDatabase( user.storageQuotas.add(Object.keys(configClusters).map((x) => new StorageQuota({ cluster: x, storageQuota: 0, - user: user!, + user: user, }))); try { await em.persistAndFlush(user); } catch (e) { if (e instanceof UniqueConstraintViolationException) { - throw { + throw { code: Status.ALREADY_EXISTS, message:`User with userId ${userId} already exists.`, details: "EXISTS_IN_SCOW", - }; + } as ServiceError; } else { throw e; } diff --git a/apps/mis-server/src/utils/job.ts b/apps/mis-server/src/utils/job.ts index 62a2006d3a..4a344b51e7 100644 --- a/apps/mis-server/src/utils/job.ts +++ b/apps/mis-server/src/utils/job.ts @@ -15,7 +15,7 @@ import { JobInfo } from "@scow/protos/build/common/ended_job"; import { JobInfo as JobInfoEntity } from "src/entities/JobInfo"; export function toGrpc(x: JobInfoEntity) { - return { + return { account: x.account, biJobIndex: x.biJobIndex, cluster: x.cluster, @@ -41,5 +41,5 @@ export function toGrpc(x: JobInfoEntity) { user: x.user, tenantPrice: decimalToMoney(x.tenantPrice), accountPrice: decimalToMoney(x.accountPrice), - }; + } as JobInfo; } diff --git a/apps/mis-server/src/utils/types.ts b/apps/mis-server/src/utils/types.ts index c06b9a7bb1..3e8f4878c0 100644 --- a/apps/mis-server/src/utils/types.ts +++ b/apps/mis-server/src/utils/types.ts @@ -10,8 +10,11 @@ * See the Mulan PSL v2 for more details. */ +// @ts-ignore +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents export type AnyJson = boolean | number | string | null | JsonArray | JsonMap; -export interface JsonMap { [key: string]: AnyJson; } +// @ts-ignore +export type JsonMap = Record; interface JsonArray extends Array {} export type ValueOf = T[keyof T]; diff --git a/apps/mis-server/tests/admin/clusterActivation.test.ts b/apps/mis-server/tests/admin/clusterActivation.test.ts index 2e131c52ff..a9ade45f6b 100644 --- a/apps/mis-server/tests/admin/clusterActivation.test.ts +++ b/apps/mis-server/tests/admin/clusterActivation.test.ts @@ -122,7 +122,7 @@ it("cannot write to db when activated a cluster has already been activated", asy clusterId: "hpc01", }); - expect(activatedCluster.lastActivationOperation).toBeUndefined; + expect(activatedCluster.lastActivationOperation).toBeUndefined(); }); it("activates a cluster", async () => { diff --git a/apps/mis-server/tests/admin/fetch.test.ts b/apps/mis-server/tests/admin/fetch.test.ts index b5f4e7484a..abe7d2e88f 100644 --- a/apps/mis-server/tests/admin/fetch.test.ts +++ b/apps/mis-server/tests/admin/fetch.test.ts @@ -10,7 +10,6 @@ * See the Mulan PSL v2 for more details. */ -/* eslint-disable max-len */ import { asyncClientCall } from "@ddadaal/tsgrpc-client"; import { Server } from "@ddadaal/tsgrpc-server"; import { ChannelCredentials } from "@grpc/grpc-js"; diff --git a/apps/mis-server/tests/admin/importUsers.test.ts b/apps/mis-server/tests/admin/importUsers.test.ts index 5f2d773bd0..8d11c84e7b 100644 --- a/apps/mis-server/tests/admin/importUsers.test.ts +++ b/apps/mis-server/tests/admin/importUsers.test.ts @@ -10,7 +10,6 @@ * See the Mulan PSL v2 for more details. */ -/* eslint-disable max-len */ import { asyncClientCall } from "@ddadaal/tsgrpc-client"; import { Server } from "@ddadaal/tsgrpc-server"; import { ChannelCredentials } from "@grpc/grpc-js"; @@ -47,13 +46,15 @@ const data = { accounts: [ { accountName: "a_user1", - users: [{ userId: "user1", userName: "user1Name", blocked: false }, { userId: "user2", userName: "user2", blocked: true }], + users: [{ userId: "user1", userName: "user1Name", blocked: false }, + { userId: "user2", userName: "user2", blocked: true }], owner: "user1", blocked: false, }, { accountName: "account2", - users: [{ userId: "user2", userName: "user2", blocked: false }, { userId: "user3", userName: "user3", blocked: true }], + users: [{ userId: "user2", userName: "user2", blocked: false }, + { userId: "user3", userName: "user3", blocked: true }], owner: "user2", blocked: false, }, diff --git a/apps/mis-server/tests/admin/tenant.test.ts b/apps/mis-server/tests/admin/tenant.test.ts index 1714ad3667..3a1c54a9d0 100644 --- a/apps/mis-server/tests/admin/tenant.test.ts +++ b/apps/mis-server/tests/admin/tenant.test.ts @@ -115,7 +115,7 @@ it("create a new tenant", async () => { const user = await em.findOneOrFail(User, { userId: "userIdTest" }); expect(user.name).toBe("userNameTest"); - expect(user.tenantRoles.includes(TenantRole["TENANT_ADMIN"])).toBe(true); + expect(user.tenantRoles.includes(TenantRole.TENANT_ADMIN)).toBe(true); expect(createUser).toHaveBeenNthCalledWith( 1, diff --git a/apps/mis-server/tests/admin/user.test.ts b/apps/mis-server/tests/admin/user.test.ts index 2f0f08f059..f8dd6335f1 100644 --- a/apps/mis-server/tests/admin/user.test.ts +++ b/apps/mis-server/tests/admin/user.test.ts @@ -499,7 +499,7 @@ it("manage platform role", async () => { }); const setUser = await em.findOne(User, { userId: data.userA.userId }); - expect(setUser?.platformRoles.includes(pRole["PLATFORM_ADMIN"])).toBe(true); + expect(setUser?.platformRoles.includes(pRole.PLATFORM_ADMIN)).toBe(true); await asyncClientCall(client, "unsetPlatformRole", { userId: data.userA.userId, @@ -507,7 +507,7 @@ it("manage platform role", async () => { }); const unsetUser = await em.findOne(User, { userId: data.userA.userId }); - expect(unsetUser?.platformRoles.includes(pRole["PLATFORM_ADMIN"])).toBe(false); + expect(unsetUser?.platformRoles.includes(pRole.PLATFORM_ADMIN)).toBe(false); }); it("manage tenant role", async () => { @@ -520,7 +520,7 @@ it("manage tenant role", async () => { }); const setUser = await em.findOne(User, { userId: data.userA.userId }); - expect(setUser?.tenantRoles.includes(tRole["TENANT_FINANCE"])).toBe(true); + expect(setUser?.tenantRoles.includes(tRole.TENANT_FINANCE)).toBe(true); await asyncClientCall(client, "unsetTenantRole", { userId: data.userA.userId, @@ -528,7 +528,7 @@ it("manage tenant role", async () => { }); const unsetUser = await em.findOne(User, { userId: data.userA.userId }); - expect(unsetUser?.tenantRoles.includes(tRole["TENANT_ADMIN"])).toBe(false); + expect(unsetUser?.tenantRoles.includes(tRole.TENANT_ADMIN)).toBe(false); }); it("get platform role users Count", async () => { diff --git a/apps/mis-server/tests/charging/charging.test.ts b/apps/mis-server/tests/charging/charging.test.ts index 3c585326e9..9b3bb95c13 100644 --- a/apps/mis-server/tests/charging/charging.test.ts +++ b/apps/mis-server/tests/charging/charging.test.ts @@ -49,7 +49,7 @@ beforeEach(async () => { comment: "test", }); - const user = new User({ + const _user = new User({ userId: "tester", tenant, name: "Tester", @@ -188,9 +188,9 @@ it("returns NOT_FOUND if account is not found", async () => { const client = new ChargingServiceClient(server.serverAddress, ChannelCredentials.createInsecure()); - const ret = await asyncClientCall(client, "pay", request).catch((e) => e); + const ret = await asyncClientCall(client, "pay", request).catch((e) => e as { code: number }); - expect(ret.code).toBe(grpc.status.NOT_FOUND); + expect((ret as { code: number }).code).toBe(grpc.status.NOT_FOUND); }); it("gets account balance", async () => { @@ -454,7 +454,7 @@ it("returns payment records", async () => { ipAddress: request1.ipAddress, amount: request1.amount, }, - ]as Partial); + ] as Partial); expect(reply6.total).toStrictEqual(numberToMoney(40)); }); @@ -754,7 +754,7 @@ it("returns charge records with query of accountsOfTenant", async () => { amount: request2.amount, type: request2.type, }, - ]as Partial); + ] as Partial); em.clear(); }); @@ -874,7 +874,7 @@ it("returns charge records with query allAccountOfAllTenants", async () => { "idJob": 9, }, }, - ]as Partial); + ] as Partial); em.clear(); }); @@ -1158,7 +1158,7 @@ it("returns charge records with query of accounts", async () => { accountName: request3.accountName, comment: request3.comment, amount: request3.amount, - } ]as Partial); + } ] as Partial); const reply2 = await asyncClientCall(client, "getPaginatedChargeRecords", { startTime: queryStartTime.toISOString(), @@ -1199,7 +1199,7 @@ it("returns charge records with query of accounts", async () => { accountName: request4.accountName, comment: request4.comment, amount: request4.amount, - } ]as Partial); + } ] as Partial); em.clear(); }); diff --git a/apps/mis-server/tests/job/billingItems.test.ts b/apps/mis-server/tests/job/billingItems.test.ts index 9cad8c3ec7..54ab65d81e 100644 --- a/apps/mis-server/tests/job/billingItems.test.ts +++ b/apps/mis-server/tests/job/billingItems.test.ts @@ -114,10 +114,10 @@ it("creates billing items in db", async () => { const anyDate = () => expect.any(String); -const priceItemToJobBillingItem = (x: PriceItem) => ({ +const priceItemToJobBillingItem = (x: PriceItem) => ({ id: x.itemId, path: x.path.join("."), tenantName: x.tenant, price: decimalToMoney(x.price), createTime: anyDate(), amountStrategy: expect.any(String), -}); +} as JobBillingItem); it("returns all default billing items", async () => { const reply = await asyncClientCall(client, "getBillingItems", { activeOnly: false }); @@ -249,7 +249,8 @@ it("calculates price", async () => { accountPrice: { expected: number; actual: number | undefined } }[]; - testData.forEach(async (t) => { + for (const t of testData) { + const price = await priceMap.calculatePrice({ jobId: t.jobId, cluster: t.cluster, @@ -269,7 +270,7 @@ it("calculates price", async () => { accountPrice: { expected: t.accountPrice, actual: price.account?.price.toNumber() }, }); } - }); + }; expect(wrongPrices).toBeArrayOfSize(0); diff --git a/apps/mis-server/tests/job/fetchJobs.test.ts b/apps/mis-server/tests/job/fetchJobs.test.ts index 9eef853291..a4acc2ce0d 100644 --- a/apps/mis-server/tests/job/fetchJobs.test.ts +++ b/apps/mis-server/tests/job/fetchJobs.test.ts @@ -10,7 +10,6 @@ * See the Mulan PSL v2 for more details. */ -/* eslint-disable max-len */ import { Server } from "@ddadaal/tsgrpc-server"; import { MySqlDriver, SqlEntityManager } from "@mikro-orm/mysql"; import { Decimal } from "@scow/lib-decimal"; @@ -67,10 +66,13 @@ it("fetches the data", async () => { expect(jobs).toBeArrayOfSize(testData.length); - const wrongPrices = [] as { tenantPrice: { expected: number; actual: number }; accountPrice: { expected: number; actual: number } }[]; + const wrongPrices = [] as { + tenantPrice: { expected: number; actual: number }; accountPrice: { expected: number; actual: number } + }[]; testData.forEach((t) => { - const job = jobs.find((x) => x.cluster === t.cluster && x.idJob === t.jobId) ?? { accountPrice: new Decimal(-1), tenantPrice: new Decimal(-1) }; + const job = jobs.find((x) => x.cluster === t.cluster && x.idJob === t.jobId) + ?? { accountPrice: new Decimal(-1), tenantPrice: new Decimal(-1) }; if (job.tenantPrice.toNumber() !== t.tenantPrice || job.accountPrice.toNumber() !== t.accountPrice) { wrongPrices.push({ tenantPrice: { expected: t.tenantPrice, actual: job.tenantPrice.toNumber() }, @@ -82,7 +84,10 @@ it("fetches the data", async () => { expect(wrongPrices).toBeArrayOfSize(0); // check account balances - let accountACharges = new Decimal(0), accountBCharges = new Decimal(0), defaultTenantCharges = new Decimal(0), anotherTenantCharges = new Decimal(0); + let accountACharges = new Decimal(0), + accountBCharges = new Decimal(0), + defaultTenantCharges = new Decimal(0), + anotherTenantCharges = new Decimal(0); jobs.forEach((x) => { if (x.tenant === data.tenant.name) { defaultTenantCharges = defaultTenantCharges.plus(x.tenantPrice); diff --git a/apps/mis-web/.eslintrc.json b/apps/mis-web/.eslintrc.json deleted file mode 100644 index 3024b32cae..0000000000 --- a/apps/mis-web/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": [ - "@ddadaal/eslint-config/react", - "../../.eslintrc.js" - ] -} diff --git a/apps/mis-web/eslint.config.js b/apps/mis-web/eslint.config.js new file mode 100644 index 0000000000..80c003f736 --- /dev/null +++ b/apps/mis-web/eslint.config.js @@ -0,0 +1,7 @@ +const base = require("../../eslint.config"); +const react = require("@ddadaal/eslint-config/react"); + +module.export = { + ...base, + ...react, +}; diff --git a/apps/mis-web/next.config.js b/apps/mis-web/next.config.js index 4e618d5e58..79bdbfa6a5 100644 --- a/apps/mis-web/next.config.js +++ b/apps/mis-web/next.config.js @@ -10,8 +10,6 @@ * See the Mulan PSL v2 for more details. */ -/* esslint-disable @typescript-eslint/no-var-requires */ - // @ts-check const withPlugins = require("next-compose-plugins"); diff --git a/apps/mis-web/package.json b/apps/mis-web/package.json index 943feed365..473194f19a 100644 --- a/apps/mis-web/package.json +++ b/apps/mis-web/package.json @@ -8,7 +8,8 @@ "serve": "next start", "build": "npm run build:next", "build:next": "cross-env BUILDING=1 next build", - "client": "ntar client" + "client": "ntar client", + "lint": "eslint ." }, "files": [ ".next", diff --git a/apps/portal-server/package.json b/apps/portal-server/package.json index 04a37e0805..7115337795 100644 --- a/apps/portal-server/package.json +++ b/apps/portal-server/package.json @@ -8,7 +8,8 @@ "dev": "dotenv -e env/.env.dev -- node -r ts-node/register -r tsconfig-paths/register --watch src/index.ts", "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", "serve": "node build/index.js", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "assets", diff --git a/apps/portal-server/src/clusterops/api/app.ts b/apps/portal-server/src/clusterops/api/app.ts index faca2c9ced..07dcbb023c 100644 --- a/apps/portal-server/src/clusterops/api/app.ts +++ b/apps/portal-server/src/clusterops/api/app.ts @@ -22,17 +22,17 @@ export interface CreateAppRequest { coreCount: number; /** in minutes */ maxTime: number; - customAttributes: { [key: string]: string }; + customAttributes: Record; proxyBasePath: string; nodeCount: number; gpuCount?: number; memory?: string; } -export type CreateAppReply = { +export interface CreateAppReply { sessionId: string; jobId: number; -} +}; export interface GetAppSessionsRequest { userId: string; @@ -62,13 +62,13 @@ export interface ConnectToAppRequest { sessionId: string; } -export type ConnectToAppReply = { +export interface ConnectToAppReply { appId: string; host: string; port: number; password: string; - customFormData?: {[key: string]: string}; -}; + customFormData?: Record; +} export interface SubmissionInfo { userId: string; @@ -83,7 +83,7 @@ export interface SubmissionInfo { gpuCount?: number; maxTime: number; submitTime?: string; - customAttributes: { [key: string]: string }; + customAttributes: Record; } export interface GetAppLastSubmissionRequest { @@ -91,9 +91,9 @@ export interface GetAppLastSubmissionRequest { appId: string; } -export type GetAppLastSubmissionReply = { +export interface GetAppLastSubmissionReply { lastSubmissionInfo?: SubmissionInfo; -} +}; export interface AppOps { createApp(req: CreateAppRequest, logger: Logger): Promise; diff --git a/apps/portal-server/src/clusterops/api/job.ts b/apps/portal-server/src/clusterops/api/job.ts index 7ceda107e9..ca9399a75a 100644 --- a/apps/portal-server/src/clusterops/api/job.ts +++ b/apps/portal-server/src/clusterops/api/job.ts @@ -54,9 +54,9 @@ export interface GetJobTemplateRequest { id: string; } -export type GetJobTemplateReply = { +export interface GetJobTemplateReply { template: JobTemplate; -} +}; export interface SaveJobTemplateRequest { userId: string; diff --git a/apps/portal-server/src/clusterops/app.ts b/apps/portal-server/src/clusterops/app.ts index 3fedc73b48..3a926ed0b8 100644 --- a/apps/portal-server/src/clusterops/app.ts +++ b/apps/portal-server/src/clusterops/app.ts @@ -13,6 +13,7 @@ import { asyncClientCall } from "@ddadaal/tsgrpc-client"; import { ServiceError } from "@grpc/grpc-js"; import { Status } from "@grpc/grpc-js/build/src/constants"; +import { AppType } from "@scow/config/build/app"; import { getPlaceholderKeys } from "@scow/lib-config/build/parse"; import { formatTime } from "@scow/lib-scheduler-adapter"; import { getAppConnectionInfoFromAdapter, getEnvVariables } from "@scow/lib-server"; @@ -80,8 +81,8 @@ export const appOps = (cluster: string): AppOps => { const memoryMb = memory ? Number(memory.slice(0, -2)) : undefined; - const userSbatchOptions = customAttributes["sbatchOptions"] - ? splitSbatchArgs(customAttributes["sbatchOptions"]) + const userSbatchOptions = customAttributes.sbatchOptions + ? splitSbatchArgs(customAttributes.sbatchOptions) : []; // prepare script file @@ -125,14 +126,14 @@ export const appOps = (cluster: string): AppOps => { && errors[0].reason === "SBATCH_FAILED") { throw new DetailedError({ code: Status.INTERNAL, - message: e.details, + message: ex.details, details: [errorInfo("SBATCH_FAILED")], }); } else { throw new DetailedError({ - code: e.code, - message: e.details, + code: ex.code, + message: ex.details, details: [errorInfo("SBATCH_FAILED")], }); } @@ -183,12 +184,12 @@ export const appOps = (cluster: string): AppOps => { customAttributesExport = customAttributesExport + envItem + "\n"; } - if (appConfig.type === "web") { + if (appConfig.type === AppType.web) { let customForm = String.raw`\"HOST\":\"$HOST\",\"PORT\":$PORT`; for (const key in appConfig.web!.connect.formData) { const texts = getPlaceholderKeys(appConfig.web!.connect.formData[key]); - for (const i in texts) { - customForm += `,\\\"${texts[i]}\\\":\\\"$${texts[i]}\\\"`; + for (const i of texts) { + customForm += `,\\"${i}\\":\\"$${i}\\"`; } } const sessionInfo = `echo -e "{${customForm}}" >$SERVER_SESSION_INFO`; @@ -321,7 +322,7 @@ export const appOps = (cluster: string): AppOps => { // 具体体现为sftpExists无法找到新生成的SERVER_SESSION_INFO和VNC_SESSION_INFO文件,必须实际读取一次目录,才能识别到它们 await sftpReaddir(sftp)(jobDir); - if (app.type === "web") { + if (app.type === AppType.web) { // for server apps, // try to read the SESSION_INFO file to get port and password const infoFilePath = join(jobDir, SERVER_SESSION_INFO); @@ -342,14 +343,13 @@ export const appOps = (cluster: string): AppOps => { const content = (await sftpReadFile(sftp)(outputFilePath)).toString(); try { const displayId = parseDisplayId(content); - port = displayIdToPort(displayId!); + port = displayIdToPort(displayId); } catch { // ignored if displayId cannot be parsed } } host = (await sftpReadFile(sftp)(vncSessionInfoPath)).toString().trim(); - port = port; } } @@ -403,7 +403,7 @@ export const appOps = (cluster: string): AppOps => { const jobDir = join(userHomeDir, portalConfig.appJobsDir, sessionId); if (!await sftpExists(sftp, jobDir)) { - throw { code: Status.NOT_FOUND, message: `session id ${sessionId} is not found` }; + throw { code: Status.NOT_FOUND, message: `session id ${sessionId} is not found` } as ServiceError; } const metadataPath = join(jobDir, SESSION_METADATA_NAME); @@ -427,13 +427,13 @@ export const appOps = (cluster: string): AppOps => { }; } - if (app.type === "web") { + if (app.type === AppType.web) { const infoFilePath = join(jobDir, SERVER_SESSION_INFO); if (await sftpExists(sftp, infoFilePath)) { const content = await sftpReadFile(sftp)(infoFilePath); const serverSessionInfo = JSON.parse(content.toString()) as ServerSessionInfoData; const { HOST, PORT, PASSWORD, ...rest } = serverSessionInfo; - const customFormData = rest as {[key: string]: string}; + const customFormData = rest as Record; const ip = await getIpFromProxyGateway(cluster, HOST, logger); return { appId: sessionMetadata.appId, @@ -477,11 +477,11 @@ export const appOps = (cluster: string): AppOps => { return await sshConnect(url.hostname, "root", logger, async (proxyGatewaySsh) => { logger.info(`Connecting to compute node ${host} via proxy gateway ${url.hostname}`); const { password, ip } = - await refreshPasswordByProxyGateway(proxyGatewaySsh, cluster, host, userId, logger, displayId!); + await refreshPasswordByProxyGateway(proxyGatewaySsh, cluster, host, userId, logger, displayId); return { appId: sessionMetadata.appId, host: ip || host, - port: displayIdToPort(displayId!), + port: displayIdToPort(displayId), password, }; }); @@ -491,11 +491,11 @@ export const appOps = (cluster: string): AppOps => { // connect as user so that // the service node doesn't need to be able to connect to compute nodes with public key return await sshConnect(host, userId, logger, async (computeNodeSsh) => { - const password = await refreshPassword(computeNodeSsh, cluster, null, logger, displayId!); + const password = await refreshPassword(computeNodeSsh, cluster, null, logger, displayId); return { appId: sessionMetadata.appId, host, - port: displayIdToPort(displayId!), + port: displayIdToPort(displayId), password, }; }); @@ -504,7 +504,7 @@ export const appOps = (cluster: string): AppOps => { } } - throw { code: Status.UNAVAILABLE, message: `session id ${sessionId} cannot be connected` }; + throw { code: Status.UNAVAILABLE, message: `session id ${sessionId} cannot be connected` } as ServiceError; }); }, diff --git a/apps/portal-server/src/clusterops/desktop/scowdDesktop.ts b/apps/portal-server/src/clusterops/desktop/scowdDesktop.ts index f660f9234d..ad812de6c8 100644 --- a/apps/portal-server/src/clusterops/desktop/scowdDesktop.ts +++ b/apps/portal-server/src/clusterops/desktop/scowdDesktop.ts @@ -29,7 +29,7 @@ export const scowdDesktopServices = (cluster: string): DesktopOps => ({ const scowdUrl = getLoginNodeScowdUrl(cluster, host); if (!scowdUrl) { - throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` }; + throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` } as ServiceError; } const client = getScowdClient(scowdUrl, certificates); @@ -59,7 +59,7 @@ export const scowdDesktopServices = (cluster: string): DesktopOps => ({ const scowdUrl = getLoginNodeScowdUrl(cluster, host); if (!scowdUrl) { - throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` }; + throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` } as ServiceError; } const client = getScowdClient(scowdUrl, certificates); @@ -87,7 +87,7 @@ export const scowdDesktopServices = (cluster: string): DesktopOps => ({ const scowdUrl = getLoginNodeScowdUrl(cluster, host); if (!scowdUrl) { - throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` }; + throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` } as ServiceError; } const client = getScowdClient(scowdUrl, certificates); @@ -115,7 +115,7 @@ export const scowdDesktopServices = (cluster: string): DesktopOps => ({ const scowdUrl = getLoginNodeScowdUrl(cluster, host); if (!scowdUrl) { - throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` }; + throw { code: status.INTERNAL, details: `Cluster ${cluster} not have login node ${host}` } as ServiceError; } const client = getScowdClient(scowdUrl, certificates); diff --git a/apps/portal-server/src/clusterops/desktop/sshDesktop.ts b/apps/portal-server/src/clusterops/desktop/sshDesktop.ts index adf2825121..d8ed88499e 100644 --- a/apps/portal-server/src/clusterops/desktop/sshDesktop.ts +++ b/apps/portal-server/src/clusterops/desktop/sshDesktop.ts @@ -37,7 +37,7 @@ export const sshDesktopServices = (cluster: string): DesktopOps => ({ const ids = parseListOutput(resp.stdout); if (ids.length >= maxDesktops) { - throw { code: status.RESOURCE_EXHAUSTED, message: "Too many desktops" }; + throw { code: status.RESOURCE_EXHAUSTED, message: "Too many desktops" } as ServiceError; } // start a session diff --git a/apps/portal-server/src/clusterops/file/scowdFile.ts b/apps/portal-server/src/clusterops/file/scowdFile.ts index 73a457efcf..8295fb0bcf 100644 --- a/apps/portal-server/src/clusterops/file/scowdFile.ts +++ b/apps/portal-server/src/clusterops/file/scowdFile.ts @@ -123,14 +123,14 @@ export const scowdFileServices = (client: ScowdClient): FileOps => ({ const { userId, path, call } = request; try { - const readStream = await client.file.download({ + const readStream = client.file.download({ userId, path, chunkSize: config.DOWNLOAD_CHUNK_SIZE, }); - + for await (const response of readStream) { call.write(response); } - + return {}; } catch (err) { throw mapTRPCExceptionToGRPC(err); @@ -140,15 +140,17 @@ export const scowdFileServices = (client: ScowdClient): FileOps => ({ upload: async (request, logger) => { const { call, userId, path } = request; - class RequestError { + class RequestError extends Error { constructor( public code: ServiceError["code"], public message: ServiceError["message"], public details?: ServiceError["details"], - ) {} + ) { + super(message); + } toServiceError(): ServiceError { - return { code: this.code, message: this.message, details: this.details }; + return { code: this.code, message: this.message, details: this.details } as ServiceError; } } @@ -157,7 +159,7 @@ export const scowdFileServices = (client: ScowdClient): FileOps => ({ try { const res = await client.file.upload((async function* () { yield { message: { case: "info", value: { path, userId } } }; - + for await (const data of call.iter()) { if (data.message?.$case !== "chunk") { throw new RequestError( @@ -182,7 +184,7 @@ export const scowdFileServices = (client: ScowdClient): FileOps => ({ const res = await client.file.getFileMetadata({ userId, filePath: path }); return { size: Number(res.size), type: res.type === FileType.DIR ? "dir" : "file" }; - + } catch (err) { throw mapTRPCExceptionToGRPC(err); } @@ -195,9 +197,9 @@ export const scowdFileServices = (client: ScowdClient): FileOps => ({ const res = await client.file.exists({ userId, path: path }); return { exists: res.exists }; - + } catch (err) { throw mapTRPCExceptionToGRPC(err); } }, -}); \ No newline at end of file +}); diff --git a/apps/portal-server/src/clusterops/file/sshFile.ts b/apps/portal-server/src/clusterops/file/sshFile.ts index b8d9f7e7e7..81170167be 100644 --- a/apps/portal-server/src/clusterops/file/sshFile.ts +++ b/apps/portal-server/src/clusterops/file/sshFile.ts @@ -12,7 +12,7 @@ import { createWriterExtensions } from "@ddadaal/tsgrpc-common"; import { ServiceError, status } from "@grpc/grpc-js"; -import { loggedExec, sftpExists, sftpMkdir, sftpReaddir, +import { loggedExec, sftpExists, sftpMkdir, sftpReaddir, sftpRealPath, sftpRename, sftpStat, sftpUnlink, sftpWriteFile, sshRmrf } from "@scow/lib-ssh"; import { FileInfo, FileInfo_FileType } from "@scow/protos/build/portal/file"; import { join } from "path"; @@ -32,7 +32,7 @@ export const sshFileServices = (host: string): FileOps => ({ const resp = await ssh.exec("cp", ["-r", fromPath, toPath], { stream: "both" }); if (resp.code !== 0) { - throw { code: status.INTERNAL, message: "cp command failed", details: resp.stderr }; + throw { code: status.INTERNAL, message: "cp command failed", details: resp.stderr } as ServiceError; } return {}; @@ -48,7 +48,7 @@ export const sshFileServices = (host: string): FileOps => ({ const sftp = await ssh.requestSFTP(); if (await sftpExists(sftp, path)) { - throw { code: status.ALREADY_EXISTS, message: `${path} already exists` }; + throw { code: status.ALREADY_EXISTS, message: `${path} already exists` } as ServiceError; } await sftpWriteFile(sftp)(path, Buffer.alloc(0)); @@ -102,7 +102,7 @@ export const sshFileServices = (host: string): FileOps => ({ const sftp = await ssh.requestSFTP(); if (await sftpExists(sftp, path)) { - throw { code: status.ALREADY_EXISTS, details: `${path} already exists` }; + throw { code: status.ALREADY_EXISTS, details: `${path} already exists` } as ServiceError; } await sftpMkdir(sftp)(path); @@ -117,10 +117,10 @@ export const sshFileServices = (host: string): FileOps => ({ return await sshConnect(host, userId, logger, async (ssh) => { const sftp = await ssh.requestSFTP(); - const error = await sftpRename(sftp)(fromPath, toPath).catch((e) => e); - if (error) { - throw { code: status.INTERNAL, message: "rename failed", details: error }; - } + await sftpRename(sftp)(fromPath, toPath).catch((e: unknown) => { + logger.error(e, "rename %s to %s as %s failed", fromPath, toPath, userId); + throw { code: status.INTERNAL, message: "rename failed", details: e } as ServiceError; + }); return [{}]; }); @@ -134,15 +134,15 @@ export const sshFileServices = (host: string): FileOps => ({ const stat = await sftpStat(sftp)(path).catch((e) => { logger.error(e, "stat %s as %s failed", path, userId); - throw { + throw { code: status.PERMISSION_DENIED, message: `${path} is not accessible`, - }; + } as ServiceError; }); if (!stat.isDirectory()) { - throw { + throw { code: status.INVALID_ARGUMENT, - message: `${path} is not directory or not exists` }; + message: `${path} is not directory or not exists` } as ServiceError; } const files = await sftpReaddir(sftp)(path); @@ -199,23 +199,28 @@ export const sshFileServices = (host: string): FileOps => ({ // cannot use pipeline because it forwards error // we don't want to forwards error // because the error has code property, conflicting with gRPC'S ServiceError - await pipeline( - readStream, - async (chunk) => { - return { chunk: Buffer.from(chunk) }; - }, - call, - ).catch((e) => { - throw { + try { + + await pipeline( + readStream, + async (chunk) => { + + return { chunk: Buffer.from(chunk) }; + }, + call, + ); + } catch (e) { + const ex = e as { message: string }; + throw { code: status.INTERNAL, message: "Error when reading file", - details: e?.message, - }; - }).finally(async () => { + details: ex?.message, + } as ServiceError; + } finally { readStream.close(() => {}); await once(readStream, "close"); // await promisify(readStream.close.bind(readStream))(); - }); + } return {}; }); @@ -227,15 +232,17 @@ export const sshFileServices = (host: string): FileOps => ({ return await sshConnect(host, userId, logger, async (ssh) => { const sftp = await ssh.requestSFTP(); - class RequestError { + class RequestError extends Error { constructor( public code: ServiceError["code"], public message: ServiceError["message"], public details?: ServiceError["details"], - ) {} + ) { + super(message); + } toServiceError(): ServiceError { - return { code: this.code, message: this.message, details: this.details }; + return { code: this.code, message: this.message, details: this.details } as ServiceError; } } @@ -296,9 +303,9 @@ export const sshFileServices = (host: string): FileOps => ({ const stat = await sftpStat(sftp)(path).catch((e) => { logger.error(e, "stat %s as %s failed", path, userId); - throw { + throw { code: status.PERMISSION_DENIED, message: `${path} is not accessible`, - }; + } as ServiceError; }); return { size: stat.size, type: stat.isDirectory() ? "dir" : "file" }; @@ -314,4 +321,4 @@ export const sshFileServices = (host: string): FileOps => ({ return { exists }; }); }, -}); \ No newline at end of file +}); diff --git a/apps/portal-server/src/clusterops/job.ts b/apps/portal-server/src/clusterops/job.ts index 3ecf5dc0ac..bcae2bc14d 100644 --- a/apps/portal-server/src/clusterops/job.ts +++ b/apps/portal-server/src/clusterops/job.ts @@ -51,7 +51,7 @@ export const jobOps = (cluster: string): JobOps => { const file = join(portalConfig.savedJobsDir, id); if (!await sftpExists(sftp, file)) { - throw { code: Status.NOT_FOUND, message: `Job template id ${id} is not found.` }; + throw { code: Status.NOT_FOUND, message: `Job template id ${id} is not found.` } as ServiceError; } const content = await sftpReadFile(sftp)(file); @@ -119,7 +119,7 @@ export const jobOps = (cluster: string): JobOps => { const file = join(portalConfig.savedJobsDir, id); if (!await sftpExists(sftp, file)) { - throw { code: Status.NOT_FOUND, message: `Job template id ${id} is not found.` }; + throw { code: Status.NOT_FOUND, message: `Job template id ${id} is not found.` } as ServiceError; } await sftpUnlink(sftp)(file); @@ -138,7 +138,7 @@ export const jobOps = (cluster: string): JobOps => { const file = join(portalConfig.savedJobsDir, id); if (!await sftpExists(sftp, file)) { - throw { code: Status.NOT_FOUND, message: `Job template id ${id} is not found.` }; + throw { code: Status.NOT_FOUND, message: `Job template id ${id} is not found.` } as ServiceError; } const content = await sftpReadFile(sftp)(file); diff --git a/apps/portal-server/src/index.ts b/apps/portal-server/src/index.ts index 75719c91d9..7fd8911473 100644 --- a/apps/portal-server/src/index.ts +++ b/apps/portal-server/src/index.ts @@ -19,4 +19,4 @@ async function main() { await server.start(); } -main(); +void main(); diff --git a/apps/portal-server/src/services/app.ts b/apps/portal-server/src/services/app.ts index aecfa4a6f7..c03cf101da 100644 --- a/apps/portal-server/src/services/app.ts +++ b/apps/portal-server/src/services/app.ts @@ -53,35 +53,35 @@ export const appServiceServer = plugin((server) => { const app = apps[reply.appId]; if (!app) { - throw { code: Status.NOT_FOUND, message: `app id ${reply.appId} is not found` }; + throw { code: Status.NOT_FOUND, message: `app id ${reply.appId} is not found` } as ServiceError; } let appProps: ConnectToAppResponse["appProps"]; switch (app.type) { - case AppType.vnc: - appProps = { - $case: "vnc", - vnc: {}, - }; - break; - case AppType.web: - appProps = { - $case: "web", - web: { - formData: app.web!.connect.formData ?? {}, - query: app.web!.connect.query ?? {}, - method: app.web!.connect.method, - path: app.web!.connect.path, - proxyType: app.web!.proxyType === "absolute" - ? WebAppProps_ProxyType.ABSOLUTE - : WebAppProps_ProxyType.RELATIVE, - customFormData: reply.customFormData ?? {}, - }, - }; - break; - default: - throw new Error(`Unknown app type ${app.type} of app id ${reply.appId}`); + case AppType.vnc: + appProps = { + $case: "vnc", + vnc: {}, + }; + break; + case AppType.web: + appProps = { + $case: "web", + web: { + formData: app.web!.connect.formData ?? {}, + query: app.web!.connect.query ?? {}, + method: app.web!.connect.method, + path: app.web!.connect.path, + proxyType: app.web!.proxyType === "absolute" + ? WebAppProps_ProxyType.ABSOLUTE + : WebAppProps_ProxyType.RELATIVE, + customFormData: reply.customFormData ?? {}, + }, + }; + break; + default: + throw new Error(`Unknown app type ${app.type as string} of app id ${reply.appId}`); } return [{ @@ -120,40 +120,40 @@ export const appServiceServer = plugin((server) => { } switch (attribute.type) { - case "number": - if (customAttributes[attribute.name] && Number.isNaN(Number(customAttributes[attribute.name]))) { - throw new DetailedError({ - code: Status.INVALID_ARGUMENT, - message: ` + case "number": + if (customAttributes[attribute.name] && Number.isNaN(Number(customAttributes[attribute.name]))) { + throw new DetailedError({ + code: Status.INVALID_ARGUMENT, + message: ` custom form attribute ${attribute.name} should be of type number, but of type ${typeof customAttributes[attribute.name]}`, - details: [errorInfo("INVALID ARGUMENT")], - }); - } - break; + details: [errorInfo("INVALID ARGUMENT")], + }); + } + break; - case "text": - break; + case "text": + break; - case "select": + case "select": // check the option selected by user is in select attributes as the config defined - if (customAttributes[attribute.name] + if (customAttributes[attribute.name] && !(attribute.select!.some((optionItem) => optionItem.value === customAttributes[attribute.name]))) { - throw new DetailedError({ - code: Status.INVALID_ARGUMENT, - message: ` + throw new DetailedError({ + code: Status.INVALID_ARGUMENT, + message: ` the option value of ${attribute.name} selected by user should be one of select attributes as the ${appId} config defined, but is ${customAttributes[attribute.name]}`, - details: [errorInfo("INVALID ARGUMENT")], - }); - } - break; + details: [errorInfo("INVALID ARGUMENT")], + }); + } + break; - default: - throw new Error(` + default: + throw new Error(` the custom form attributes type in ${appId} config should be one of number, text or select, - but the type of ${attribute.name} is ${attribute.type}`); + but the type of ${attribute.name} is ${attribute.type as string}`); } }); @@ -204,7 +204,7 @@ export const appServiceServer = plugin((server) => { const app = apps[appId]; if (!app) { - throw { code: Status.NOT_FOUND, message: `app id ${appId} is not found` }; + throw { code: Status.NOT_FOUND, message: `app id ${appId} is not found` } as ServiceError; } const attributes: AppCustomAttribute[] = []; @@ -225,7 +225,7 @@ export const appServiceServer = plugin((server) => { name: item.name, required: item.required, defaultInput: defaultInput, - placeholder: getI18nSeverTypeFormat(item.placeholder), + placeholder: item.placeholder ? getI18nSeverTypeFormat(item.placeholder) : undefined, options: item.select?.map((x) => { return { value: x.value, @@ -251,8 +251,7 @@ export const appServiceServer = plugin((server) => { return [{ apps: Object.keys(apps) - .map((x) => ({ id: x, name: apps[x].name, logoPath: apps[x].logoPath || undefined })), - }]; + .map((x) => ({ id: x, name: apps[x].name, logoPath: apps[x].logoPath })) }]; }, getAppLastSubmission: async ({ request, logger }) => { diff --git a/apps/portal-server/src/services/config.ts b/apps/portal-server/src/services/config.ts index 7c8b2b2bf2..9c036ae646 100644 --- a/apps/portal-server/src/services/config.ts +++ b/apps/portal-server/src/services/config.ts @@ -56,7 +56,10 @@ export const staticConfigServiceServer = plugin((server) => { ); availablePartitions = resp.partitions; } catch (error) { - logger.error(`Error occured when query the available partitions of ${userId} in ${accountName}.`); + logger.error( + "Error occured when query the available partitions of %s in %s. Error %o", + userId, accountName, error, + ); availablePartitions = []; } diff --git a/apps/portal-server/src/services/dashboard.ts b/apps/portal-server/src/services/dashboard.ts index da61ab33dd..3991c4e1cf 100644 --- a/apps/portal-server/src/services/dashboard.ts +++ b/apps/portal-server/src/services/dashboard.ts @@ -42,10 +42,10 @@ export const dashboardServiceServer = plugin((server) => { // 其他错误则抛错 logger.info("Read file failed with %o", error); - throw { + throw { code: status.INTERNAL, message: `read file ${userId}'s quickEntries.json failed`, - }; + } as ServiceError; } return [{ @@ -76,10 +76,10 @@ export const dashboardServiceServer = plugin((server) => { : ""; logger.info("Saving file failed with %o", err); - throw { + throw { code: status.INTERNAL, message: errorMessage || `An error occurred while saving quick entry for user ${userId}`, - }; + } as ServiceError; } }, }); diff --git a/apps/portal-server/src/services/desktop.ts b/apps/portal-server/src/services/desktop.ts index 5e7acf9131..d26c72e45f 100644 --- a/apps/portal-server/src/services/desktop.ts +++ b/apps/portal-server/src/services/desktop.ts @@ -34,7 +34,7 @@ export const desktopServiceServer = plugin((server) => { const availableWms = getDesktopConfig(cluster).wms; if (availableWms.find((x) => x.wm === wm) === undefined) { - throw { code: Status.INVALID_ARGUMENT, message: `${wm} is not a acceptable wm.` }; + throw { code: Status.INVALID_ARGUMENT, message: `${wm} is not a acceptable wm.` } as ServiceError; } checkLoginNodeInCluster(cluster, host); @@ -44,7 +44,7 @@ export const desktopServiceServer = plugin((server) => { const reply = await clusterops.desktop.createDesktop({ loginNode: host, wm, userId, desktopName }, logger); return [{ ...reply }]; - + }, killDesktop: async ({ request, logger }) => { diff --git a/apps/portal-server/src/services/file.ts b/apps/portal-server/src/services/file.ts index 7256dfaa1b..efe361e467 100644 --- a/apps/portal-server/src/services/file.ts +++ b/apps/portal-server/src/services/file.ts @@ -167,13 +167,13 @@ export const fileServiceServer = plugin((server) => { const info = await call.readAsync(); if (info?.message?.$case !== "info") { - throw { + throw { code: status.INVALID_ARGUMENT, message: "The first message is not file info", - }; + } as ServiceError; } - const { cluster, path, userId } = info.message?.info; + const { cluster, path, userId } = info.message.info; const host = getClusterLoginNode(cluster); @@ -254,11 +254,11 @@ export const fileServiceServer = plugin((server) => { const resp = await loggedExec(ssh, logger, true, cmd, args); if (resp.code !== 0) { - throw { + throw { code: status.INTERNAL, message: "scow-sync-start command failed", details: resp.stderr, - }; + } as ServiceError; } return [{}]; }); @@ -276,11 +276,11 @@ export const fileServiceServer = plugin((server) => { const resp = await loggedExec(ssh, logger, true, cmd, []); if (resp.code !== 0) { - throw { + throw { code: status.INTERNAL, message: "scow-sync-query command failed", details: resp.stderr, - }; + } as ServiceError; } interface TransferInfosJson { @@ -293,7 +293,7 @@ export const fileServiceServer = plugin((server) => { } // 解析scow-sync-query返回的json数组 - const transferInfosJsons: TransferInfosJson[] = JSON.parse(resp.stdout); + const transferInfosJsons = JSON.parse(resp.stdout) as TransferInfosJson[]; const transferInfos: TransferInfo[] = []; // 根据host确定clusterId @@ -315,22 +315,22 @@ export const fileServiceServer = plugin((server) => { // 将json数组中的string类型解析成protos中定义的格式 let speedInKB = 0; - const speedMatch = info.speed.match(/([\d\.]+)([kMGB]?B\/s)/); + const speedMatch = /([\d.]+)([kMGB]?B\/s)/.exec(info.speed); if (speedMatch) { const speed = Number(speedMatch[1]); switch (speedMatch[2]) { - case "B/s": - speedInKB = speed / 1024; - break; - case "kB/s": - speedInKB = speed; - break; - case "MB/s": - speedInKB = speed * 1024; - break; - case "GB/s": - speedInKB = speed * 1024 * 1024; - break; + case "B/s": + speedInKB = speed / 1024; + break; + case "kB/s": + speedInKB = speed; + break; + case "MB/s": + speedInKB = speed * 1024; + break; + case "GB/s": + speedInKB = speed * 1024 * 1024; + break; } } @@ -369,11 +369,11 @@ export const fileServiceServer = plugin((server) => { const resp = await loggedExec(ssh, logger, true, cmd, args); if (resp.code !== 0) { - throw { + throw { code: status.INTERNAL, message: "scow-sync-terminate command failed", details: resp.stderr, - }; + } as ServiceError; } return [{}]; @@ -415,11 +415,11 @@ export const fileServiceServer = plugin((server) => { const resp = await loggedExec(ssh, logger, true, cmd, args); if (resp.code !== 0) { - throw { + throw { code: status.INTERNAL, message: "check the key of transferring cross clusters failed", details: resp.stderr, - }; + } as ServiceError; } const lines = resp.stdout.trim().split("\n"); const keyConfigured = lines[lines.length - 1] === "true"; @@ -452,8 +452,8 @@ export const fileServiceServer = plugin((server) => { "-C", "for scow-sync", "-f", privateKeyPath, ]; - // eslint-disable-next-line quotes - const genKeyCmd = `ssh-keygen -N ""`; + + const genKeyCmd = "ssh-keygen -N \"\""; await loggedExec(ssh, logger, true, genKeyCmd, genKeyArgs); // 读公钥 @@ -487,4 +487,4 @@ export const fileServiceServer = plugin((server) => { return [{}]; }, }); -}); \ No newline at end of file +}); diff --git a/apps/portal-server/src/services/job.ts b/apps/portal-server/src/services/job.ts index 663d4729f0..e30c15df75 100644 --- a/apps/portal-server/src/services/job.ts +++ b/apps/portal-server/src/services/job.ts @@ -250,18 +250,18 @@ export const jobServiceServer = plugin((server) => { cluster, logger, async (client) => await asyncClientCall(client.job, "submitJob", { - userId, jobName, account, partition: partition!, qos, nodeCount, gpuCount: gpuCount || 0, + userId, jobName, account, partition: partition, qos, nodeCount, gpuCount: gpuCount ?? 0, memoryMb: Number(memory?.split("M")[0]), coreCount, timeLimitMinutes: maxTimeConversion, script: command, workingDirectory, stdout: output, stderr: errorOutput, extraOptions: [], }).catch((e) => { const ex = e as ServiceError; const errors = parseErrorDetails(ex.metadata); if (errors[0] && errors[0].$type === "google.rpc.ErrorInfo" && errors[0].reason === "SBATCH_FAILED") { - throw { + throw { code: Status.INTERNAL, message: "sbatch failed", - details: e.details, - }; + details: ex.details, + } as ServiceError; } else { throw e; } @@ -324,25 +324,25 @@ export const jobServiceServer = plugin((server) => { // 判断文件操作权限 const stat = await sftpStat(sftp)(filePath).catch((e) => { logger.error(e, "stat %s as %s failed", filePath, userId); - throw { + throw { code: Status.PERMISSION_DENIED, message: `${filePath} is not accessible`, - }; + } as ServiceError; }); // 文件SIZE大于1M不能提交sbatch执行 if (stat.size / (1024 * 1024) > 1) { - throw { + throw { code: Status.INVALID_ARGUMENT, message: `${filePath} is too large. Maximum file size is 1M`, - }; + } as ServiceError; } const isTextFile = await ssh.exec("file", [filePath]).then((res) => { - return res.match(/text/); + return /text/.exec(res); }); // 文件不是文本文件不能提交Sbatch执行 if (!isTextFile) { - throw { + throw { code: Status.INVALID_ARGUMENT, message: `${filePath} is not a text file`, - }; + } as ServiceError; } return await sftpReadFile(sftp)(filePath) @@ -370,11 +370,11 @@ export const jobServiceServer = plugin((server) => { const ex = e as ServiceError; const errors = parseErrorDetails(ex.metadata); if (errors[0] && errors[0].$type === "google.rpc.ErrorInfo" && errors[0].reason === "SBATCH_FAILED") { - throw { + throw { code: Status.INTERNAL, message: "sbatch failed", - details: e.details, - }; + details: ex.details, + } as ServiceError; } else { throw e; } diff --git a/apps/portal-server/src/services/shell.ts b/apps/portal-server/src/services/shell.ts index 335ecdc2eb..b0b0a6ab8f 100644 --- a/apps/portal-server/src/services/shell.ts +++ b/apps/portal-server/src/services/shell.ts @@ -28,10 +28,10 @@ export const shellServiceServer = plugin((server) => { const firstMessage = await call.readAsync(); if (firstMessage?.message?.$case !== "connect") { - throw { + throw { code: status.INVALID_ARGUMENT, message: "The first message is not connect", - }; + } as ServiceError; } const { message: { connect } } = firstMessage; @@ -85,10 +85,10 @@ export const shellServiceServer = plugin((server) => { async function(req) { if (!req.message) { - throw { + throw { code: status.INVALID_ARGUMENT, message: "Received a request without message", - }; + } as ServiceError; } if (req.message.$case === "resize") { @@ -108,10 +108,10 @@ export const shellServiceServer = plugin((server) => { return req.message.data.data; } - throw { + throw { code: status.INVALID_ARGUMENT, message: `Received unexpected message type ${req.message.$case}`, - }; + } as ServiceError; }, channel, ), diff --git a/apps/portal-server/src/utils/app.ts b/apps/portal-server/src/utils/app.ts index a315259f71..62469aa9d9 100644 --- a/apps/portal-server/src/utils/app.ts +++ b/apps/portal-server/src/utils/app.ts @@ -29,7 +29,7 @@ export const getClusterAppConfigs = (cluster: string) => { const clusterAppsConfigs = getAppConfigs(join(DEFAULT_CONFIG_BASE_PATH, "clusters/", cluster)); - const apps = {}; + const apps = {} as Record; for (const [key, value] of Object.entries(commonApps)) { apps[key] = value; diff --git a/apps/portal-server/src/utils/clusters.ts b/apps/portal-server/src/utils/clusters.ts index 17393278d9..f7498ce25a 100644 --- a/apps/portal-server/src/utils/clusters.ts +++ b/apps/portal-server/src/utils/clusters.ts @@ -64,7 +64,7 @@ export const callOnOne: CallOnOne = async (cluster, logger, call) => { clusterId: cluster, details: errorDetail, }]; - const reason = "Cluster ID : " + cluster + ", Details : " + errorDetail; + const reason = "Cluster ID : " + cluster + ", Details : " + errorDetail.toString(); // 统一错误处理 if (e instanceof Error) { @@ -83,7 +83,7 @@ export const callOnOne: CallOnOne = async (cluster, logger, call) => { export const checkActivatedClusters = async ( - { clusterIds }: {clusterIds: string[] | string}, + { clusterIds }: { clusterIds: string[] | string }, ) => { if (!config.MIS_DEPLOYED) { diff --git a/apps/portal-server/src/utils/desktops.ts b/apps/portal-server/src/utils/desktops.ts index 42865bc70f..ca4ebd1ce4 100644 --- a/apps/portal-server/src/utils/desktops.ts +++ b/apps/portal-server/src/utils/desktops.ts @@ -32,7 +32,7 @@ export function ensureEnabled(cluster: string) { const enabled = getDesktopConfig(cluster).enabled; if (!enabled) { - throw { code: Status.UNAVAILABLE, message: "Login desktop is not enabled" }; + throw { code: Status.UNAVAILABLE, message: "Login desktop is not enabled" } as ServiceError; } } diff --git a/apps/portal-server/src/utils/errors.ts b/apps/portal-server/src/utils/errors.ts index a385efac73..81b3bb5d36 100644 --- a/apps/portal-server/src/utils/errors.ts +++ b/apps/portal-server/src/utils/errors.ts @@ -14,27 +14,27 @@ import { ServiceError } from "@grpc/grpc-js"; import { Status } from "@grpc/grpc-js/build/src/constants"; export const scowdClientNotFound = (cluster: string) => { - return { code: Status.NOT_FOUND, message: `The scowd client on cluster ${cluster} was not found` }; + return { code: Status.NOT_FOUND, message: `The scowd client on cluster ${cluster} was not found` } as ServiceError; }; export const clusterNotFound = (cluster: string) => { - return { code: Status.NOT_FOUND, message: `cluster ${cluster} is not found` }; + return { code: Status.NOT_FOUND, message: `cluster ${cluster} is not found` } as ServiceError; }; export const jobNotFound = (jobId: number) => { - return { code: Status.NOT_FOUND, message: `job id ${jobId} is not found` }; + return { code: Status.NOT_FOUND, message: `job id ${jobId} is not found` } as ServiceError; }; export const loginNodeNotFound = (loginNode: string) => { - return { code: Status.NOT_FOUND, message: `login node ${loginNode} is not found` }; + return { code: Status.NOT_FOUND, message: `login node ${loginNode} is not found` } as ServiceError; }; export const transferNotEnabled = (cluster: string) => { - return { + return { code: Status.INTERNAL, message: `the transmission function is not enabled for the cluster ${cluster}`, - }; + } as ServiceError; }; export const transferNodeNotFound = (cluster: string) => { - return { code: Status.NOT_FOUND, message: `transfer node of cluster ${cluster} is not found` }; + return { code: Status.NOT_FOUND, message: `transfer node of cluster ${cluster} is not found` } as ServiceError; }; diff --git a/apps/portal-server/src/utils/proxy.ts b/apps/portal-server/src/utils/proxy.ts index 5bc10eed42..1e6e265df2 100644 --- a/apps/portal-server/src/utils/proxy.ts +++ b/apps/portal-server/src/utils/proxy.ts @@ -79,7 +79,7 @@ server { export const parseIp = (stdout: string): string => { const ipReg = /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/; - return stdout.split("\n")[0]?.match(ipReg)?.[0] || ""; + return (ipReg.exec(stdout.split("\n")[0]))?.[0] ?? ""; }; export const getIpFromProxyGateway diff --git a/apps/portal-server/src/utils/scowd.ts b/apps/portal-server/src/utils/scowd.ts index 298626f8db..e0dda7beff 100644 --- a/apps/portal-server/src/utils/scowd.ts +++ b/apps/portal-server/src/utils/scowd.ts @@ -62,51 +62,51 @@ export function getLoginNodeFromAddress(cluster: string, address: string) { // 映射 tRPC 状态码到 gRPC 状态码的函数 function mapTRPCStatusToGRPC(statusCode: Code): status { switch (statusCode) { - case Code.Canceled: - return status.CANCELLED; - case Code.Unknown: - return status.UNKNOWN; - case Code.InvalidArgument: - return status.INVALID_ARGUMENT; - case Code.DeadlineExceeded: - return status.DEADLINE_EXCEEDED; - case Code.NotFound: - return status.NOT_FOUND; - case Code.AlreadyExists: - return status.ALREADY_EXISTS; - case Code.PermissionDenied: - return status.PERMISSION_DENIED; - case Code.ResourceExhausted: - return status.RESOURCE_EXHAUSTED; - case Code.FailedPrecondition: - return status.FAILED_PRECONDITION; - case Code.Aborted: - return status.ABORTED; - case Code.OutOfRange: - return status.OUT_OF_RANGE; - case Code.Unimplemented: - return status.UNIMPLEMENTED; - case Code.Internal: - return status.INTERNAL; - case Code.Unavailable: - return status.UNAVAILABLE; - case Code.DataLoss: - return status.DATA_LOSS; - case Code.Unauthenticated: - return status.UNAUTHENTICATED; - default: - return status.OK; + case Code.Canceled: + return status.CANCELLED; + case Code.Unknown: + return status.UNKNOWN; + case Code.InvalidArgument: + return status.INVALID_ARGUMENT; + case Code.DeadlineExceeded: + return status.DEADLINE_EXCEEDED; + case Code.NotFound: + return status.NOT_FOUND; + case Code.AlreadyExists: + return status.ALREADY_EXISTS; + case Code.PermissionDenied: + return status.PERMISSION_DENIED; + case Code.ResourceExhausted: + return status.RESOURCE_EXHAUSTED; + case Code.FailedPrecondition: + return status.FAILED_PRECONDITION; + case Code.Aborted: + return status.ABORTED; + case Code.OutOfRange: + return status.OUT_OF_RANGE; + case Code.Unimplemented: + return status.UNIMPLEMENTED; + case Code.Internal: + return status.INTERNAL; + case Code.Unavailable: + return status.UNAVAILABLE; + case Code.DataLoss: + return status.DATA_LOSS; + case Code.Unauthenticated: + return status.UNAUTHENTICATED; + default: + return status.OK; } } // 映射 tRPC 异常到 gRPC 异常的函数 export function mapTRPCExceptionToGRPC(err: any): ServiceError { if (err instanceof ConnectError) { - return { code: mapTRPCStatusToGRPC(err.code), details: err.message }; + return { code: mapTRPCStatusToGRPC(err.code), details: err.message } as ServiceError; } - return { + return { code: status.UNKNOWN, details: "An unknown error occurred.", - }; + } as ServiceError; } diff --git a/apps/portal-server/src/utils/ssh.ts b/apps/portal-server/src/utils/ssh.ts index 5cd734f334..39f225d6aa 100644 --- a/apps/portal-server/src/utils/ssh.ts +++ b/apps/portal-server/src/utils/ssh.ts @@ -136,7 +136,7 @@ export async function checkClustersRootUserLogin(logger: Logger) { /** * Check whether login node is in current cluster */ -export async function checkLoginNodeInCluster(cluster: string, loginNode: string) { +export function checkLoginNodeInCluster(cluster: string, loginNode: string) { const loginNodes = configClusters[cluster]?.loginNodes.map(getLoginNode); if (!loginNodes) { throw clusterNotFound(cluster); diff --git a/apps/portal-server/src/utils/turbovnc.ts b/apps/portal-server/src/utils/turbovnc.ts index 0ad0ba994d..b06067a18b 100644 --- a/apps/portal-server/src/utils/turbovnc.ts +++ b/apps/portal-server/src/utils/turbovnc.ts @@ -24,6 +24,7 @@ export function getTurboVNCPath(cluster: string) { const clusterTurboVNCPath = getClusterConfigs(undefined, undefined, ["hpc"])[cluster].turboVNCPath; + return clusterTurboVNCPath || commonTurboVNCPath; } @@ -69,7 +70,7 @@ export function parseDisplayId(stdout: string): number { const lines = stdout.split("\n"); for (const line of lines) { - const matches = line.match(regex); + const matches = regex.exec(line); if (!matches) { continue; } return +matches[1]; diff --git a/apps/portal-server/tests/app/createAppSession.test.ts b/apps/portal-server/tests/app/createAppSession.test.ts index 4ab94fc14a..6a3192afd8 100644 --- a/apps/portal-server/tests/app/createAppSession.test.ts +++ b/apps/portal-server/tests/app/createAppSession.test.ts @@ -46,7 +46,8 @@ it("create app with wrong argument", async () => { qos: "high", proxyBasePath: "/api/proxy", customAttributes: { version5: "abc" }, - }).catch((e) => e); - expect(reply.code).toBe(status.INVALID_ARGUMENT); + }).catch((e) => e as { code: number }); + + expect((reply as { code: number }).code).toBe(status.INVALID_ARGUMENT); }); diff --git a/apps/portal-server/tests/app/getAppMetadata.test.ts b/apps/portal-server/tests/app/getAppMetadata.test.ts index a444a3802f..d4d1830138 100644 --- a/apps/portal-server/tests/app/getAppMetadata.test.ts +++ b/apps/portal-server/tests/app/getAppMetadata.test.ts @@ -19,19 +19,19 @@ import { createServer } from "src/app"; import { getI18nTypeFormat } from "tests/file/utils"; export interface SelectOption { - value: string; - label: I18nStringType; + value: string; + label: I18nStringType; } interface AppCustomAttribute { - type: "NUMBER" | "SELECT" | "TEXT"; - label: I18nStringType; - name: string; - required: boolean; - placeholder?: I18nStringType | undefined; - default?: string | number | undefined; - select: SelectOption[]; - } + type: "NUMBER" | "SELECT" | "TEXT"; + label: I18nStringType; + name: string; + required: boolean; + placeholder?: I18nStringType | undefined; + default?: string | number | undefined; + select: SelectOption[]; +} // import { actualPath, cluster, connectToTestServer, // createTestItems, expectGrpcThrow, resetTestServer, TestSshServer, userId } from "./utils"; diff --git a/apps/portal-server/tests/utils/desktop.test.ts b/apps/portal-server/tests/utils/desktop.test.ts index ed6161d887..1456ef72af 100644 --- a/apps/portal-server/tests/utils/desktop.test.ts +++ b/apps/portal-server/tests/utils/desktop.test.ts @@ -64,6 +64,7 @@ const addedDesktopInfo: DesktopInfo = { jest.mock("@scow/lib-ssh", () => { const originalModule = jest.requireActual("@scow/lib-ssh"); + return { __esModule: true, ...originalModule, @@ -203,9 +204,10 @@ it("return cluster logindesktop enabled when setting enabled both in portal and try { ensureEnabled(testCluster); expect("").fail("not enabled"); - } catch (e: any) { - expect(e.code).toBe(Status.UNAVAILABLE); - expect(e.message).toContain("Login desktop is not enabled"); + } catch (e: unknown) { + const ex = e as { code: Status; message: string }; + expect(ex.code).toBe(Status.UNAVAILABLE); + expect(ex.message).toContain("Login desktop is not enabled"); } }); diff --git a/apps/portal-web/.eslintrc.json b/apps/portal-web/.eslintrc.json deleted file mode 100644 index f1128f5d1b..0000000000 --- a/apps/portal-web/.eslintrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": [ - "@ddadaal/eslint-config/react", - "../../.eslintrc.js" - ], - "ignorePatterns": ["public/monaco-assets/**"] -} diff --git a/apps/portal-web/eslint.config.js b/apps/portal-web/eslint.config.js new file mode 100644 index 0000000000..2330ab5625 --- /dev/null +++ b/apps/portal-web/eslint.config.js @@ -0,0 +1,12 @@ +const base = require("../../eslint.config"); +const react = require("@ddadaal/eslint-config/react"); + +module.export = [ + { + ...base, + ...react, + }, + { + ignores: ["public/monaco-assets/**"] + }, +]; diff --git a/apps/portal-web/next.config.js b/apps/portal-web/next.config.js index 2614091fd1..a8f8accc21 100644 --- a/apps/portal-web/next.config.js +++ b/apps/portal-web/next.config.js @@ -10,8 +10,6 @@ * See the Mulan PSL v2 for more details. */ -/* eslint-disable @typescript-eslint/no-var-requires */ - // @ts-check const withPlugins = require("next-compose-plugins"); diff --git a/apps/portal-web/package.json b/apps/portal-web/package.json index 746e5dd527..f4791eefa0 100644 --- a/apps/portal-web/package.json +++ b/apps/portal-web/package.json @@ -10,7 +10,8 @@ "build:next": "cross-env BUILDING=1 next build", "test": "jest --passWithNoTests", "client": "ntar client", - "prepareDev": "node scripts/copyMonacoToPublic.js" + "prepareDev": "node scripts/copyMonacoToPublic.js", + "lint": "eslint ." }, "files": [ ".next", diff --git a/dev/test-adapter/Dockerfile b/dev/test-adapter/Dockerfile index b0d5f31e9c..1215c39615 100644 --- a/dev/test-adapter/Dockerfile +++ b/dev/test-adapter/Dockerfile @@ -39,7 +39,7 @@ COPY --from=pruner /app/out/json/ . RUN pnpm i --offline --frozen-lockfile COPY --from=pruner /app/out/full/ . -COPY tsconfig.json .eslintrc.js turbo.json ./ +COPY tsconfig.json turbo.json ./ COPY libs/tsconfig.json ./libs/ COPY protos ./protos COPY scripts/ ./scripts diff --git a/dev/test-adapter/package.json b/dev/test-adapter/package.json index fcd6d6c057..0d0e5db9f1 100644 --- a/dev/test-adapter/package.json +++ b/dev/test-adapter/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "node -r ts-node/register -r tsconfig-paths/register --watch src/index.ts", "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", - "serve": "node build/index.js" + "serve": "node build/index.js", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "scripts", diff --git a/dev/test-adapter/src/index.ts b/dev/test-adapter/src/index.ts index 7f2eb0f2f5..13962b2019 100644 --- a/dev/test-adapter/src/index.ts +++ b/dev/test-adapter/src/index.ts @@ -20,4 +20,4 @@ async function main() { } -main(); +void main(); diff --git a/docker/Dockerfile.docs b/docker/Dockerfile.docs index 71bcf762e2..ac4450ba0d 100644 --- a/docker/Dockerfile.docs +++ b/docker/Dockerfile.docs @@ -37,7 +37,7 @@ COPY --from=pruner /app/out/json/ . RUN pnpm i --offline --frozen-lockfile COPY --from=pruner /app/out/full/ . -COPY tsconfig.json .eslintrc.js turbo.json ./ +COPY tsconfig.json turbo.json ./ COPY libs/tsconfig.json ./libs/ COPY protos ./protos diff --git a/docs/package.json b/docs/package.json index 249c22bd4f..75dd9eaee8 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,7 +15,8 @@ "typecheck": "tsc", "genDocs": "pnpm writeEnvDocs && pnpm writeConfigDocs", "writeEnvDocs": "ts-node tools/envDoc.ts", - "writeConfigDocs": "node tools/configDoc.mjs" + "writeConfigDocs": "node tools/configDoc.mjs", + "lint": "eslint -c ../eslint.config.js ." }, "dependencies": { "@docusaurus/core": "3.4.0", diff --git a/docs/src/components/HomepageFeatures.tsx b/docs/src/components/HomepageFeatures.tsx index a0f0c277df..d9fbba63f1 100644 --- a/docs/src/components/HomepageFeatures.tsx +++ b/docs/src/components/HomepageFeatures.tsx @@ -17,11 +17,11 @@ import React from "react"; import styles from "./HomepageFeatures.module.css"; -type FeatureItem = { +interface FeatureItem { title: string; image: string; description: JSX.Element; -}; +} const FeatureList: FeatureItem[] = [ { diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index b3d68936ff..1c867fba96 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -31,7 +31,7 @@ function HomepageHeader() { - 简介 + 简介 diff --git a/docs/tools/envDoc.ts b/docs/tools/envDoc.ts index 10e73b0aec..17a8c63e41 100644 --- a/docs/tools/envDoc.ts +++ b/docs/tools/envDoc.ts @@ -28,8 +28,6 @@ const docNames = { [regex.name]: "正则表达式", }; -debugger; - export function getDocFromSpec(spec: Record>) { let docs = ` @@ -45,7 +43,8 @@ export function getDocFromSpec(spec: Record>) { `\`${key}\``, docNames[func.name], // @ts-ignore - // eslint-disable-next-line max-len + + // eslint-disable-next-line @stylistic/max-len `${spec.desc ? spec.desc.replaceAll("\n", "
") : ""}${spec.choices ? "
可选项:" + spec.choices.join(",") : ""}${spec.example ? "
示例:" + spec.example : ""}`, "default" in spec ? (spec.default === undefined @@ -65,9 +64,10 @@ export function genConfigTable(pack: string, configFile = "src/config/env") { const appPath = resolve(join("../apps", pack)); dotenv.config({ path: join(appPath, "env/.env.dev") }); - // eslint-disable-next-line @typescript-eslint/no-var-requires + const { config } = require(join(appPath, configFile)); + const docs = getDocFromSpec(config._specs); return docs; diff --git a/docs/tsconfig.json b/docs/tsconfig.json index b651133d71..61428ebcfb 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -4,4 +4,7 @@ "compilerOptions": { "baseUrl": ".", }, + "include": [ + "tools/envDoc.ts" + ] } diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000000..056b248944 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy + * SCOW is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +const path = require("path"); + +const base = require("@ddadaal/eslint-config"); +const licenseHeader = require("eslint-plugin-license-header"); + +module.exports = [ + { + ignores: [ + "**/node_modules/", + "**/build/", + "**/coverage/", + "**/next-env.d.ts", + "**/generated/", + "**/.turbo/", + "**/.next", + "**/.docusaurus/", + ] + }, + ...base, + { + plugins: { + "license-header": licenseHeader, + }, + rules: { + "license-header/header": [ + "error", + path.join(__dirname, "license-header"), + ], + } + }, + { + rules: { + "@typescript-eslint/no-require-imports": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-empty-object-type": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/prefer-nullish-coalescing": "off", + "require-await": "off", + "@typescript-eslint/require-await": "off" + } + } + +]; diff --git a/libs/auth/package.json b/libs/auth/package.json index b9b3148fcc..29d34d51d7 100644 --- a/libs/auth/package.json +++ b/libs/auth/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/auth/src/getUser.ts b/libs/auth/src/getUser.ts index 81be49284d..4e6e2b41d2 100644 --- a/libs/auth/src/getUser.ts +++ b/libs/auth/src/getUser.ts @@ -39,7 +39,7 @@ export async function getUser( }); if (resp.status === 200) { - return (await resp.json()).user; + return (await resp.json()).user as AuthUserInfo; } else if (resp.status === 404) { const json = await resp.json().catch(() => undefined); diff --git a/libs/config/package.json b/libs/config/package.json index ae442daf17..2ce37879fa 100644 --- a/libs/config/package.json +++ b/libs/config/package.json @@ -5,7 +5,8 @@ "description": "", "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", - "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json" + "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/config/src/app.ts b/libs/config/src/app.ts index 52a33f180a..3a4f042d5a 100644 --- a/libs/config/src/app.ts +++ b/libs/config/src/app.ts @@ -28,7 +28,7 @@ export type AppConnectPropsSchema = Static; export enum AppType { web = "web", - vnc = "vnc" + vnc = "vnc", } export const WebAppConfigSchema = Type.Object({ diff --git a/libs/config/src/appForAi.ts b/libs/config/src/appForAi.ts index 043f32b705..f439d7f112 100644 --- a/libs/config/src/appForAi.ts +++ b/libs/config/src/appForAi.ts @@ -18,7 +18,7 @@ import { createI18nStringSchema } from "src/i18n"; export enum AppType { web = "web", - vnc = "vnc" + vnc = "vnc", } export const AppConnectPropsSchema = Type.Object({ diff --git a/libs/config/src/cluster.ts b/libs/config/src/cluster.ts index 1a6268aaf6..f7972e38f5 100644 --- a/libs/config/src/cluster.ts +++ b/libs/config/src/cluster.ts @@ -31,7 +31,7 @@ const LoginNodeConfigSchema = Type.Object({ }, { description: "scowd 相关配置" })), }); -export type LoginNodeConfigSchema = Static +export type LoginNodeConfigSchema = Static; interface LoginNode { name: I18nStringType; @@ -50,7 +50,7 @@ export const getLoginNode = export type Cluster = { id: string -} & ClusterConfigSchema +} & ClusterConfigSchema; export const getSortedClusters = (clusters: Record): Cluster[] => { return Object.keys(clusters) diff --git a/libs/config/src/i18n.ts b/libs/config/src/i18n.ts index c33b155280..6fc65c840f 100644 --- a/libs/config/src/i18n.ts +++ b/libs/config/src/i18n.ts @@ -15,7 +15,9 @@ import { Type } from "@sinclair/typebox"; import { CommonConfigSchema } from "./common"; // 创建配置文件中支持国际化文本文字项的配置类型 -export const createI18nStringSchema = ({ description, defaultValue }: {description: string, defaultValue?: string}) => { +export const createI18nStringSchema = ({ description, defaultValue }: { + description: string, defaultValue?: string +}) => { return Type.Union([ Type.String(), Type.Object({ @@ -45,12 +47,16 @@ export const SYSTEM_VALID_LANGUAGES = { // 系统合法语言枚举值 export enum SYSTEM_VALID_LANGUAGE_ENUM { "zh_cn" = "zh_cn", - "en" = "en" + "en" = "en", } export type SystemLanguage = CommonConfigSchema["systemLanguage"]; -export type SystemLanguageConfig = {defaultLanguage: string, isUsingI18n: boolean, autoDetectWhenUserNotSet?: boolean} +export interface SystemLanguageConfig { + defaultLanguage: string, + isUsingI18n: boolean, + autoDetectWhenUserNotSet?: boolean +}; // 配置项文本国际化类型 @@ -60,7 +66,7 @@ export type I18nStringType = string | { en?: string, zh_cn?: string, } -} +}; export interface I18nObject { i18n?: I18nObject_I18n | undefined; diff --git a/libs/config/src/type.ts b/libs/config/src/type.ts index 33db186b3b..9a03eae03d 100644 --- a/libs/config/src/type.ts +++ b/libs/config/src/type.ts @@ -14,7 +14,7 @@ import { Static, Type } from "@sinclair/typebox"; import { I18nStringType } from "./i18n"; -export type Cluster = { id: string; name: I18nStringType; } +export interface Cluster { id: string; name: I18nStringType; }; export enum ClusterActivationStatus { ACTIVATED = 0, @@ -38,5 +38,5 @@ export type LoginNodesType = string[] | { name: I18nStringType, address: string, - }[] + }[]; diff --git a/libs/decimal/package.json b/libs/decimal/package.json index bcb8351c5d..35acf8e102 100644 --- a/libs/decimal/package.json +++ b/libs/decimal/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", "build": "rimraf build && tsc -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/decimal/tests/convertion.test.ts b/libs/decimal/tests/convertion.test.ts index 61bff91f7e..02fa416878 100644 --- a/libs/decimal/tests/convertion.test.ts +++ b/libs/decimal/tests/convertion.test.ts @@ -12,14 +12,13 @@ import { Decimal, decimalToMoney, moneyToNumber, numberToMoney } from "../src"; - interface Money { positive: boolean; yuan: number; decimalPlace: number; } -it("performs correct converions", async () => { +it("performs correct converions", () => { expect(numberToMoney(20.4)).toMatchObject({ positive: true, decimalPlace: 4000, yuan: 20 } as Money); expect(moneyToNumber({ decimalPlace: 2010, yuan: 100, positive: true })).toBe(100.201); expect(moneyToNumber({ decimalPlace: 30, yuan: 100, positive: false })).toBe(-100.003); diff --git a/libs/decimal/tests/decimal.test.ts b/libs/decimal/tests/decimal.test.ts index a7fb10f2d3..b7c947a45a 100644 --- a/libs/decimal/tests/decimal.test.ts +++ b/libs/decimal/tests/decimal.test.ts @@ -12,12 +12,12 @@ import { Decimal } from "../src"; -it("performs correct values", async () => { +it("performs correct values", () => { const expectEqual = (actual: Decimal, expected: Decimal) => { expect(actual.toNumber()).toBe(expected.toNumber()); }; - const v = (v) => new Decimal(v); + const v = (v: number) => new Decimal(v); expectEqual(v(0.1).plus(v(0.2)), v(0.3)); expectEqual(v(100000).minus(v(0.01)), v(99999.99)); diff --git a/libs/hook/package.json b/libs/hook/package.json index c7b82b3f3b..82ed22b223 100644 --- a/libs/hook/package.json +++ b/libs/hook/package.json @@ -6,7 +6,8 @@ "main": "build/index.js", "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", - "build": "rimraf build && tsc -p tsconfig.build.json" + "build": "rimraf build && tsc -p tsconfig.build.json", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/libconfig/package.json b/libs/libconfig/package.json index d42849d911..22714e2a38 100644 --- a/libs/libconfig/package.json +++ b/libs/libconfig/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/libconfig/src/envConfig.ts b/libs/libconfig/src/envConfig.ts index 749d2276ba..9d5f5b523c 100644 --- a/libs/libconfig/src/envConfig.ts +++ b/libs/libconfig/src/envConfig.ts @@ -49,11 +49,11 @@ export const num = make(envalid.num, "num"); export const bool = make(envalid.bool, "bool"); export const envConfig = ( - specs: { [K in keyof T]: Validator }, + specs: {[K in keyof T]: Validator }, envObject = process.env, ) => { - const envalidSpecs = {} as { [ K in keyof T]: ValidatorSpec }; + const envalidSpecs = {} as {[ K in keyof T]: ValidatorSpec }; for (const k in specs) { const [func, spec] = specs[k]; diff --git a/libs/libconfig/src/parse.ts b/libs/libconfig/src/parse.ts index e864923122..b4f1215636 100644 --- a/libs/libconfig/src/parse.ts +++ b/libs/libconfig/src/parse.ts @@ -34,7 +34,7 @@ export function parseKeyValue(input: string): Record { * @returns replaced string */ export function parsePlaceholder(str: string, valueObj: object, showUndefined: boolean = false) { - return str.replace(/\{\{\ ([a-zA-Z0-9_]+)\ \}\}/g, (_, p1: string) => valueObj[p1] ?? (showUndefined ? "-" : "")); + return str.replace(/\{\{ ([a-zA-Z0-9_]+) \}\}/g, (_, p1: string) => valueObj[p1] ?? (showUndefined ? "-" : "")); } /** @@ -57,7 +57,7 @@ export function parseArray(str: string): string[] { */ export function getPlaceholderKeys(str: string): string[] { - const matchValues = str.match(/\{\{\ ([a-zA-Z0-9_]+)\ \}\}/g); + const matchValues = str.match(/\{\{ ([a-zA-Z0-9_]+) \}\}/g); if (matchValues) { const texts = matchValues.map(function(val) { diff --git a/libs/operation-log/package.json b/libs/operation-log/package.json index 614074efaa..52d99703b0 100644 --- a/libs/operation-log/package.json +++ b/libs/operation-log/package.json @@ -6,7 +6,8 @@ "main": "build/index.js", "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", - "build": "rimraf build && tsc -p tsconfig.build.json" + "build": "rimraf build && tsc -p tsconfig.build.json", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/operation-log/src/constant.ts b/libs/operation-log/src/constant.ts index 6f40bcf213..5114e5c9e1 100644 --- a/libs/operation-log/src/constant.ts +++ b/libs/operation-log/src/constant.ts @@ -22,4 +22,4 @@ type ExtractCases = T extends { $case: infer U } ? U : never; export type OperationType = ExtractCases; -export type OperationTypeEnum = { [K in OperationType]: K }; +export type OperationTypeEnum = {[K in OperationType]: K }; diff --git a/libs/operation-log/src/index.ts b/libs/operation-log/src/index.ts index e893bea02a..5d6e83a752 100644 --- a/libs/operation-log/src/index.ts +++ b/libs/operation-log/src/index.ts @@ -42,7 +42,7 @@ export const createOperationLogClient = ( config: AuditConfigSchema | undefined, logger: Logger | Console, ) => { - const client = config && config.url + const client = config?.url ? new OperationLogServiceClient(config.url, ChannelCredentials.createInsecure()) : undefined; diff --git a/libs/rich-error-model/package.json b/libs/rich-error-model/package.json index a926f59da3..42a179d643 100644 --- a/libs/rich-error-model/package.json +++ b/libs/rich-error-model/package.json @@ -7,7 +7,8 @@ "scripts": { "generate": "rimraf generated && buf generate --template buf.gen.yaml https://github.com/googleapis/googleapis.git#subdir=google/rpc", "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/scheduler-adapter/package.json b/libs/scheduler-adapter/package.json index 62c189f758..692a5eaa84 100644 --- a/libs/scheduler-adapter/package.json +++ b/libs/scheduler-adapter/package.json @@ -6,7 +6,8 @@ "private": true, "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", - "build": "rimraf build && tsc -p tsconfig.build.json" + "build": "rimraf build && tsc -p tsconfig.build.json", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/scheduler-adapter/src/client.ts b/libs/scheduler-adapter/src/client.ts index 6787461c0d..2ef6512cac 100644 --- a/libs/scheduler-adapter/src/client.ts +++ b/libs/scheduler-adapter/src/client.ts @@ -40,12 +40,12 @@ export function getClient( } export const getSchedulerAdapterClient = (address: string) => { - return { + return { account: getClient(address, AccountServiceClient), user: getClient(address, UserServiceClient), job: getClient(address, JobServiceClient), config: getClient(address, ConfigServiceClient), version: getClient(address, VersionServiceClient), app: getClient(address, AppServiceClient), - }; + } as SchedulerAdapterClient; }; diff --git a/libs/scheduler-adapter/src/map.ts b/libs/scheduler-adapter/src/map.ts index 9911989d28..ba1acba7f7 100644 --- a/libs/scheduler-adapter/src/map.ts +++ b/libs/scheduler-adapter/src/map.ts @@ -18,33 +18,33 @@ import { formatTime } from "./time"; export const jobInfoToRunningjob = (jobInfo: JobInfo) => { return { - jobId: jobInfo.jobId!.toString(), - partition: jobInfo.partition!, - name: jobInfo.name!, - user: jobInfo.user!, - state: jobInfo.state!, + jobId: jobInfo.jobId.toString(), + partition: jobInfo.partition, + name: jobInfo.name, + user: jobInfo.user, + state: jobInfo.state, runningTime: jobInfo.elapsedSeconds !== undefined ? formatTime(jobInfo.elapsedSeconds * 1000) : "", nodes: jobInfo.nodesAlloc!.toString(), nodesOrReason: jobInfo.state === "RUNNING" ? jobInfo.nodeList! : jobInfo.reason, - account: jobInfo.account!, - cores: jobInfo.cpusAlloc!.toString(), - gpus: (jobInfo.gpusAlloc || 0).toString(), - qos: jobInfo.qos!, + account: jobInfo.account, + cores: (jobInfo.cpusAlloc ?? 0).toString(), + gpus: (jobInfo.gpusAlloc ?? 0).toString(), + qos: jobInfo.qos, submissionTime: jobInfo.submitTime!, timeLimit: jobInfo.timeLimitMinutes ? formatTime(jobInfo.timeLimitMinutes * 60 * 1000) : "", - workingDir: jobInfo.workingDirectory!, + workingDir: jobInfo.workingDirectory, } as RunningJob; }; export const jobInfoToPortalJobInfo = (jobInfo: JobInfo) => { return { - jobId: jobInfo.jobId!, - name: jobInfo.name!, - account: jobInfo.account!, - partition: jobInfo.partition!, - qos: jobInfo.qos!, - state: jobInfo.state!, - workingDirectory: jobInfo.workingDirectory!, + jobId: jobInfo.jobId, + name: jobInfo.name, + account: jobInfo.account, + partition: jobInfo.partition, + qos: jobInfo.qos, + state: jobInfo.state, + workingDirectory: jobInfo.workingDirectory, reason: jobInfo.reason, elapsed: jobInfo.elapsedSeconds !== undefined ? formatTime(jobInfo.elapsedSeconds * 1000) : "", timeLimit: jobInfo.timeLimitMinutes ? formatTime(jobInfo.timeLimitMinutes * 60 * 1000) : "", diff --git a/libs/scowd/package.json b/libs/scowd/package.json index dc89ccddee..0ab669d4b7 100644 --- a/libs/scowd/package.json +++ b/libs/scowd/package.json @@ -6,7 +6,8 @@ "private": true, "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", - "build": "rimraf build && tsc -p tsconfig.build.json" + "build": "rimraf build && tsc -p tsconfig.build.json", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/scowd/src/client.ts b/libs/scowd/src/client.ts index 8fb80523ab..9a8424a40f 100644 --- a/libs/scowd/src/client.ts +++ b/libs/scowd/src/client.ts @@ -37,8 +37,8 @@ export function getClient( } export const getScowdClient = (scowdUrl: string, certificates?: SslConfig) => { - return { + return { file: getClient(scowdUrl, FileService, certificates), desktop: getClient(scowdUrl, DesktopService, certificates), - }; + } as ScowdClient; }; diff --git a/libs/server/package.json b/libs/server/package.json index 00cedc4410..77356ec462 100644 --- a/libs/server/package.json +++ b/libs/server/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", "build": "rimraf build && tsc -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/server/src/date.ts b/libs/server/src/date.ts index 301264f513..5d81215457 100644 --- a/libs/server/src/date.ts +++ b/libs/server/src/date.ts @@ -59,7 +59,7 @@ export function isValidTimezone(timezoneStr: string) { // 如果 dayjs 能够正确解析时区名称,函数将正常运行并返回 true return true; } - } catch (e) { + } catch { // 如果发生错误,说明时区字符串无效 return false; } @@ -77,9 +77,9 @@ export function dayjsToDateMessage(dayjsObj: Dayjs): DateMessage { export function checkTimeZone(timeZone: string) { if (!isValidTimezone(timeZone)) { - throw { + throw { code: status.INVALID_ARGUMENT, message: "Invalid timezone", - }; + } as ServiceError; } } diff --git a/libs/server/src/misCommon/clustersActivation.ts b/libs/server/src/misCommon/clustersActivation.ts index f5b1e55a8e..793390ac48 100644 --- a/libs/server/src/misCommon/clustersActivation.ts +++ b/libs/server/src/misCommon/clustersActivation.ts @@ -79,10 +79,10 @@ export const NO_CLUSTERS = "NO_CLUSTERS"; */ export const libCheckActivatedClusters = ({ clusterIds, activatedClusters, logger }: - { clusterIds: string[] | string, - activatedClusters: Record, - logger: Logger - }) => { +{ clusterIds: string[] | string, + activatedClusters: Record, + logger: Logger +}) => { const idsToCheck = Array.isArray(clusterIds) ? clusterIds : [clusterIds]; diff --git a/libs/server/src/scheduleAdapter.ts b/libs/server/src/scheduleAdapter.ts index 8d31a80f95..40d1c9bf8f 100644 --- a/libs/server/src/scheduleAdapter.ts +++ b/libs/server/src/scheduleAdapter.ts @@ -40,14 +40,14 @@ export async function checkSchedulerApiVersion(client: SchedulerAdapterClient, } else if (((e as any).code === status.CANCELLED)) { throw e; } else { - throw { + throw { code: Status.UNIMPLEMENTED, message: "unimplemented", details: "The scheduler API version can not be confirmed." + "To use this method, the scheduler adapter must be upgraded to the version " + `${minVersion.major}.${minVersion.minor}.${minVersion.patch} ` + "or higher.", - }; + } as ServiceError; } } @@ -64,14 +64,14 @@ export async function checkSchedulerApiVersion(client: SchedulerAdapterClient, } if (!geMinVersion) { - throw { + throw { code: Status.FAILED_PRECONDITION, message: "precondition failed", details: "The method is not supported with the current scheduler adapter version. " + "To use this method, the scheduler adapter must be upgraded to the version " + `${minVersion.major}.${minVersion.minor}.${minVersion.patch} ` + "or higher.", - }; + } as ServiceError; } } diff --git a/libs/server/src/systemLanguage.ts b/libs/server/src/systemLanguage.ts index 9603734cda..f77242c735 100644 --- a/libs/server/src/systemLanguage.ts +++ b/libs/server/src/systemLanguage.ts @@ -29,12 +29,12 @@ export function getI18nConfigCurrentText( // 当语言id或者对应的配置文本中某种语言不存在时,显示default的值 if (!languageId) return i18nConfigText.i18n.default; switch (languageId) { - case SYSTEM_VALID_LANGUAGES.EN: - return i18nConfigText.i18n.en || i18nConfigText.i18n.default; - case SYSTEM_VALID_LANGUAGES.ZH_CN: - return i18nConfigText.i18n.zh_cn || i18nConfigText.i18n.default; - default: - return i18nConfigText.i18n.default; + case SYSTEM_VALID_LANGUAGES.EN: + return i18nConfigText.i18n.en || i18nConfigText.i18n.default; + case SYSTEM_VALID_LANGUAGES.ZH_CN: + return i18nConfigText.i18n.zh_cn || i18nConfigText.i18n.default; + default: + return i18nConfigText.i18n.default; } } }; @@ -55,7 +55,7 @@ export function getCurrentLanguageId(req: IncomingMessage | undefined, const cookies = parseCookies({ req }); // 如果cookie设置了而且有效,就使用cookie - if (cookies && cookies.language) { + if (cookies?.language) { const currentCookieLang = cookies.language; if (Object.values(SYSTEM_VALID_LANGUAGES).includes(currentCookieLang)) { return currentCookieLang; @@ -73,14 +73,14 @@ export function getCurrentLanguageId(req: IncomingMessage | undefined, // 判断偏好语言中的语言是否合法 if (Object.values(HEADER_ACCEPT_VALID_LANGUAGES).includes(preferredLanguage)) { switch (preferredLanguage) { - case HEADER_ACCEPT_VALID_LANGUAGES.ZH_CN: - case HEADER_ACCEPT_VALID_LANGUAGES.ZH: - return SYSTEM_VALID_LANGUAGES.ZH_CN; - case HEADER_ACCEPT_VALID_LANGUAGES.EN_US: - case HEADER_ACCEPT_VALID_LANGUAGES.EN: - return SYSTEM_VALID_LANGUAGES.EN; - default: - break; + case HEADER_ACCEPT_VALID_LANGUAGES.ZH_CN: + case HEADER_ACCEPT_VALID_LANGUAGES.ZH: + return SYSTEM_VALID_LANGUAGES.ZH_CN; + case HEADER_ACCEPT_VALID_LANGUAGES.EN_US: + case HEADER_ACCEPT_VALID_LANGUAGES.EN: + return SYSTEM_VALID_LANGUAGES.EN; + default: + break; } } } diff --git a/libs/server/src/typeConversion.ts b/libs/server/src/typeConversion.ts index 997fab310b..79381ea86d 100644 --- a/libs/server/src/typeConversion.ts +++ b/libs/server/src/typeConversion.ts @@ -51,11 +51,11 @@ ClusterConfigSchemaProto_LoginNodesProtoType | undefined => { if (loginNodes instanceof Array && loginNodes.every((node) => typeof node === "string")) { return { value: { $case: "loginNodeAddresses", - loginNodeAddresses: { loginNodeAddressesValue: loginNodes as string[] } } }; + loginNodeAddresses: { loginNodeAddressesValue: loginNodes } } }; } else { return { value: { $case: "loginNodeConfigs", loginNodeConfigs: { loginNodeConfigsValue: loginNodes.map((node) => ({ - name: getI18nSeverTypeFormat(node.name) as I18nStringProtoType, + name: getI18nSeverTypeFormat(node.name)!, address: node.address, })) as ClusterConfigSchemaProto_LoginNodeConfigSchemaProto[] }, } }; @@ -74,7 +74,7 @@ export const convertClusterConfigsToServerProtoType = ( const protoItem: ClusterConfigSchemaProto = { clusterId: key, - displayName: getI18nSeverTypeFormat(item.displayName) as I18nStringProtoType, + displayName: getI18nSeverTypeFormat(item.displayName)!, adapterUrl: item.adapterUrl, priority: item.priority, proxyGateway: item.proxyGateway ? @@ -82,7 +82,7 @@ export const convertClusterConfigsToServerProtoType = ( url: item.proxyGateway.url || "", autoSetupNginx: item.proxyGateway.autoSetupNginx, } : undefined, - loginNodes: getLoginNodesSeverTypeFormat(item.loginNodes) as ClusterConfigSchemaProto_LoginNodesProtoType, + loginNodes: getLoginNodesSeverTypeFormat(item.loginNodes)!, loginDesktop: item.loginDesktop ? { enabled: item.loginDesktop.enabled, diff --git a/libs/ssh/package.json b/libs/ssh/package.json index 38ac40829f..8e8088d2bc 100644 --- a/libs/ssh/package.json +++ b/libs/ssh/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", "build": "rimraf build && tsc -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/ssh/src/sftp.ts b/libs/ssh/src/sftp.ts index aad301984e..e48fcedffd 100644 --- a/libs/ssh/src/sftp.ts +++ b/libs/ssh/src/sftp.ts @@ -35,43 +35,43 @@ export const sftpExists = (sftp: SFTPWrapper, path: string) => }); export const sftpWriteFile = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.writeFile.bind(sftp) as typeof sftp["writeFile"])); + handleSftpError(promisify(sftp.writeFile.bind(sftp))); export const sftpReadFile = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.readFile.bind(sftp) as typeof sftp["readFile"])); + handleSftpError(promisify(sftp.readFile.bind(sftp))); export const sftpReaddir = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.readdir.bind(sftp) as typeof sftp["readdir"])); + handleSftpError(promisify(sftp.readdir.bind(sftp))); export const sftpChmod = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.chmod.bind(sftp) as typeof sftp["chmod"])); + handleSftpError(promisify(sftp.chmod.bind(sftp))); export const sftpChown = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.chown.bind(sftp) as typeof sftp["chown"])); + handleSftpError(promisify(sftp.chown.bind(sftp))); export const sftpRealPath = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.realpath.bind(sftp) as typeof sftp["realpath"])); + handleSftpError(promisify(sftp.realpath.bind(sftp))); export const sftpStat = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.stat.bind(sftp) as typeof sftp["stat"])); + handleSftpError(promisify(sftp.stat.bind(sftp))); export const sftpStatOrUndefined = (sftp: SFTPWrapper) => (path: string) => sftpStat(sftp)(path).catch(() => undefined); export const sftpUnlink = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.unlink.bind(sftp) as typeof sftp["unlink"])); + handleSftpError(promisify(sftp.unlink.bind(sftp))); export const sftpRmdir = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.rmdir.bind(sftp) as typeof sftp["rmdir"])); + handleSftpError(promisify(sftp.rmdir.bind(sftp))); export const sftpRename = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.rename.bind(sftp) as typeof sftp["rename"])); + handleSftpError(promisify(sftp.rename.bind(sftp))); export const sftpMkdir = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.mkdir.bind(sftp) as typeof sftp["mkdir"])); + handleSftpError(promisify(sftp.mkdir.bind(sftp))); export const sftpAppendFile = (sftp: SFTPWrapper) => - handleSftpError(promisify(sftp.appendFile.bind(sftp) as typeof sftp["appendFile"])); + handleSftpError(promisify(sftp.appendFile.bind(sftp))); export const createDirectoriesRecursively = async (sftp: SFTPWrapper, directory: string) => { diff --git a/libs/utils/package.json b/libs/utils/package.json index bcab28041b..c6e04615cf 100644 --- a/libs/utils/package.json +++ b/libs/utils/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", "build": "rimraf build && tsc -p tsconfig.build.json", - "test": "jest" + "test": "jest", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/utils/src/array.ts b/libs/utils/src/array.ts index bfe295d10e..744be45e3f 100644 --- a/libs/utils/src/array.ts +++ b/libs/utils/src/array.ts @@ -18,7 +18,7 @@ export function range(start = 1, end = 0, step = 1): number[] { return r; } -export function flatten(nestedArray: Array>): T[] { +export function flatten(nestedArray: (T | T[])[]): T[] { const result = [] as T[]; for (const item of nestedArray) { if (Array.isArray(item)) { diff --git a/libs/utils/src/version.ts b/libs/utils/src/version.ts index 1738fe38d6..bec418c251 100644 --- a/libs/utils/src/version.ts +++ b/libs/utils/src/version.ts @@ -18,20 +18,21 @@ interface VersionJsonInfo { commit: string; } -export interface VersionInfo extends VersionJsonInfo {} +export type VersionInfo = VersionJsonInfo; export function readVersionFile(versionJsonFileName = "version.json") { - const jsonInfo: VersionJsonInfo = existsSync(versionJsonFileName) + const jsonInfo: VersionInfo = existsSync(versionJsonFileName) ? JSON.parse(readFileSync(versionJsonFileName, "utf-8")) : {}; - return jsonInfo; } + return jsonInfo; +} // SemVer类型version -export type ApiVersion = { +export interface ApiVersion { major: number; minor: number; patch: number; -} +}; diff --git a/libs/web/package.json b/libs/web/package.json index 1728650e11..929f0191db 100644 --- a/libs/web/package.json +++ b/libs/web/package.json @@ -7,7 +7,8 @@ "sideEffects": false, "scripts": { "dev": "tsc -p tsconfig.build.json && (concurrently \"tsc -p tsconfig.build.json -w\" \"tsc-alias -p tsconfig.build.json -w\")", - "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json" + "build": "rimraf build && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", + "lint": "eslint -c ../../eslint.config.js ." }, "files": [ "build", diff --git a/libs/web/src/components/head.tsx b/libs/web/src/components/head.tsx index 43f63ce815..24fa8fb777 100644 --- a/libs/web/src/components/head.tsx +++ b/libs/web/src/components/head.tsx @@ -14,7 +14,7 @@ import NextHead from "next/head"; type Props = React.PropsWithChildren<{ title: string; -}> +}>; export const Head: React.FC = ({ title, children }) => { return ( diff --git a/libs/web/src/extensions/UiExtensionStore.ts b/libs/web/src/extensions/UiExtensionStore.ts index e6d3db77bd..6603474453 100644 --- a/libs/web/src/extensions/UiExtensionStore.ts +++ b/libs/web/src/extensions/UiExtensionStore.ts @@ -20,10 +20,10 @@ const fetchManifestsWithErrorHandling = (url: string, name?: string): Promise ({ url, manifests: x, name })) .catch((e) => { console.error(`Error fetching extension manifests. ${e}`); return undefined; }); -export type ExtensionManifestWithUrl = { +export interface ExtensionManifestWithUrl { url: string; manifests: Awaited> name?: string; -}; +} export const UiExtensionStore = (uiExtensionConfig?: UiExtensionConfigSchema) => { diff --git a/libs/web/src/extensions/manifests.ts b/libs/web/src/extensions/manifests.ts index 36da2883fe..6477e7321b 100644 --- a/libs/web/src/extensions/manifests.ts +++ b/libs/web/src/extensions/manifests.ts @@ -39,6 +39,6 @@ export async function fetchExtensionManifests(url: string) { if (resp[200]) { return resp[200]; } else { - throw new Error(`Cannot fetch extension manifests. ${resp}`); + throw new Error(`Cannot fetch extension manifests. ${JSON.stringify(resp)}`); } } diff --git a/libs/web/src/extensions/navigations.tsx b/libs/web/src/extensions/navigations.tsx index bf6202dc93..8cda4ef5e9 100644 --- a/libs/web/src/extensions/navigations.tsx +++ b/libs/web/src/extensions/navigations.tsx @@ -31,10 +31,10 @@ export const BaseNavItem = z.object({ export type NavItem = z.infer & { children?: NavItem[]; -} +}; export const NavItem = BaseNavItem.extend({ - children: z.lazy(() => NavItem).array().optional(), + children: z.lazy(() => NavItem as NavItem).array().optional(), }); export const rewriteNavigationsRoute = (from: "portal" | "mis") => defineExtensionRoute({ diff --git a/libs/web/src/extensions/routes.ts b/libs/web/src/extensions/routes.ts index e12751cd24..c0d51571be 100644 --- a/libs/web/src/extensions/routes.ts +++ b/libs/web/src/extensions/routes.ts @@ -53,7 +53,7 @@ export const callExtensionRoute = async < query: TQuery, body: TBody, extensionUrl: string, -): Promise }>> => { +): Promise }>> => { let url = joinWithUrl(extensionUrl, "api", route.path); diff --git a/libs/web/src/layouts/base/SideNav/index.tsx b/libs/web/src/layouts/base/SideNav/index.tsx index e366e90407..71c92641ca 100644 --- a/libs/web/src/layouts/base/SideNav/index.tsx +++ b/libs/web/src/layouts/base/SideNav/index.tsx @@ -92,7 +92,7 @@ export const SideNav: React.FC = ({ * 仅在账户管理页面有效,且用户管理的账户数量超过三个 */ // - menuFocusedRef.current = parentKeys.length > 3 && /^\/accounts/.test(parentKeys[0]); + menuFocusedRef.current = parentKeys.length > 3 && parentKeys[0].startsWith("/accounts"); if (menuFocusedRef.current) setOpenKeys([parentKeys[0]]); else setOpenKeys(parentKeys); @@ -106,9 +106,9 @@ export const SideNav: React.FC = ({ const onOpenChange = useCallback((keys) => { if (menuFocusedRef.current) { - const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1); + const latestOpenKey = keys.find((key) => !openKeys.includes(key)); - if (parentKeys.indexOf(latestOpenKey!) === -1) { + if (!parentKeys.includes(latestOpenKey)) { setOpenKeys(keys); } else { setOpenKeys(latestOpenKey ? [latestOpenKey] : []); diff --git a/libs/web/src/layouts/base/common.tsx b/libs/web/src/layouts/base/common.tsx index 05da5bafcc..0eae41b35d 100644 --- a/libs/web/src/layouts/base/common.tsx +++ b/libs/web/src/layouts/base/common.tsx @@ -45,8 +45,11 @@ export function createMenuItems( if (route.openInNewPage) { window.open(target); } else { - EXTERNAL_URL_PREFIX.some((pref) => target.startsWith(pref)) - ? window.location.href = target : Router.push(target); + if (EXTERNAL_URL_PREFIX.some((pref) => target.startsWith(pref))) { + window.location.href = target; + } else { + void Router.push(target); + } } } : undefined, diff --git a/libs/web/src/layouts/base/constants.d.ts b/libs/web/src/layouts/base/constants.d.ts index 867cbd1a3e..0d6dba4256 100644 --- a/libs/web/src/layouts/base/constants.d.ts +++ b/libs/web/src/layouts/base/constants.d.ts @@ -11,22 +11,22 @@ */ export declare const antdBreakpoints: { - xxs: number; - xs: number; - sm: number; - md: number; - lg: number; - xl: number; - xxl: number; + xxs: number; + xs: number; + sm: number; + md: number; + lg: number; + xl: number; + xxl: number; }; export type Breakpoint = keyof typeof antdBreakpoints; export declare const layoutConstants: { - paddingBreakpoint: "md" | "xxs" | "xs" | "sm" | "lg" | "xl" | "xxl"; - menuBreakpoint: "md" | "xxs" | "xs" | "sm" | "lg" | "xl" | "xxl"; - headerHeight: number; - sidebarBreakpoint: "md" | "xxs" | "xs" | "sm" | "lg" | "xl" | "xxl"; - headerIconColor: string; - headerIconBackgroundColor: string; - headerBackgrounColor: string; - maxWidth: number; + paddingBreakpoint: "md" | "xxs" | "xs" | "sm" | "lg" | "xl" | "xxl"; + menuBreakpoint: "md" | "xxs" | "xs" | "sm" | "lg" | "xl" | "xxl"; + headerHeight: number; + sidebarBreakpoint: "md" | "xxs" | "xs" | "sm" | "lg" | "xl" | "xxl"; + headerIconColor: string; + headerIconBackgroundColor: string; + headerBackgrounColor: string; + maxWidth: number; }; diff --git a/libs/web/src/layouts/base/header/index.tsx b/libs/web/src/layouts/base/header/index.tsx index a3735abf7e..8cbbf87175 100644 --- a/libs/web/src/layouts/base/header/index.tsx +++ b/libs/web/src/layouts/base/header/index.tsx @@ -66,11 +66,11 @@ const IndicatorPart = styled(HeaderItem)` flex-wrap: nowrap; `; -export type HeaderNavbarLink = { +export interface HeaderNavbarLink { icon: React.ReactNode; href: string; text: string; -} +}; interface Props { hasSidebar: boolean; diff --git a/libs/web/src/layouts/darkMode.tsx b/libs/web/src/layouts/darkMode.tsx index 1d7bdd4155..9d175329eb 100644 --- a/libs/web/src/layouts/darkMode.tsx +++ b/libs/web/src/layouts/darkMode.tsx @@ -19,16 +19,16 @@ import React, { PropsWithChildren, useEffect, useState } from "react"; import { addBasePathToImage } from "src/utils/image"; import { getCurrentLangLibWebText } from "src/utils/libWebI18n/libI18n"; -const modes = ["system", "dark", "light"] as const; +const _modes = ["system", "dark", "light"] as const; -export type DarkMode = typeof modes[number]; +export type DarkMode = typeof _modes[number]; const DarkModeContext = React.createContext<{ - mode: DarkMode; - dark: boolean; - setMode: (mode: DarkMode) => void; - }>(undefined!); + mode: DarkMode; + dark: boolean; + setMode: (mode: DarkMode) => void; +}>(undefined!); export const useDarkMode = () => React.useContext(DarkModeContext); diff --git a/libs/web/src/utils/array.ts b/libs/web/src/utils/array.ts index bfe295d10e..744be45e3f 100644 --- a/libs/web/src/utils/array.ts +++ b/libs/web/src/utils/array.ts @@ -18,7 +18,7 @@ export function range(start = 1, end = 0, step = 1): number[] { return r; } -export function flatten(nestedArray: Array>): T[] { +export function flatten(nestedArray: (T | T[])[]): T[] { const result = [] as T[]; for (const item of nestedArray) { if (Array.isArray(item)) { diff --git a/libs/web/src/utils/datetime.ts b/libs/web/src/utils/datetime.ts index adc3adbeae..4407a838ca 100644 --- a/libs/web/src/utils/datetime.ts +++ b/libs/web/src/utils/datetime.ts @@ -77,12 +77,12 @@ export enum TimeUnits { export const parseMinutes = (time: number, unit: TimeUnits): number => { switch (unit) { - case TimeUnits.MINUTE: - return time; - case TimeUnits.HOUR: - return time * 60; - case TimeUnits.DAY: - return time * 60 * 24; + case TimeUnits.MINUTE: + return time; + case TimeUnits.HOUR: + return time * 60; + case TimeUnits.DAY: + return time * 60 * 24; } }; diff --git a/libs/web/src/utils/debounce.ts b/libs/web/src/utils/debounce.ts index db1f353493..c818759119 100644 --- a/libs/web/src/utils/debounce.ts +++ b/libs/web/src/utils/debounce.ts @@ -26,7 +26,7 @@ export function debounce any>( () => { const resp = func(...args); if (resp instanceof Promise) { - resp.then((v) => resolve(v)); + void resp.then((v) => resolve(v)); } else { resolve(resp); } diff --git a/libs/web/src/utils/form.ts b/libs/web/src/utils/form.ts index 37e6ecd0a2..9a201a5466 100644 --- a/libs/web/src/utils/form.ts +++ b/libs/web/src/utils/form.ts @@ -18,7 +18,7 @@ import { getCurrentLangLibWebText } from "./libWebI18n/libI18n"; export const confirmPasswordFormItemProps = < PasswordFieldName extends string, - T extends { [key in PasswordFieldName]: string } + T extends {[key in PasswordFieldName]: string }, >(form: FormInstance, passwordFieldName: PasswordFieldName, languageId: string) => { return { dependencies: [passwordFieldName], @@ -50,7 +50,7 @@ export const positiveNumberRule = (_: RuleObject, value: any, languageId: string if ((value && parseFloat(value) < 0) || value === 0) { const errorMessage = getCurrentLangLibWebText(languageId, "notPositiveNumberError"); - return Promise.reject(errorMessage); + return Promise.reject(new Error(errorMessage)); } return Promise.resolve(); }; @@ -61,14 +61,14 @@ export const compareUsedChargeRule = if (usedCharge && value < usedCharge) { const errorMessage = getCurrentLangLibWebText(languageId, "compareUsedChargeError"); - return Promise.reject(errorMessage); + return Promise.reject(new Error(errorMessage)); } return Promise.resolve(); }; // check consistency of the compared value and the input value export const validateDataConsistency = - (inputFieldName: InputFieldName, comparedValue: string, languageId: string) => { +(inputFieldName: InputFieldName, comparedValue: string, languageId: string) => { return { dependencies: [inputFieldName], validateFirst: true, diff --git a/libs/web/src/utils/libWebI18n/libI18n.ts b/libs/web/src/utils/libWebI18n/libI18n.ts index 9ddb8172e7..ffe0245d45 100644 --- a/libs/web/src/utils/libWebI18n/libI18n.ts +++ b/libs/web/src/utils/libWebI18n/libI18n.ts @@ -16,7 +16,7 @@ import libWebZhCn from "./libWebZhCn"; export type LibWebTextsType = typeof libWebEn; export type LibWebTextsKeys = keyof LibWebTextsType; -export const libWebLanguages: { [id: string]: LibWebTextsType } = { +export const libWebLanguages: Record = { en: libWebEn, zh_cn: libWebZhCn, }; diff --git a/libs/web/src/utils/misCommon/clustersActivation.ts b/libs/web/src/utils/misCommon/clustersActivation.ts index 4c7a8238d5..7562b17c1e 100644 --- a/libs/web/src/utils/misCommon/clustersActivation.ts +++ b/libs/web/src/utils/misCommon/clustersActivation.ts @@ -29,13 +29,13 @@ export function formatActivatedClusters({ misDeployed = true, }: { clustersRuntimeInfo?: ClusterRuntimeInfo[], - misConfigClusters?: {[clusterId: string]: Cluster}, + misConfigClusters?: Record, configClusters?: Cluster[], misDeployed?: boolean, }): { - misActivatedClusters?: {[clusterId: string]: Cluster}, - activatedClusters?: Cluster[], -} { + misActivatedClusters?: Record, + activatedClusters?: Cluster[], + } { // for system except mis, if mis is not deployed, using config clusters if (!misDeployed) { @@ -57,7 +57,7 @@ export function formatActivatedClusters({ } else { - const misActivatedClusters: {[clusterId: string]: Cluster} = {}; + const misActivatedClusters: Record = {}; if (!misConfigClusters) { console.warn("No available clusters in Mis"); diff --git a/libs/web/src/utils/systemLanguage.ts b/libs/web/src/utils/systemLanguage.ts index 1c665cc663..b71aae78e5 100644 --- a/libs/web/src/utils/systemLanguage.ts +++ b/libs/web/src/utils/systemLanguage.ts @@ -27,12 +27,12 @@ export function getI18nConfigCurrentText( // 当语言id或者对应的配置文本中某种语言不存在时,显示default的值 if (!languageId) return i18nConfigText.i18n.default; switch (languageId) { - case SYSTEM_VALID_LANGUAGES.EN: - return i18nConfigText.i18n.en || i18nConfigText.i18n.default; - case SYSTEM_VALID_LANGUAGES.ZH_CN: - return i18nConfigText.i18n.zh_cn || i18nConfigText.i18n.default; - default: - return i18nConfigText.i18n.default; + case SYSTEM_VALID_LANGUAGES.EN: + return i18nConfigText.i18n.en || i18nConfigText.i18n.default; + case SYSTEM_VALID_LANGUAGES.ZH_CN: + return i18nConfigText.i18n.zh_cn || i18nConfigText.i18n.default; + default: + return i18nConfigText.i18n.default; } } }; @@ -53,7 +53,7 @@ export function getCurrentLanguageId(req: IncomingMessage | undefined, const cookies = parseCookies({ req }); // 如果cookie设置了而且有效,就使用cookie - if (cookies && cookies.language) { + if (cookies?.language) { const currentCookieLang = cookies.language; if (Object.values(SYSTEM_VALID_LANGUAGES).includes(currentCookieLang)) { return currentCookieLang; @@ -71,14 +71,14 @@ export function getCurrentLanguageId(req: IncomingMessage | undefined, // 判断偏好语言中的语言是否合法 if (Object.values(HEADER_ACCEPT_VALID_LANGUAGES).includes(preferredLanguage)) { switch (preferredLanguage) { - case HEADER_ACCEPT_VALID_LANGUAGES.ZH_CN: - case HEADER_ACCEPT_VALID_LANGUAGES.ZH: - return SYSTEM_VALID_LANGUAGES.ZH_CN; - case HEADER_ACCEPT_VALID_LANGUAGES.EN_US: - case HEADER_ACCEPT_VALID_LANGUAGES.EN: - return SYSTEM_VALID_LANGUAGES.EN; - default: - break; + case HEADER_ACCEPT_VALID_LANGUAGES.ZH_CN: + case HEADER_ACCEPT_VALID_LANGUAGES.ZH: + return SYSTEM_VALID_LANGUAGES.ZH_CN; + case HEADER_ACCEPT_VALID_LANGUAGES.EN_US: + case HEADER_ACCEPT_VALID_LANGUAGES.EN: + return SYSTEM_VALID_LANGUAGES.EN; + default: + break; } } } diff --git a/libs/web/src/utils/typeConversion.ts b/libs/web/src/utils/typeConversion.ts index bef10436fc..98c5145347 100644 --- a/libs/web/src/utils/typeConversion.ts +++ b/libs/web/src/utils/typeConversion.ts @@ -66,7 +66,7 @@ export const getClusterConfigsTypeFormat = ( ...rest, displayName: getI18nTypeFormat(cluster.displayName), loginNodes: getLoginNodesTypeFormat( - cluster.loginNodes as ClusterConfigSchemaProto_LoginNodesProtoType | undefined), + cluster.loginNodes), k8s: cluster.k8s ? { k8sRuntime: clusterConfigSchemaProto_K8sRuntimeToJSON(cluster.k8s.runtime).toLowerCase(), kubeconfig: cluster.k8s.kubeconfig, diff --git a/package.json b/package.json index ed89d51d87..8f9e1add31 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,7 @@ "test": "turbo run test", "test:ci": "pnpm run -r test --ci --coverage --runInBand", "prepare": "node -e \"try { import('husky').then((d) => console.log(d.default())) } catch (e) {if (e.code !== 'MODULE_NOT_FOUND' && !e.message.startsWith('.git')) throw e}\"", - "lint": "turbo run lint lint:ts", - "lint:ts": "eslint --cache --ext .tsx,.ts,.js .", + "lint": "turbo run lint --continue", "ci:version": "node scripts/version.mjs", "ci:publish": "pnpm publish -r", "api:breaking": "cd protos && pnpm exec buf breaking --against '../.git#subdir=protos'" @@ -25,23 +24,17 @@ "@bufbuild/buf": "1.34.0", "@changesets/cli": "2.27.6", "@commitlint/config-conventional": "19.2.2", - "@ddadaal/eslint-config": "1.9.0", + "@ddadaal/eslint-config": "2.1.0", "@pnpm/lockfile-file": "9.1.1", "@pnpm/logger": "5.0.0", "@types/jest": "29.5.12", "@types/node": "20.14.8", - "@typescript-eslint/eslint-plugin": "7.13.1", - "@typescript-eslint/parser": "7.13.1", "concurrently": "8.2.2", "cross-env": "7.0.3", "cz-conventional-changelog": "3.3.0", "dotenv": "16.4.5", "dotenv-cli": "7.4.2", - "eslint": "8.57.0", - "eslint-import-resolver-typescript": "3.6.1", - "eslint-plugin-import": "2.29.1", - "eslint-plugin-license-header": "0.6.1", - "eslint-plugin-simple-import-sort": "12.1.0", + "eslint": "9.6.0", "front-matter": "4.0.2", "globby": "14.0.1", "husky": "9.0.11", @@ -57,7 +50,8 @@ "tsc-alias": "1.8.10", "tsconfig-paths": "4.2.0", "turbo": "2.0.4", - "typescript": "5.5.2" + "typescript": "5.5.2", + "eslint-plugin-license-header": "0.6.1" }, "volta": { "node": "20.15.0" @@ -69,4 +63,4 @@ "next@14.2.4": "patches/next@14.2.4.patch" } } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38f2575286..0224dc2943 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,8 +26,8 @@ importers: specifier: 19.2.2 version: 19.2.2 '@ddadaal/eslint-config': - specifier: 1.9.0 - version: 1.9.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint-plugin-simple-import-sort@12.1.0(eslint@8.57.0))(eslint@8.57.0)(typescript@5.5.2) + specifier: 2.1.0 + version: 2.1.0(eslint@9.6.0)(typescript@5.5.2) '@pnpm/lockfile-file': specifier: 9.1.1 version: 9.1.1(@pnpm/logger@5.0.0) @@ -40,12 +40,6 @@ importers: '@types/node': specifier: 20.14.8 version: 20.14.8 - '@typescript-eslint/eslint-plugin': - specifier: 7.13.1 - version: 7.13.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/parser': - specifier: 7.13.1 - version: 7.13.1(eslint@8.57.0)(typescript@5.5.2) concurrently: specifier: 8.2.2 version: 8.2.2 @@ -62,20 +56,11 @@ importers: specifier: 7.4.2 version: 7.4.2 eslint: - specifier: 8.57.0 - version: 8.57.0 - eslint-import-resolver-typescript: - specifier: 3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: - specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + specifier: 9.6.0 + version: 9.6.0 eslint-plugin-license-header: specifier: 0.6.1 version: 0.6.1 - eslint-plugin-simple-import-sort: - specifier: 12.1.0 - version: 12.1.0(eslint@8.57.0) front-matter: specifier: 4.0.2 version: 4.0.2 @@ -1217,13 +1202,13 @@ importers: dependencies: '@docusaurus/core': specifier: 3.4.0 - version: 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + version: 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/preset-classic': specifier: 3.4.0 - version: 3.4.0(@algolia/client-search@4.23.3)(@types/react@18.2.37)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2) + version: 3.4.0(@algolia/client-search@4.23.3)(@types/react@18.2.37)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2) '@easyops-cn/docusaurus-search-local': specifier: 0.44.2 - version: 0.44.2(@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2))(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + version: 0.44.2(@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2))(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@mdx-js/react': specifier: 3.0.0 version: 3.0.0(@types/react@18.2.37)(react@18.2.0) @@ -2611,14 +2596,10 @@ packages: resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} engines: {node: '>=10'} - '@ddadaal/eslint-config@1.9.0': - resolution: {integrity: sha512-k6RKAT1FUGBEpyB5rRS/RuF20pA/H8u0cBNUf7xKDxHcTk/yR3oN0FjtxmAPkZhVuKjoUWvGniSlEeg1zE3K2g==} + '@ddadaal/eslint-config@2.1.0': + resolution: {integrity: sha512-Lg/4rotuWJ8+HVI57AP/moNhj1XSq9lJD/d/Pp3vJ+97PVSe+Z47EaSljC+0XhTndx21K7JqiVQFxfw3GB8DWA==} peerDependencies: - '@typescript-eslint/eslint-plugin': '>=5.37.0' - eslint: '>=8.23.1' - eslint-import-resolver-typescript: '>=3.5.1' - eslint-plugin-import: '>=2.26.0' - eslint-plugin-simple-import-sort: '>=8.0.0' + eslint: '>=9' '@ddadaal/next-typed-api-routes-cli@0.9.1': resolution: {integrity: sha512-BrSB5edjR5wy5CCbxHd4ulNFNoT8OrOccNWOuEDvpKnTUpJuxiylyut5xZBvYhW9cg35CEZUQ9N6E0DhsTjdrQ==} @@ -2955,13 +2936,21 @@ packages: resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.17.0': + resolution: {integrity: sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.6.0': + resolution: {integrity: sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.4': + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fastify/accept-negotiator@1.1.0': resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==} @@ -3016,18 +3005,13 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} '@ioredis/commands@1.2.0': resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} @@ -3720,6 +3704,35 @@ packages: '@slorber/remark-comment@1.0.0': resolution: {integrity: sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==} + '@stylistic/eslint-plugin-js@2.3.0': + resolution: {integrity: sha512-lQwoiYb0Fs6Yc5QS3uT8+T9CPKK2Eoxc3H8EnYJgM26v/DgtW+1lvy2WNgyBflU+ThShZaHm3a6CdD9QeKx23w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.40.0' + + '@stylistic/eslint-plugin-jsx@2.3.0': + resolution: {integrity: sha512-tsQ0IEKB195H6X9A4iUSgLLLKBc8gUBWkBIU8tp1/3g2l8stu+PtMQVV/VmK1+3bem5FJCyvfcZIQ/WF1fsizA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.40.0' + + '@stylistic/eslint-plugin-plus@2.3.0': + resolution: {integrity: sha512-xboPWGUU5yaPlR+WR57GwXEuY4PSlPqA0C3IdNA/+1o2MuBi95XgDJcZiJ9N+aXsqBXAPIpFFb+WQ7QEHo4f7g==} + peerDependencies: + eslint: '*' + + '@stylistic/eslint-plugin-ts@2.3.0': + resolution: {integrity: sha512-wqOR38/uz/0XPnHX68ftp8sNMSAqnYGjovOTN7w00xnjS6Lxr3Sk7q6AaxWWqbMvOj7V2fQiMC5HWAbTruJsCg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.40.0' + + '@stylistic/eslint-plugin@2.3.0': + resolution: {integrity: sha512-rtiz6u5gRyyEZp36FcF1/gHJbsbT3qAgXZ1qkad6Nr/xJ9wrSJkiSFFQhpYVTIZ7FJNRJurEcumZDCwN9dEI4g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.40.0' + '@svgr/babel-plugin-add-jsx-attribute@6.5.1': resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} engines: {node: '>=10'} @@ -4151,6 +4164,9 @@ packages: '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + '@types/eslint@8.56.10': + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + '@types/eslint@8.56.9': resolution: {integrity: sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==} @@ -4223,9 +4239,6 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/ldapjs@2.2.5': resolution: {integrity: sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==} @@ -4385,22 +4398,22 @@ packages: '@types/yargs@17.0.32': resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} - '@typescript-eslint/eslint-plugin@7.13.1': - resolution: {integrity: sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/eslint-plugin@8.0.0-alpha.39': + resolution: {integrity: sha512-ILv1vDA8M9ah1vzYpnOs4UOLRdB63Ki/rsxedVikjMLq68hFfpsDR25bdMZ4RyUkzLJwOhcg3Jujm/C1nupXKA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/parser@7.13.1': - resolution: {integrity: sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/parser@8.0.0-alpha.39': + resolution: {integrity: sha512-5k+pwV91plJojHgZkWlq4/TQdOrnEaeSvt48V0m8iEwdMJqX/63BXYxy8BUOSghWcjp05s73vy9HJjovAKmHkQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: @@ -4410,11 +4423,14 @@ packages: resolution: {integrity: sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/type-utils@7.13.1': - resolution: {integrity: sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@8.0.0-alpha.39': + resolution: {integrity: sha512-HCBlKQROY+JIgWolucdFMj1W3VUnnIQTdxAhxJTAj3ix2nASmvKIFgrdo5KQMrXxQj6tC4l3zva10L+s0dUIIw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.0.0-alpha.39': + resolution: {integrity: sha512-alO13fRU6yVeJbwl9ESI3AYhq5dQdz3Dpd0I5B4uezs2lvgYp44dZsj5hWyPz/kL7JFEsjbn+4b/CZA0OQJzjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: @@ -4424,6 +4440,10 @@ packages: resolution: {integrity: sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==} engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/types@8.0.0-alpha.39': + resolution: {integrity: sha512-yINN7j0/+S1VGSp0IgH52oQvUx49vkOug6xbrDA/9o+U55yCAQKSvYWvzYjNa+SZE3hXI0zwvYtMVsIAAMmKIQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@7.13.1': resolution: {integrity: sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4433,16 +4453,35 @@ packages: typescript: optional: true + '@typescript-eslint/typescript-estree@8.0.0-alpha.39': + resolution: {integrity: sha512-S8gREuP8r8PCxGegeojeXntx0P50ul9YH7c7JYpbLIIsEPNr5f7UHlm+I1NUbL04CBin4kvZ60TG4eWr/KKN9A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/utils@7.13.1': resolution: {integrity: sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 + '@typescript-eslint/utils@8.0.0-alpha.39': + resolution: {integrity: sha512-Nr2PrlfNhrNQTlFHlD7XJdTGw/Vt8qY44irk6bfjn9LxGdSG5e4c1R2UN6kvGMhhx20DBPbM7q3Z3r+huzmL1w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/visitor-keys@7.13.1': resolution: {integrity: sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==} engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/visitor-keys@8.0.0-alpha.39': + resolution: {integrity: sha512-DVJ0UdhucZy+/1GlIy7FX2+CFhCeNAi4VwaEAe7u2UDenQr9/kGqvzx00UlpWibmEVDw4KsPOI7Aqa1+2Vqfmw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/vfs@1.5.0': resolution: {integrity: sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==} @@ -4600,6 +4639,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} @@ -4770,10 +4814,6 @@ packages: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} - array.prototype.flat@1.3.2: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} @@ -4785,8 +4825,9 @@ packages: array.prototype.toreversed@1.1.2: resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} - array.prototype.tosorted@1.1.3: - resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.3: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} @@ -5992,10 +6033,6 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -6114,10 +6151,6 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - enhanced-resolve@5.16.0: - resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} - engines: {node: '>=10.13.0'} - enhanced-resolve@5.17.0: resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} engines: {node: '>=10.13.0'} @@ -6164,8 +6197,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.0.18: - resolution: {integrity: sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==} + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} engines: {node: '>= 0.4'} es-module-lexer@1.5.0: @@ -6222,58 +6255,17 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-import-resolver-typescript@3.6.1: - resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - - eslint-module-utils@2.8.1: - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.29.1: - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint-plugin-license-header@0.6.1: resolution: {integrity: sha512-9aIz8q3OaMr1/uQmCGCWySjTs5nEXUJexNegz/8lluNcZbEl82Ag1Vyr1Hu3oIveRW1NbXDPs6nu4zu9mbrmWA==} - eslint-plugin-react@7.34.1: - resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} + eslint-plugin-react@7.34.3: + resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - eslint-plugin-simple-import-sort@12.1.0: - resolution: {integrity: sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==} + eslint-plugin-simple-import-sort@12.1.1: + resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} peerDependencies: eslint: '>=5.0.0' @@ -6281,26 +6273,30 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.0.1: + resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.6.0: + resolution: {integrity: sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -6540,9 +6536,9 @@ packages: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} file-loader@6.2.0: resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} @@ -6599,9 +6595,9 @@ packages: resolution: {integrity: sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==} engines: {node: '>= 8'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} @@ -6758,9 +6754,6 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} - get-tsconfig@4.7.3: - resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} - get-uri@6.0.3: resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} engines: {node: '>= 14'} @@ -6847,9 +6840,13 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.8.0: + resolution: {integrity: sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==} + engines: {node: '>=18'} globalthis@1.0.3: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} @@ -7817,10 +7814,6 @@ packages: json2mq@0.2.0: resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -8721,10 +8714,6 @@ packages: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - object.hasown@1.1.4: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} @@ -8991,6 +8980,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pidusage@2.0.21: resolution: {integrity: sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==} engines: {node: '>=8'} @@ -10372,9 +10365,6 @@ packages: resolve-pathname@3.0.0: resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} engines: {node: '>=10'} @@ -11283,9 +11273,6 @@ packages: resolution: {integrity: sha512-Ibv4KAWfFkFdKJxnWfVtdOmB0Zi1RJVxcbPGiCDsFpCQSsmpWyuzHG3rQyI5YkobWwxFPEyQfu1hdo4qLG2zPw==} hasBin: true - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -11406,6 +11393,15 @@ packages: types-ramda@0.30.0: resolution: {integrity: sha512-oVPw/KHB5M0Du0txTEKKM8xZOG9cZBRdCVXvwHYuNJUVkAiJ9oWyqkA+9Bj2gjMsHgkkhsYevobQBWs8I2/Xvw==} + typescript-eslint@8.0.0-alpha.39: + resolution: {integrity: sha512-bsuR1BVJfHr7sBh7Cca962VPIcP+5UWaIa/+6PpnFZ+qtASjGTxKWIF5dG2o73BX9NsyqQfvRWujb3M9CIoRXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + typescript@4.5.2: resolution: {integrity: sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==} engines: {node: '>=4.2.0'} @@ -13285,16 +13281,16 @@ snapshots: '@ctrl/tinycolor@3.6.1': {} - '@ddadaal/eslint-config@1.9.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint-plugin-simple-import-sort@12.1.0(eslint@8.57.0))(eslint@8.57.0)(typescript@5.5.2)': + '@ddadaal/eslint-config@2.1.0(eslint@9.6.0)(typescript@5.5.2)': dependencies: - '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/parser': 7.13.1(eslint@8.57.0)(typescript@5.5.2) - eslint: 8.57.0 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-simple-import-sort: 12.1.0(eslint@8.57.0) + '@eslint/js': 9.6.0 + '@stylistic/eslint-plugin': 2.3.0(eslint@9.6.0)(typescript@5.5.2) + eslint: 9.6.0 + eslint-plugin-simple-import-sort: 12.1.1(eslint@9.6.0) + globals: 15.8.0 + typescript-eslint: 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) optionalDependencies: - eslint-plugin-react: 7.34.1(eslint@8.57.0) + eslint-plugin-react: 7.34.3(eslint@9.6.0) transitivePeerDependencies: - supports-color - typescript @@ -13377,7 +13373,7 @@ snapshots: transitivePeerDependencies: - '@algolia/client-search' - '@docusaurus/core@3.2.0(@docusaurus/types@3.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(debug@4.3.4)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/core@3.2.0(@docusaurus/types@3.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(debug@4.3.4)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: '@babel/core': 7.24.4 '@babel/generator': 7.24.4 @@ -13430,7 +13426,7 @@ snapshots: postcss-loader: 7.3.4(postcss@8.4.38)(typescript@5.5.2)(webpack@5.92.1) prompts: 2.4.2 react: 18.2.0 - react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.5.2)(webpack@5.92.1) + react-dev-utils: 12.0.1(eslint@9.6.0)(typescript@5.5.2)(webpack@5.92.1) react-dom: 18.2.0(react@18.2.0) react-helmet-async: 1.3.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-loadable: '@docusaurus/react-loadable@5.5.2(react@18.2.0)' @@ -13470,7 +13466,7 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/core@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/core@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: '@babel/core': 7.24.4 '@babel/generator': 7.24.4 @@ -13521,7 +13517,7 @@ snapshots: postcss-loader: 7.3.4(postcss@8.4.38)(typescript@5.5.2)(webpack@5.92.1) prompts: 2.4.2 react: 18.2.0 - react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.5.2)(webpack@5.92.1) + react-dev-utils: 12.0.1(eslint@9.6.0)(typescript@5.5.2)(webpack@5.92.1) react-dom: 18.2.0(react@18.2.0) react-helmet-async: 1.3.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.2.0)' @@ -13695,9 +13691,9 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/plugin-content-blog@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-content-blog@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/logger': 3.4.0 '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -13734,9 +13730,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-docs@3.2.0(debug@4.3.4)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-content-docs@3.2.0(debug@4.3.4)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.2.0(@docusaurus/types@3.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(debug@4.3.4)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.2.0(@docusaurus/types@3.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(debug@4.3.4)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/logger': 3.2.0 '@docusaurus/mdx-loader': 3.2.0(@docusaurus/types@3.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/module-type-aliases': 3.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -13772,9 +13768,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-docs@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-content-docs@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/logger': 3.4.0 '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -13810,9 +13806,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-pages@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-content-pages@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) @@ -13840,9 +13836,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-debug@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-debug@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) fs-extra: 11.2.0 @@ -13868,9 +13864,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-analytics@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-google-analytics@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) react: 18.2.0 @@ -13894,9 +13890,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-gtag@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-google-gtag@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) '@types/gtag.js': 0.0.12 @@ -13921,9 +13917,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-tag-manager@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-google-tag-manager@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) react: 18.2.0 @@ -13947,9 +13943,9 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-sitemap@3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/plugin-sitemap@3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/logger': 3.4.0 '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) @@ -13978,20 +13974,20 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/preset-classic@3.4.0(@algolia/client-search@4.23.3)(@types/react@18.2.37)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2)': - dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-blog': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-pages': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-debug': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-google-analytics': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-google-gtag': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-google-tag-manager': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-sitemap': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/theme-classic': 3.4.0(@types/react@18.2.37)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/theme-search-algolia': 3.4.0(@algolia/client-search@4.23.3)(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@types/react@18.2.37)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2) + '@docusaurus/preset-classic@3.4.0(@algolia/client-search@4.23.3)(@types/react@18.2.37)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2)': + dependencies: + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-blog': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-docs': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-pages': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-debug': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-google-analytics': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-google-gtag': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-google-tag-manager': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-sitemap': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/theme-classic': 3.4.0(@types/react@18.2.37)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/theme-search-algolia': 3.4.0(@algolia/client-search@4.23.3)(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@types/react@18.2.37)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2) '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -14027,15 +14023,15 @@ snapshots: '@types/react': 18.3.3 react: 18.2.0 - '@docusaurus/theme-classic@3.4.0(@types/react@18.2.37)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/theme-classic@3.4.0(@types/react@18.2.37)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@docusaurus/plugin-content-blog': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-pages': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-blog': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-docs': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-pages': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/theme-translations': 3.4.0 '@docusaurus/types': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) @@ -14075,13 +14071,13 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@docusaurus/plugin-content-blog': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/plugin-content-pages': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-blog': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-docs': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-pages': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) '@types/history': 4.7.11 @@ -14113,13 +14109,13 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-search-algolia@3.4.0(@algolia/client-search@4.23.3)(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@types/react@18.2.37)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2)': + '@docusaurus/theme-search-algolia@3.4.0(@algolia/client-search@4.23.3)(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@types/react@18.2.37)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0)(typescript@5.5.2)': dependencies: '@docsearch/react': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.2.37)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.13.0) - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/logger': 3.4.0 - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-docs': 3.4.0(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/theme-translations': 3.4.0 '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.5.2) @@ -14371,10 +14367,10 @@ snapshots: cssesc: 3.0.0 immediate: 3.3.0 - '@easyops-cn/docusaurus-search-local@0.44.2(@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2))(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': + '@easyops-cn/docusaurus-search-local@0.44.2(@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2))(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2)': dependencies: - '@docusaurus/plugin-content-docs': 3.2.0(debug@4.3.4)(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.57.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/plugin-content-docs': 3.2.0(debug@4.3.4)(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) + '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@9.6.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.2) '@docusaurus/theme-translations': 3.2.0 '@docusaurus/utils': 3.2.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) '@docusaurus/utils-common': 3.2.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) @@ -14432,19 +14428,27 @@ snapshots: '@emotion/unitless@0.8.1': {} - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.6.0)': dependencies: - eslint: 8.57.0 + eslint: 9.6.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.10.0': {} - '@eslint/eslintrc@2.1.4': + '@eslint/config-array@0.17.0': + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.4(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 debug: 4.3.4(supports-color@5.5.0) - espree: 9.6.1 - globals: 13.24.0 + espree: 10.1.0 + globals: 14.0.0 ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -14453,7 +14457,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@9.6.0': {} + + '@eslint/object-schema@2.1.4': {} '@fastify/accept-negotiator@1.1.0': {} @@ -14524,17 +14530,9 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.4(supports-color@5.5.0) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.3.0': {} '@ioredis/commands@1.2.0': {} @@ -15506,6 +15504,53 @@ snapshots: micromark-util-character: 1.2.0 micromark-util-symbol: 1.1.0 + '@stylistic/eslint-plugin-js@2.3.0(eslint@9.6.0)': + dependencies: + '@types/eslint': 8.56.10 + acorn: 8.12.1 + eslint: 9.6.0 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 + + '@stylistic/eslint-plugin-jsx@2.3.0(eslint@9.6.0)': + dependencies: + '@stylistic/eslint-plugin-js': 2.3.0(eslint@9.6.0) + '@types/eslint': 8.56.10 + eslint: 9.6.0 + estraverse: 5.3.0 + picomatch: 4.0.2 + + '@stylistic/eslint-plugin-plus@2.3.0(eslint@9.6.0)(typescript@5.5.2)': + dependencies: + '@types/eslint': 8.56.10 + '@typescript-eslint/utils': 7.13.1(eslint@9.6.0)(typescript@5.5.2) + eslint: 9.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@stylistic/eslint-plugin-ts@2.3.0(eslint@9.6.0)(typescript@5.5.2)': + dependencies: + '@stylistic/eslint-plugin-js': 2.3.0(eslint@9.6.0) + '@types/eslint': 8.56.10 + '@typescript-eslint/utils': 7.13.1(eslint@9.6.0)(typescript@5.5.2) + eslint: 9.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@stylistic/eslint-plugin@2.3.0(eslint@9.6.0)(typescript@5.5.2)': + dependencies: + '@stylistic/eslint-plugin-js': 2.3.0(eslint@9.6.0) + '@stylistic/eslint-plugin-jsx': 2.3.0(eslint@9.6.0) + '@stylistic/eslint-plugin-plus': 2.3.0(eslint@9.6.0)(typescript@5.5.2) + '@stylistic/eslint-plugin-ts': 2.3.0(eslint@9.6.0)(typescript@5.5.2) + '@types/eslint': 8.56.10 + eslint: 9.6.0 + transitivePeerDependencies: + - supports-color + - typescript + '@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -16210,6 +16255,11 @@ snapshots: '@types/eslint': 8.56.9 '@types/estree': 1.0.5 + '@types/eslint@8.56.10': + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + '@types/eslint@8.56.9': dependencies: '@types/estree': 1.0.5 @@ -16295,8 +16345,6 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} - '@types/ldapjs@2.2.5': dependencies: '@types/node': 20.14.8 @@ -16476,15 +16524,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/eslint-plugin@8.0.0-alpha.39(@typescript-eslint/parser@8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)(typescript@5.5.2)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.13.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/scope-manager': 7.13.1 - '@typescript-eslint/type-utils': 7.13.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/utils': 7.13.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.13.1 - eslint: 8.57.0 + '@typescript-eslint/parser': 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/scope-manager': 8.0.0-alpha.39 + '@typescript-eslint/type-utils': 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/utils': 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/visitor-keys': 8.0.0-alpha.39 + eslint: 9.6.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -16494,14 +16542,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/parser@8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2)': dependencies: - '@typescript-eslint/scope-manager': 7.13.1 - '@typescript-eslint/types': 7.13.1 - '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.13.1 + '@typescript-eslint/scope-manager': 8.0.0-alpha.39 + '@typescript-eslint/types': 8.0.0-alpha.39 + '@typescript-eslint/typescript-estree': 8.0.0-alpha.39(typescript@5.5.2) + '@typescript-eslint/visitor-keys': 8.0.0-alpha.39 debug: 4.3.4(supports-color@5.5.0) - eslint: 8.57.0 + eslint: 9.6.0 optionalDependencies: typescript: 5.5.2 transitivePeerDependencies: @@ -16512,20 +16560,27 @@ snapshots: '@typescript-eslint/types': 7.13.1 '@typescript-eslint/visitor-keys': 7.13.1 - '@typescript-eslint/type-utils@7.13.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/scope-manager@8.0.0-alpha.39': dependencies: - '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.2) - '@typescript-eslint/utils': 7.13.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/types': 8.0.0-alpha.39 + '@typescript-eslint/visitor-keys': 8.0.0-alpha.39 + + '@typescript-eslint/type-utils@8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2)': + dependencies: + '@typescript-eslint/typescript-estree': 8.0.0-alpha.39(typescript@5.5.2) + '@typescript-eslint/utils': 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) debug: 4.3.4(supports-color@5.5.0) - eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.5.2) optionalDependencies: typescript: 5.5.2 transitivePeerDependencies: + - eslint - supports-color '@typescript-eslint/types@7.13.1': {} + '@typescript-eslint/types@8.0.0-alpha.39': {} + '@typescript-eslint/typescript-estree@7.13.1(typescript@5.5.2)': dependencies: '@typescript-eslint/types': 7.13.1 @@ -16534,20 +16589,46 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 - semver: 7.6.0 + semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.5.2) optionalDependencies: typescript: 5.5.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.13.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/typescript-estree@8.0.0-alpha.39(typescript@5.5.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@typescript-eslint/types': 8.0.0-alpha.39 + '@typescript-eslint/visitor-keys': 8.0.0-alpha.39 + debug: 4.3.4(supports-color@5.5.0) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.4 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.5.2) + optionalDependencies: + typescript: 5.5.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.13.1(eslint@9.6.0)(typescript@5.5.2)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) '@typescript-eslint/scope-manager': 7.13.1 '@typescript-eslint/types': 7.13.1 '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.2) - eslint: 8.57.0 + eslint: 9.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) + '@typescript-eslint/scope-manager': 8.0.0-alpha.39 + '@typescript-eslint/types': 8.0.0-alpha.39 + '@typescript-eslint/typescript-estree': 8.0.0-alpha.39(typescript@5.5.2) + eslint: 9.6.0 transitivePeerDependencies: - supports-color - typescript @@ -16557,6 +16638,11 @@ snapshots: '@typescript-eslint/types': 7.13.1 eslint-visitor-keys: 3.4.3 + '@typescript-eslint/visitor-keys@8.0.0-alpha.39': + dependencies: + '@typescript-eslint/types': 8.0.0-alpha.39 + eslint-visitor-keys: 3.4.3 + '@typescript/vfs@1.5.0': dependencies: debug: 4.3.4(supports-color@5.5.0) @@ -16738,10 +16824,16 @@ snapshots: dependencies: acorn: 8.11.3 + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + acorn-walk@8.3.2: {} acorn@8.11.3: {} + acorn@8.12.1: {} + address@1.2.2: {} agent-base@6.0.2: @@ -16960,6 +17052,7 @@ snapshots: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 + optional: true array-flatten@1.1.1: {} @@ -16973,6 +17066,7 @@ snapshots: es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 + optional: true array-tree-filter@2.1.0: {} @@ -16988,21 +17082,13 @@ snapshots: es-shim-unscopables: 1.0.2 optional: true - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 + optional: true array.prototype.flatmap@1.3.2: dependencies: @@ -17010,6 +17096,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 + optional: true array.prototype.toreversed@1.1.2: dependencies: @@ -17019,7 +17106,7 @@ snapshots: es-shim-unscopables: 1.0.2 optional: true - array.prototype.tosorted@1.1.3: + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -17038,6 +17125,7 @@ snapshots: get-intrinsic: 1.2.4 is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 + optional: true asn1@0.2.6: dependencies: @@ -17082,6 +17170,7 @@ snapshots: available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 + optional: true avvio@8.3.0: dependencies: @@ -18134,18 +18223,21 @@ snapshots: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + optional: true data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + optional: true data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + optional: true dataloader@2.2.2: {} @@ -18309,10 +18401,7 @@ snapshots: doctrine@2.1.0: dependencies: esutils: 2.0.3 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 + optional: true dom-accessibility-api@0.5.16: {} @@ -18430,11 +18519,6 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.16.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - enhanced-resolve@5.17.0: dependencies: graceful-fs: 4.2.11 @@ -18517,6 +18601,7 @@ snapshots: typed-array-length: 1.0.6 unbox-primitive: 1.0.2 which-typed-array: 1.1.15 + optional: true es-define-property@1.0.0: dependencies: @@ -18524,7 +18609,7 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.0.18: + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -18547,22 +18632,26 @@ snapshots: es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 + optional: true es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 hasown: 2.0.2 + optional: true es-shim-unscopables@1.0.2: dependencies: hasown: 2.0.2 + optional: true es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 + optional: true es2015-i18n-tag@1.6.1: {} @@ -18588,83 +18677,20 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.13.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0): - dependencies: - debug: 4.3.4(supports-color@5.5.0) - enhanced-resolve: 5.16.0 - eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - fast-glob: 3.3.2 - get-tsconfig: 4.7.3 - is-core-module: 2.13.1 - is-glob: 4.0.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 7.13.1(eslint@8.57.0)(typescript@5.5.2) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - hasown: 2.0.2 - is-core-module: 2.13.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 7.13.1(eslint@8.57.0)(typescript@5.5.2) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-license-header@0.6.1: dependencies: requireindex: 1.2.0 - eslint-plugin-react@7.34.1(eslint@8.57.0): + eslint-plugin-react@7.34.3(eslint@9.6.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.2 array.prototype.toreversed: 1.1.2 - array.prototype.tosorted: 1.1.3 + array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.0.18 - eslint: 8.57.0 + es-iterator-helpers: 1.0.19 + eslint: 9.6.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 @@ -18678,54 +18704,52 @@ snapshots: string.prototype.matchall: 4.0.11 optional: true - eslint-plugin-simple-import-sort@12.1.0(eslint@8.57.0): + eslint-plugin-simple-import-sort@12.1.1(eslint@9.6.0): dependencies: - eslint: 8.57.0 + eslint: 9.6.0 eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@7.2.2: + eslint-scope@8.0.1: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint@8.57.0: + eslint-visitor-keys@4.0.0: {} + + eslint@9.6.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/config-array': 0.17.0 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.6.0 '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4(supports-color@5.5.0) - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.0.1 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 @@ -18739,11 +18763,11 @@ snapshots: esm@3.2.25: {} - espree@9.6.1: + espree@10.1.0: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 4.0.0 esprima@4.0.1: {} @@ -19035,9 +19059,9 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 file-loader@6.2.0(webpack@5.92.1): dependencies: @@ -19112,11 +19136,10 @@ snapshots: micromatch: 4.0.5 resolve-dir: 1.0.1 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: flatted: 3.3.1 keyv: 4.5.4 - rimraf: 3.0.2 flat@5.0.2: {} @@ -19129,6 +19152,7 @@ snapshots: for-each@0.3.3: dependencies: is-callable: 1.2.7 + optional: true foreground-child@3.1.1: dependencies: @@ -19137,7 +19161,7 @@ snapshots: forever-agent@0.6.1: {} - fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.0)(typescript@5.5.2)(webpack@5.92.1): + fork-ts-checker-webpack-plugin@6.5.3(eslint@9.6.0)(typescript@5.5.2)(webpack@5.92.1): dependencies: '@babel/code-frame': 7.24.2 '@types/json-schema': 7.0.15 @@ -19155,7 +19179,7 @@ snapshots: typescript: 5.5.2 webpack: 5.92.1 optionalDependencies: - eslint: 8.57.0 + eslint: 9.6.0 form-data-encoder@2.1.4: {} @@ -19242,8 +19266,10 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 functions-have-names: 1.2.3 + optional: true - functions-have-names@1.2.3: {} + functions-have-names@1.2.3: + optional: true generate-function@2.3.1: dependencies: @@ -19274,10 +19300,7 @@ snapshots: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - - get-tsconfig@4.7.3: - dependencies: - resolve-pkg-maps: 1.0.0 + optional: true get-uri@6.0.3: dependencies: @@ -19378,13 +19401,14 @@ snapshots: globals@11.12.0: {} - globals@13.24.0: - dependencies: - type-fest: 0.20.2 + globals@14.0.0: {} + + globals@15.8.0: {} globalthis@1.0.3: dependencies: define-properties: 1.2.1 + optional: true globby@11.1.0: dependencies: @@ -19478,7 +19502,8 @@ snapshots: ajv: 6.12.6 har-schema: 2.0.0 - has-bigints@1.0.2: {} + has-bigints@1.0.2: + optional: true has-flag@3.0.0: {} @@ -19495,6 +19520,7 @@ snapshots: has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 + optional: true has-yarn@3.0.0: {} @@ -19886,6 +19912,7 @@ snapshots: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 + optional: true internmap@2.0.3: {} @@ -19945,6 +19972,7 @@ snapshots: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 + optional: true is-arrayish@0.2.1: {} @@ -19956,6 +19984,7 @@ snapshots: is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 + optional: true is-binary-path@2.1.0: dependencies: @@ -19965,8 +19994,10 @@ snapshots: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 + optional: true - is-callable@1.2.7: {} + is-callable@1.2.7: + optional: true is-ci@3.0.1: dependencies: @@ -19983,10 +20014,12 @@ snapshots: is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 + optional: true is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 + optional: true is-decimal@1.0.4: {} @@ -20030,13 +20063,15 @@ snapshots: is-map@2.0.3: optional: true - is-negative-zero@2.0.3: {} + is-negative-zero@2.0.3: + optional: true is-npm@6.0.0: {} is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 + optional: true is-number@7.0.0: {} @@ -20072,6 +20107,7 @@ snapshots: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 + optional: true is-regexp@1.0.0: {} @@ -20083,12 +20119,14 @@ snapshots: is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 + optional: true is-stream@2.0.1: {} is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 + optional: true is-subdir@1.2.0: dependencies: @@ -20097,10 +20135,12 @@ snapshots: is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 + optional: true is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 + optional: true is-typedarray@1.0.0: {} @@ -20114,6 +20154,7 @@ snapshots: is-weakref@1.0.2: dependencies: call-bind: 1.0.7 + optional: true is-weakset@2.0.3: dependencies: @@ -20137,7 +20178,8 @@ snapshots: isarray@1.0.0: {} - isarray@2.0.5: {} + isarray@2.0.5: + optional: true isexe@2.0.0: {} @@ -20642,10 +20684,6 @@ snapshots: dependencies: string-convert: 0.2.1 - json5@1.0.2: - dependencies: - minimist: 1.2.8 - json5@2.2.3: {} jsonfile@4.0.0: @@ -21810,12 +21848,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 + optional: true object.hasown@1.1.4: dependencies: @@ -21829,6 +21862,7 @@ snapshots: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 + optional: true obuf@1.1.2: {} @@ -22095,6 +22129,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pidusage@2.0.21: dependencies: safe-buffer: 5.2.1 @@ -22294,7 +22330,8 @@ snapshots: pony-cause@2.1.11: {} - possible-typed-array-names@1.0.0: {} + possible-typed-array-names@1.0.0: + optional: true postcss-calc@8.2.4(postcss@8.4.38): dependencies: @@ -23292,7 +23329,7 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 - react-dev-utils@12.0.1(eslint@8.57.0)(typescript@5.5.2)(webpack@5.92.1): + react-dev-utils@12.0.1(eslint@9.6.0)(typescript@5.5.2)(webpack@5.92.1): dependencies: '@babel/code-frame': 7.24.2 address: 1.2.2 @@ -23303,7 +23340,7 @@ snapshots: escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.0)(typescript@5.5.2)(webpack@5.92.1) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.6.0)(typescript@5.5.2)(webpack@5.92.1) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -23613,6 +23650,7 @@ snapshots: define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 + optional: true regexparam@2.0.2: {} @@ -23797,8 +23835,6 @@ snapshots: resolve-pathname@3.0.0: {} - resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.2: {} resolve@1.22.8: @@ -23872,6 +23908,7 @@ snapshots: get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 + optional: true safe-buffer@5.1.2: {} @@ -23888,6 +23925,7 @@ snapshots: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 + optional: true safe-regex2@2.0.0: dependencies: @@ -24058,6 +24096,7 @@ snapshots: es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + optional: true setimmediate@1.0.5: {} @@ -24364,18 +24403,21 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 + optional: true string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 + optional: true string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 + optional: true string_decoder@1.1.1: dependencies: @@ -24821,13 +24863,6 @@ snapshots: normalize-path: 3.0.0 plimit-lit: 1.6.1 - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -24910,6 +24945,7 @@ snapshots: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 + optional: true typed-array-byte-length@1.0.1: dependencies: @@ -24918,6 +24954,7 @@ snapshots: gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 + optional: true typed-array-byte-offset@1.0.2: dependencies: @@ -24927,6 +24964,7 @@ snapshots: gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 + optional: true typed-array-length@1.0.6: dependencies: @@ -24936,6 +24974,7 @@ snapshots: has-proto: 1.0.3 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 + optional: true typedarray-to-buffer@3.1.5: dependencies: @@ -24945,6 +24984,17 @@ snapshots: dependencies: ts-toolbelt: 9.6.0 + typescript-eslint@8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.0.0-alpha.39(@typescript-eslint/parser@8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/utils': 8.0.0-alpha.39(eslint@9.6.0)(typescript@5.5.2) + optionalDependencies: + typescript: 5.5.2 + transitivePeerDependencies: + - eslint + - supports-color + typescript@4.5.2: {} typescript@4.9.5: {} @@ -24969,6 +25019,7 @@ snapshots: has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + optional: true uncrypto@0.1.3: {} @@ -25384,6 +25435,7 @@ snapshots: is-number-object: 1.0.7 is-string: 1.0.7 is-symbol: 1.0.4 + optional: true which-builtin-type@1.1.3: dependencies: @@ -25423,6 +25475,7 @@ snapshots: for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 + optional: true which@1.3.1: dependencies: diff --git a/tsconfig.json b/tsconfig.json index 31db1bcac9..3d3f2c585d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,6 @@ "module": "commonjs", "moduleResolution": "node", "resolveJsonModule": true, - "isolatedModules": true - }, + "isolatedModules": true, + } } diff --git a/turbo.json b/turbo.json index 8aadb81242..fad557bdec 100644 --- a/turbo.json +++ b/turbo.json @@ -90,18 +90,11 @@ "build/**" ] }, - "//#lint:ts": { + "lint": { "inputs": [ + "**/*.proto", "**/*.tsx", "**/*.ts" - ], - "outputs": [ - ".eslintcache" - ] - }, - "lint": { - "inputs": [ - "**/*.proto" ] } }