);
+ /**
+ * override base path
+ *
+ * @type {string}
+ * @memberof Configuration
+ */
+ basePath?: string;
+ /**
+ * override server index
+ *
+ * @type {number}
+ * @memberof Configuration
+ */
+ serverIndex?: number;
+ /**
+ * base options for axios calls
+ *
+ * @type {any}
+ * @memberof Configuration
+ */
+ baseOptions?: any;
+ /**
+ * The FormData constructor that will be used to create multipart form data
+ * requests. You can inject this here so that execution environments that
+ * do not support the FormData class can still run the generated client.
+ *
+ * @type {new () => FormData}
+ */
+ formDataCtor?: new () => any;
+
+ constructor(param: ConfigurationParameters = {}) {
+ this.apiKey = param.apiKey;
+ this.username = param.username;
+ this.password = param.password;
+ this.accessToken = param.accessToken;
+ this.basePath = param.basePath;
+ this.serverIndex = param.serverIndex;
+ this.baseOptions = param.baseOptions;
+ this.formDataCtor = param.formDataCtor;
+ }
+
+ /**
+ * Check if the given MIME is a JSON MIME.
+ * JSON MIME examples:
+ * application/json
+ * application/json; charset=UTF8
+ * APPLICATION/JSON
+ * application/vnd.company+json
+ * @param mime - MIME (Multipurpose Internet Mail Extensions)
+ * @return True if the given MIME is JSON, false otherwise.
+ */
+ public isJsonMime(mime: string): boolean {
+ const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
+ return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
+ }
+}
diff --git a/data_migration_tools/client/src/api/git_push.sh b/data_migration_tools/client/src/api/git_push.sh
new file mode 100644
index 0000000..f53a75d
--- /dev/null
+++ b/data_migration_tools/client/src/api/git_push.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
+#
+# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
+
+git_user_id=$1
+git_repo_id=$2
+release_note=$3
+git_host=$4
+
+if [ "$git_host" = "" ]; then
+ git_host="github.com"
+ echo "[INFO] No command line input provided. Set \$git_host to $git_host"
+fi
+
+if [ "$git_user_id" = "" ]; then
+ git_user_id="GIT_USER_ID"
+ echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
+fi
+
+if [ "$git_repo_id" = "" ]; then
+ git_repo_id="GIT_REPO_ID"
+ echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
+fi
+
+if [ "$release_note" = "" ]; then
+ release_note="Minor update"
+ echo "[INFO] No command line input provided. Set \$release_note to $release_note"
+fi
+
+# Initialize the local directory as a Git repository
+git init
+
+# Adds the files in the local repository and stages them for commit.
+git add .
+
+# Commits the tracked changes and prepares them to be pushed to a remote repository.
+git commit -m "$release_note"
+
+# Sets the new remote
+git_remote=$(git remote)
+if [ "$git_remote" = "" ]; then # git remote not defined
+
+ if [ "$GIT_TOKEN" = "" ]; then
+ echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
+ git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
+ else
+ git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
+ fi
+
+fi
+
+git pull origin master
+
+# Pushes (Forces) the changes in the local repository up to the remote repository
+echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
+git push origin master 2>&1 | grep -v 'To https'
diff --git a/data_migration_tools/client/src/api/index.ts b/data_migration_tools/client/src/api/index.ts
new file mode 100644
index 0000000..c982723
--- /dev/null
+++ b/data_migration_tools/client/src/api/index.ts
@@ -0,0 +1,18 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * ODMSOpenAPI
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+export * from "./api";
+export * from "./configuration";
+
diff --git a/data_migration_tools/client/src/app/store.ts b/data_migration_tools/client/src/app/store.ts
index 7727f4a..d34f6a0 100644
--- a/data_migration_tools/client/src/app/store.ts
+++ b/data_migration_tools/client/src/app/store.ts
@@ -1,7 +1,11 @@
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
+import auth from "features/auth/authSlice";
+import ui from "features/ui/uiSlice";
export const store = configureStore({
reducer: {
+ auth,
+ ui,
},
});
diff --git a/data_migration_tools/client/src/common/error/code.ts b/data_migration_tools/client/src/common/error/code.ts
new file mode 100644
index 0000000..3c488da
--- /dev/null
+++ b/data_migration_tools/client/src/common/error/code.ts
@@ -0,0 +1,70 @@
+/*
+エラーコード作成方針
+E+6桁(数字)で構成する。
+- 1~2桁目の値は種類(業務エラー、システムエラー...)
+- 3~4桁目の値は原因箇所(トークン、DB、...)
+- 5~6桁目の値は任意の重複しない値
+ex)
+E00XXXX : システムエラー(通信エラーやDB接続失敗など)
+E01XXXX : 業務エラー
+EXX00XX : 内部エラー(内部プログラムのエラー)
+EXX01XX : トークンエラー(トークン認証関連)
+EXX02XX : DBエラー(DB関連)
+EXX03XX : ADB2Cエラー(DB関連)
+*/
+export const ErrorCodes = [
+ 'E009999', // 汎用エラー
+ 'E000101', // トークン形式不正エラー
+ 'E000102', // トークン有効期限切れエラー
+ 'E000103', // トークン非アクティブエラー
+ 'E000104', // トークン署名エラー
+ 'E000105', // トークン発行元エラー
+ 'E000106', // トークンアルゴリズムエラー
+ 'E000107', // トークン不足エラー
+ 'E000108', // トークン権限エラー
+ 'E000301', // ADB2Cへのリクエスト上限超過エラー
+ 'E000401', // IPアドレス未設定エラー
+ 'E000501', // リクエストID未設定エラー
+ 'E010001', // パラメータ形式不正エラー
+ 'E010201', // 未認証ユーザエラー
+ 'E010202', // 認証済ユーザエラー
+ 'E010203', // 管理ユーザ権限エラー
+ 'E010204', // ユーザ不在エラー
+ 'E010205', // DBのRoleが想定外の値エラー
+ 'E010206', // DBのTierが想定外の値エラー
+ 'E010207', // ユーザーのRole変更不可エラー
+ 'E010208', // ユーザーの暗号化パスワード不足エラー
+ 'E010209', // ユーザーの同意済み利用規約バージョンが最新でないエラー
+ 'E010301', // メールアドレス登録済みエラー
+ 'E010302', // authorId重複エラー
+ 'E010401', // PONumber重複エラー
+ 'E010501', // アカウント不在エラー
+ 'E010502', // アカウント情報変更不可エラー
+ 'E010503', // 代行操作不許可エラー
+ 'E010504', // アカウントロックエラー
+ 'E010601', // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
+ 'E010602', // タスク変更権限不足エラー
+ 'E010603', // タスク不在エラー
+ 'E010701', // Blobファイル不在エラー
+ 'E010801', // ライセンス不在エラー
+ 'E010802', // ライセンス取り込み済みエラー
+ 'E010803', // ライセンス発行済みエラー
+ 'E010804', // ライセンス不足エラー
+ 'E010805', // ライセンス有効期限切れエラー
+ 'E010806', // ライセンス割り当て不可エラー
+ 'E010807', // ライセンス割り当て解除済みエラー
+ 'E010808', // ライセンス注文キャンセル不可エラー
+ 'E010809', // ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
+ 'E010810', // ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
+ 'E010811', // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
+ 'E010812', // ライセンス未割当エラー
+ 'E010908', // タイピストグループ不在エラー
+ 'E010909', // タイピストグループ名重複エラー
+ 'E011001', // ワークタイプ重複エラー
+ 'E011002', // ワークタイプ登録上限超過エラー
+ 'E011003', // ワークタイプ不在エラー
+ 'E011004', // ワークタイプ使用中エラー
+ 'E012001', // テンプレートファイル不在エラー
+ 'E013001', // ワークフローのAuthorIDとWorktypeIDのペア重複エラー
+ 'E013002', // ワークフロー不在エラー
+] as const;
diff --git a/data_migration_tools/client/src/common/error/makeErrorResponse.ts b/data_migration_tools/client/src/common/error/makeErrorResponse.ts
new file mode 100644
index 0000000..0a677b4
--- /dev/null
+++ b/data_migration_tools/client/src/common/error/makeErrorResponse.ts
@@ -0,0 +1,10 @@
+import { errors } from './message';
+import { ErrorCodeType, ErrorResponse } from './types/types';
+
+export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => {
+ const msg = errors[errorcode];
+ return {
+ code: errorcode,
+ message: msg,
+ };
+};
diff --git a/data_migration_tools/client/src/common/error/message.ts b/data_migration_tools/client/src/common/error/message.ts
new file mode 100644
index 0000000..9383694
--- /dev/null
+++ b/data_migration_tools/client/src/common/error/message.ts
@@ -0,0 +1,59 @@
+import { Errors } from './types/types';
+
+// エラーコードとメッセージ対応表
+export const errors: Errors = {
+ E009999: 'Internal Server Error.',
+ E000101: 'Token invalid format Error.',
+ E000102: 'Token expired Error.',
+ E000103: 'Token not before Error',
+ E000104: 'Token invalid signature Error.',
+ E000105: 'Token invalid issuer Error.',
+ E000106: 'Token invalid algorithm Error.',
+ E000107: 'Token is not exist Error.',
+ E000108: 'Token authority failed Error.',
+ E000301: 'ADB2C request limit exceeded Error',
+ E000401: 'IP address not found Error.',
+ E000501: 'Request ID not found Error.',
+ E010001: 'Param invalid format Error.',
+ E010201: 'Email not verified user Error.',
+ E010202: 'Email already verified user Error.',
+ E010203: 'Administrator Permissions Error.',
+ E010204: 'User not Found Error.',
+ E010205: 'Role from DB is unexpected value Error.',
+ E010206: 'Tier from DB is unexpected value Error.',
+ E010207: 'User role change not allowed Error.',
+ E010208: 'User encryption password not found Error.',
+ E010209: 'Accepted term not latest Error.',
+ E010301: 'This email user already created Error',
+ E010302: 'This AuthorId already used Error',
+ E010401: 'This PoNumber already used Error',
+ E010501: 'Account not Found Error.',
+ E010502: 'Account information cannot be changed Error.',
+ E010503: 'Delegation not allowed Error.',
+ E010504: 'Account is locked Error.',
+ E010601: 'Task is not Editable Error',
+ E010602: 'No task edit permissions Error',
+ E010603: 'Task not found Error.',
+ E010701: 'File not found in Blob Storage Error.',
+ E010801: 'License not exist Error',
+ E010802: 'License already activated Error',
+ E010803: 'License already issued Error',
+ E010804: 'License shortage Error',
+ E010805: 'License is expired Error',
+ E010806: 'License is unavailable Error',
+ E010807: 'License is already deallocated Error',
+ E010808: 'Order cancel failed Error',
+ E010809: 'Already license order status changed Error',
+ E010810: 'Cancellation period expired error',
+ E010811: 'Already license allocated Error',
+ E010812: 'License not allocated Error',
+ E010908: 'Typist Group not exist Error',
+ E010909: 'Typist Group name already exist Error',
+ E011001: 'This WorkTypeID already used Error',
+ E011002: 'WorkTypeID create limit exceeded Error',
+ E011003: 'WorkTypeID not found Error',
+ E011004: 'WorkTypeID is in use Error',
+ E012001: 'Template file not found Error',
+ E013001: 'AuthorId and WorktypeId pair already exists Error',
+ E013002: 'Workflow not found Error',
+};
diff --git a/data_migration_tools/client/src/common/error/types/types.ts b/data_migration_tools/client/src/common/error/types/types.ts
new file mode 100644
index 0000000..8746924
--- /dev/null
+++ b/data_migration_tools/client/src/common/error/types/types.ts
@@ -0,0 +1,15 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { ErrorCodes } from '../code';
+
+export class ErrorResponse {
+ @ApiProperty()
+ message: string;
+ @ApiProperty()
+ code: string;
+}
+
+export type ErrorCodeType = (typeof ErrorCodes)[number];
+
+export type Errors = {
+ [P in ErrorCodeType]: string;
+};
diff --git a/data_migration_tools/client/src/common/errors/code.ts b/data_migration_tools/client/src/common/errors/code.ts
new file mode 100644
index 0000000..6237762
--- /dev/null
+++ b/data_migration_tools/client/src/common/errors/code.ts
@@ -0,0 +1,17 @@
+/*
+エラーコード作成方針
+E+6桁(数字)で構成する。
+- 1~2桁目の値は種類(業務エラー、システムエラー...)
+- 3~4桁目の値は原因箇所(トークン、DB、...)
+- 5~6桁目の値は任意の重複しない値
+ex)
+E00XXXX : システムエラー(通信エラーやDB接続失敗など)
+E01XXXX : 業務エラー
+EXX00XX : 内部エラー(内部プログラムのエラー)
+EXX01XX : トークンエラー(トークン認証関連)
+EXX02XX : DBエラー(DB関連)
+EXX03XX : ADB2Cエラー(DB関連)
+*/
+export const errorCodes = [
+ "E009999", // 汎用エラー
+] as const;
diff --git a/data_migration_tools/client/src/common/errors/index.ts b/data_migration_tools/client/src/common/errors/index.ts
new file mode 100644
index 0000000..1543e4c
--- /dev/null
+++ b/data_migration_tools/client/src/common/errors/index.ts
@@ -0,0 +1,3 @@
+export * from "./code";
+export * from "./types";
+export * from "./utils";
diff --git a/data_migration_tools/client/src/common/errors/types.ts b/data_migration_tools/client/src/common/errors/types.ts
new file mode 100644
index 0000000..f4a42b4
--- /dev/null
+++ b/data_migration_tools/client/src/common/errors/types.ts
@@ -0,0 +1,9 @@
+import { errorCodes } from "./code";
+
+export type ErrorObject = {
+ message: string;
+ code: ErrorCodeType;
+ statusCode?: number;
+};
+
+export type ErrorCodeType = (typeof errorCodes)[number];
diff --git a/data_migration_tools/client/src/common/errors/utils.ts b/data_migration_tools/client/src/common/errors/utils.ts
new file mode 100644
index 0000000..3dd2410
--- /dev/null
+++ b/data_migration_tools/client/src/common/errors/utils.ts
@@ -0,0 +1,101 @@
+import { AxiosError } from "axios";
+import { isError } from "lodash";
+import { ErrorResponse } from "../../api";
+import { errorCodes } from "./code";
+import { ErrorCodeType, ErrorObject } from "./types";
+
+export const createErrorObject = (error: unknown): ErrorObject => {
+ // 最低限通常のエラーかを判定
+ // Error以外のものがthrowされた場合
+ // 基本的にないはずだがプログラム上あるので拾う
+ if (!isError(error)) {
+ return {
+ message: "not error type.",
+ code: "E009999",
+ };
+ }
+
+ // Axiosエラー 通信してのエラーであるかを判定
+ if (!isAxiosError(error)) {
+ return {
+ message: "not axios error.",
+ code: "E009999",
+ };
+ }
+
+ const errorResponse = error.response;
+ if (!errorResponse) {
+ return {
+ message: error.message,
+ code: "E009999",
+ statusCode: errorResponse,
+ };
+ }
+
+ const { data } = errorResponse;
+
+ // 想定しているエラーレスポンスの型か判定
+ if (!isErrorResponse(data)) {
+ return {
+ message: error.message,
+ code: "E009999",
+ statusCode: errorResponse.status,
+ };
+ }
+
+ const { message, code } = data;
+
+ // 想定しているエラーコードかを判定
+ if (!isErrorCode(code)) {
+ return {
+ message,
+ code: "E009999",
+ statusCode: errorResponse.status,
+ };
+ }
+
+ return {
+ message,
+ code,
+ statusCode: errorResponse.status,
+ };
+};
+
+const isAxiosError = (e: unknown): e is AxiosError => {
+ const error = e as AxiosError;
+ return error?.isAxiosError ?? false;
+};
+
+const isErrorResponse = (error: unknown): error is ErrorResponse => {
+ const errorResponse = error as ErrorResponse;
+ if (
+ errorResponse === undefined ||
+ errorResponse.message === undefined ||
+ errorResponse.code === undefined
+ ) {
+ return false;
+ }
+
+ return true;
+};
+
+const isErrorCode = (errorCode: string): errorCode is ErrorCodeType =>
+ errorCodes.includes(errorCode as ErrorCodeType);
+
+export const isErrorObject = (
+ data: unknown
+): data is { error: ErrorObject } => {
+ if (
+ data &&
+ typeof data === "object" &&
+ "error" in data &&
+ typeof (data as { error: ErrorObject }).error === "object" &&
+ typeof (data as { error: ErrorObject }).error.message === "string" &&
+ typeof (data as { error: ErrorObject }).error.code === "string" &&
+ (typeof (data as { error: ErrorObject }).error.statusCode === "number" ||
+ (data as { error: ErrorObject }).error.statusCode === undefined)
+ ) {
+ return true;
+ }
+ return false;
+};
diff --git a/data_migration_tools/client/src/common/getBasePath.ts b/data_migration_tools/client/src/common/getBasePath.ts
index f1053d4..f94fdff 100644
--- a/data_migration_tools/client/src/common/getBasePath.ts
+++ b/data_migration_tools/client/src/common/getBasePath.ts
@@ -1,6 +1,6 @@
export const getBasePath = () => {
if (import.meta.env.VITE_STAGE === "local") {
- return "http://localhost:8180";
+ return "http://localhost:8280";
}
return window.location.origin;
};
diff --git a/data_migration_tools/client/src/features/auth/authSlice.ts b/data_migration_tools/client/src/features/auth/authSlice.ts
new file mode 100644
index 0000000..cb014fa
--- /dev/null
+++ b/data_migration_tools/client/src/features/auth/authSlice.ts
@@ -0,0 +1,15 @@
+import { createSlice } from "@reduxjs/toolkit";
+import type { AuthState } from "./state";
+import { initialConfig } from "./utils";
+
+const initialState: AuthState = {
+ configuration: initialConfig(),
+};
+
+export const authSlice = createSlice({
+ name: "auth",
+ initialState,
+ reducers: {},
+});
+
+export default authSlice.reducer;
diff --git a/data_migration_tools/client/src/features/auth/index.ts b/data_migration_tools/client/src/features/auth/index.ts
new file mode 100644
index 0000000..9dcef78
--- /dev/null
+++ b/data_migration_tools/client/src/features/auth/index.ts
@@ -0,0 +1,3 @@
+export * from "./authSlice";
+export * from "./state";
+export * from "./utils";
diff --git a/data_migration_tools/client/src/features/auth/state.ts b/data_migration_tools/client/src/features/auth/state.ts
new file mode 100644
index 0000000..e95c05f
--- /dev/null
+++ b/data_migration_tools/client/src/features/auth/state.ts
@@ -0,0 +1,5 @@
+import { ConfigurationParameters } from "../../api";
+
+export interface AuthState {
+ configuration: ConfigurationParameters;
+}
diff --git a/data_migration_tools/client/src/features/auth/utils.ts b/data_migration_tools/client/src/features/auth/utils.ts
new file mode 100644
index 0000000..764dbb1
--- /dev/null
+++ b/data_migration_tools/client/src/features/auth/utils.ts
@@ -0,0 +1,13 @@
+import { ConfigurationParameters } from "../../api";
+
+// 初期状態のAPI Config
+export const initialConfig = (): ConfigurationParameters => {
+ const config: ConfigurationParameters = {};
+ if (import.meta.env.VITE_STAGE === "local") {
+ config.basePath = "http://localhost:8280";
+ } else {
+ config.basePath = `${window.location.origin}/dictation/api`;
+ }
+
+ return config;
+};
diff --git a/data_migration_tools/client/src/features/delete/deleteSlice.ts b/data_migration_tools/client/src/features/delete/deleteSlice.ts
new file mode 100644
index 0000000..c89eac9
--- /dev/null
+++ b/data_migration_tools/client/src/features/delete/deleteSlice.ts
@@ -0,0 +1,25 @@
+import { createSlice } from "@reduxjs/toolkit";
+import { DeleteState } from "./state";
+import { deleteDataAsync } from "./operations";
+
+// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
+const initialState: DeleteState = {};
+
+export const deleteSlice = createSlice({
+ name: "detete",
+ initialState,
+ reducers: {},
+ extraReducers: (builder) => {
+ builder.addCase(deleteDataAsync.pending, () => {
+ /* Empty Object */
+ });
+ builder.addCase(deleteDataAsync.fulfilled, () => {
+ /* Empty Object */
+ });
+ builder.addCase(deleteDataAsync.rejected, () => {
+ /* Empty Object */
+ });
+ },
+});
+
+export default deleteSlice.reducer;
diff --git a/data_migration_tools/client/src/features/delete/index.ts b/data_migration_tools/client/src/features/delete/index.ts
new file mode 100644
index 0000000..b705514
--- /dev/null
+++ b/data_migration_tools/client/src/features/delete/index.ts
@@ -0,0 +1,3 @@
+export * from "./state";
+export * from "./deleteSlice";
+export * from "./operations";
diff --git a/data_migration_tools/client/src/features/delete/operations.ts b/data_migration_tools/client/src/features/delete/operations.ts
new file mode 100644
index 0000000..7c275cc
--- /dev/null
+++ b/data_migration_tools/client/src/features/delete/operations.ts
@@ -0,0 +1,47 @@
+import { createAsyncThunk } from "@reduxjs/toolkit";
+import { openSnackbar } from "../ui/uiSlice";
+import type { RootState } from "../../app/store";
+import { ErrorObject, createErrorObject } from "../../common/errors";
+import { DeleteApi } from "../../api/api";
+import { Configuration } from "../../api/configuration";
+
+export const deleteDataAsync = createAsyncThunk<
+ {
+ /* Empty Object */
+ },
+ void,
+ {
+ // rejectした時の返却値の型
+ rejectValue: {
+ error: ErrorObject;
+ };
+ }
+>("delete/deleteDataAsync", async (args, thunkApi) => {
+ // apiのConfigurationを取得する
+ const { getState } = thunkApi;
+ const state = getState() as RootState;
+ const { configuration } = state.auth;
+ const config = new Configuration(configuration);
+ const deleteApi = new DeleteApi(config);
+
+ try {
+ await deleteApi.deleteData();
+ thunkApi.dispatch(
+ openSnackbar({
+ level: "info",
+ message: "削除しました。",
+ })
+ );
+ return {};
+ } catch (e) {
+ const error = createErrorObject(e);
+ thunkApi.dispatch(
+ openSnackbar({
+ level: "error",
+ message: "削除に失敗しました。",
+ })
+ );
+
+ return thunkApi.rejectWithValue({ error });
+ }
+});
diff --git a/data_migration_tools/client/src/features/delete/state.ts b/data_migration_tools/client/src/features/delete/state.ts
new file mode 100644
index 0000000..3ac9dbd
--- /dev/null
+++ b/data_migration_tools/client/src/features/delete/state.ts
@@ -0,0 +1,2 @@
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface DeleteState {}
diff --git a/data_migration_tools/client/src/features/ui/constants.ts b/data_migration_tools/client/src/features/ui/constants.ts
new file mode 100644
index 0000000..9374dea
--- /dev/null
+++ b/data_migration_tools/client/src/features/ui/constants.ts
@@ -0,0 +1,2 @@
+// 標準のスナックバー表示時間(ミリ秒)
+export const DEFAULT_SNACKBAR_DURATION = 3000;
diff --git a/data_migration_tools/client/src/features/ui/index.ts b/data_migration_tools/client/src/features/ui/index.ts
new file mode 100644
index 0000000..464ecc2
--- /dev/null
+++ b/data_migration_tools/client/src/features/ui/index.ts
@@ -0,0 +1,5 @@
+export * from "./constants";
+export * from "./selectors";
+export * from "./state";
+export * from "./uiSlice";
+export * from "./types";
diff --git a/data_migration_tools/client/src/features/ui/selectors.ts b/data_migration_tools/client/src/features/ui/selectors.ts
new file mode 100644
index 0000000..3fa47c0
--- /dev/null
+++ b/data_migration_tools/client/src/features/ui/selectors.ts
@@ -0,0 +1,15 @@
+import type { RootState } from "app/store";
+import { SnackbarLevel } from "./types";
+
+export const selectSnackber = (
+ state: RootState
+): {
+ isOpen: boolean;
+ level: SnackbarLevel;
+ message: string;
+ duration?: number;
+} => {
+ const { isOpen, level, message, duration } = state.ui;
+
+ return { isOpen, level, message, duration };
+};
diff --git a/data_migration_tools/client/src/features/ui/state.ts b/data_migration_tools/client/src/features/ui/state.ts
new file mode 100644
index 0000000..c9bb660
--- /dev/null
+++ b/data_migration_tools/client/src/features/ui/state.ts
@@ -0,0 +1,8 @@
+import { SnackbarLevel } from "./types";
+
+export interface UIState {
+ isOpen: boolean;
+ level: SnackbarLevel;
+ message: string;
+ duration?: number;
+}
diff --git a/data_migration_tools/client/src/features/ui/types.ts b/data_migration_tools/client/src/features/ui/types.ts
new file mode 100644
index 0000000..cacf182
--- /dev/null
+++ b/data_migration_tools/client/src/features/ui/types.ts
@@ -0,0 +1 @@
+export type SnackbarLevel = "info" | "error";
diff --git a/data_migration_tools/client/src/features/ui/uiSlice.ts b/data_migration_tools/client/src/features/ui/uiSlice.ts
new file mode 100644
index 0000000..dd84534
--- /dev/null
+++ b/data_migration_tools/client/src/features/ui/uiSlice.ts
@@ -0,0 +1,38 @@
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import { SnackbarLevel } from "./types";
+import { UIState } from "./state";
+import { DEFAULT_SNACKBAR_DURATION } from "./constants";
+
+const initialState: UIState = {
+ isOpen: false,
+ level: "error",
+ message: "",
+};
+
+export const uiSlice = createSlice({
+ name: "ui",
+ initialState,
+ reducers: {
+ openSnackbar: (
+ state,
+ action: PayloadAction<{
+ level: SnackbarLevel;
+ message: string;
+ duration?: number;
+ }>
+ ) => {
+ const { level, message, duration } = action.payload;
+ state.isOpen = true;
+ state.level = level;
+ state.message = message;
+ state.duration =
+ level === "error" ? undefined : duration ?? DEFAULT_SNACKBAR_DURATION;
+ },
+ closeSnackbar: (state) => {
+ state.isOpen = false;
+ },
+ },
+});
+export const { openSnackbar, closeSnackbar } = uiSlice.actions;
+
+export default uiSlice.reducer;
diff --git a/data_migration_tools/client/src/pages/deletePage/index.tsx b/data_migration_tools/client/src/pages/deletePage/index.tsx
new file mode 100644
index 0000000..2643d87
--- /dev/null
+++ b/data_migration_tools/client/src/pages/deletePage/index.tsx
@@ -0,0 +1,32 @@
+import { AppDispatch } from "app/store";
+import { deleteDataAsync } from "features/delete";
+import React, { useCallback } from "react";
+import { useDispatch } from "react-redux";
+import { Link } from "react-router-dom";
+
+const DeletePage = (): JSX.Element => {
+ const dispatch: AppDispatch = useDispatch();
+
+ const onDelete = useCallback(() => {
+ if (
+ /* eslint-disable-next-line no-alert */
+ !window.confirm("本当に削除しますか?")
+ ) {
+ return;
+ }
+ dispatch(deleteDataAsync());
+ }, [dispatch]);
+
+ return (
+
+
データ削除ツール
+
+
+
return to TopPage
+
+ );
+};
+
+export default DeletePage;
diff --git a/data_migration_tools/client/src/pages/topPage/index.tsx b/data_migration_tools/client/src/pages/topPage/index.tsx
new file mode 100644
index 0000000..9d244b0
--- /dev/null
+++ b/data_migration_tools/client/src/pages/topPage/index.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+import { Link } from "react-router-dom";
+
+const TopPage = (): JSX.Element => {
+ console.log("DeletePage");
+ return (
+
+ データ削除ツール
+
+ return to TopPage
+
+ );
+};
+
+export default TopPage;
diff --git a/data_migration_tools/package-lock.json b/data_migration_tools/package-lock.json
new file mode 100644
index 0000000..343f405
--- /dev/null
+++ b/data_migration_tools/package-lock.json
@@ -0,0 +1,63 @@
+{
+ "name": "data_migration_tools",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "author": "",
+ "description": "",
+ "license": "UNLICENSED",
+ "private": true,
+ "packages": {},
+ "devDependencies": {
+ "@types/express": "^4.17.17",
+ "@types/multer": "^1.4.7",
+ "@types/node": "^20.2.3",
+ "eslint": "^8.0.1",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-prettier": "^4.0.0",
+ "jest": "28.0.3",
+ "license-checker": "^25.0.1",
+ "prettier": "^2.3.2",
+ "source-map-support": "^0.5.20",
+ "supertest": "^6.1.3",
+ "swagger-ui-express": "^4.5.0",
+ "ts-jest": "28.0.1",
+ "ts-loader": "^9.2.3",
+ "ts-node": "^10.0.0",
+ "tsconfig-paths": "4.0.0",
+ "typescript": "^4.3.5"
+ },
+ "jest": {
+ "collectCoverageFrom": [
+ "**/*.(t|j)s"
+ ],
+ "coverageDirectory": "../coverage",
+ "moduleFileExtensions": [
+ "js",
+ "json",
+ "ts"
+ ],
+ "rootDir": "src",
+ "testEnvironment": "node",
+ "testRegex": ".*\\.spec\\.ts$",
+ "transform": {
+ "^.+\\.(t|j)s$": "ts-jest"
+ }
+ },
+ "scripts": {
+ "build": "nest build && cp -r build dist",
+ "build:exe": "nest build && cp -r build dist && sh ./buildTool.sh",
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
+ "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
+ "prebuild": "rimraf dist",
+ "start": "nest start",
+ "start:debug": "nest start --debug --watch",
+ "start:dev": "nest start --watch",
+ "start:prod": "node dist/main",
+ "test": "jest",
+ "test:cov": "jest --coverage",
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
+ "test:watch": "jest --watch"
+ }
+}
diff --git a/data_migration_tools/server/.devcontainer/docker-compose.yml b/data_migration_tools/server/.devcontainer/docker-compose.yml
index 547e6db..9842f96 100644
--- a/data_migration_tools/server/.devcontainer/docker-compose.yml
+++ b/data_migration_tools/server/.devcontainer/docker-compose.yml
@@ -14,5 +14,11 @@ services:
- "8280"
environment:
- CHOKIDAR_USEPOLLING=true
+ networks:
+ - external
+networks:
+ external:
+ name: omds_network
+ external: true
volumes:
data_migration_tools_server_node_modules:
diff --git a/data_migration_tools/server/.env b/data_migration_tools/server/.env
index e69de29..c104e29 100644
--- a/data_migration_tools/server/.env
+++ b/data_migration_tools/server/.env
@@ -0,0 +1,5 @@
+DB_HOST=omds-mysql
+DB_PORT=3306
+DB_NAME=omds
+DB_USERNAME=omdsdbuser
+DB_PASSWORD=omdsdbpass
diff --git a/data_migration_tools/server/.env.local.example b/data_migration_tools/server/.env.local.example
index e69de29..0cee3f5 100644
--- a/data_migration_tools/server/.env.local.example
+++ b/data_migration_tools/server/.env.local.example
@@ -0,0 +1,36 @@
+STAGE=local
+NO_COLOR=TRUE
+CORS=TRUE
+PORT=8280
+# 開発環境ではADB2Cが別テナントになる都合上、環境変数を分けている
+TENANT_NAME=adb2codmsdev
+SIGNIN_FLOW_NAME=b2c_1_signin_dev
+ADB2C_TENANT_ID=xxxxxxxx
+ADB2C_CLIENT_ID=xxxxxxxx
+ADB2C_CLIENT_SECRET=xxxxxxxx
+ADB2C_ORIGIN=https://zzzzzzzzzz
+JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA5IZZNgDew9eGmuFTezwdHYLSaJvUPPIKYoiOeVLD1paWNI51\n7Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3yCTR6wcWR3PfFJrl9vh5SOo79koZ\noJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbWFJXnDe0DVXYXpJLb4LAlF2XAyYX0\nSYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qSfiL9zWk9dvHoKzSnfSDzDFoFcEoV\nchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//mBNNaDHv83Yuw3mGShT73iJ0JQdk\nTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GOOQIDAQABAoIBADrwp7u097+dK/tw\nWD61n3DIGAqg/lmFt8X4IH8MKLSE/FKr16CS1bqwOEuIM3ZdUtDeXd9Xs7IsyEPE\n5ZwuXK7DSF0M4+Mj8Ip49Q0Aww9aUoLQU9HGfgN/r4599GTrt31clZXA/6Mlighq\ncOZgCcEfdItz8OMu5SQuOIW4CKkCuaWnPOP26UqZocaXNZfpZH0iFLATMMH/TT8x\nay9ToHTQYE17ijdQ/EOLSwoeDV1CU1CIE3P4YfLJjvpKptly5dTevriHEzBi70Jx\n/KEPUn9Jj2gZafrUxRVhmMbm1zkeYxL3gsqRuTzRjEeeILuZhSJyCkQZyUNARxsg\nQY4DZfECgYEA+YLKUtmYTx60FS6DJ4s31TAsXY8kwhq/lB9E3GBZKDd0DPayXEeK\n4UWRQDTT6MI6fedW69FOZJ5sFLp8HQpcssb4Weq9PCpDhNTx8MCbdH3Um5QR3vfW\naKq/1XM8MDUnx5XcNYd87Aw3azvJAvOPr69as8IPnj6sKaRR9uQjbYUCgYEA6nfV\n5j0qmn0EJXZJblk4mvvjLLoWSs17j9YlrZJlJxXMDFRYtgnelv73xMxOMvcGoxn5\nifs7dpaM2x5EmA6jVU5sYaB/beZGEPWqPYGyjIwXPvUGAAv8Gbnvpp+xlSco/Dum\nIq0w+43ry5/xWh6CjfrvKV0J2bDOiJwPEdu/8iUCgYEAnBBSvL+dpN9vhFAzeOh7\nY71eAqcmNsLEUcG9MJqTKbSFwhYMOewF0iHRWHeylEPokhfBJn8kqYrtz4lVWFTC\n5o/Nh3BsLNXCpbMMIapXkeWiti1HgE9ErPMgSkJpwz18RDpYIqM8X+jEQS6D7HSr\nyxfDg+w+GJza0rEVE3hfMIECgYBw+KZ2VfhmEWBjEHhXE+QjQMR3s320MwebCUqE\nNCpKx8TWF/naVC0MwfLtvqbbBY0MHyLN6d//xpA9r3rLbRojqzKrY2KiuDYAS+3n\nzssRzxoQOozWju+8EYu30/ADdqfXyIHG6X3VZs87AGiQzGyJLmP3oR1y5y7MQa09\nJI16hQKBgHK5uwJhGa281Oo5/FwQ3uYLymbNwSGrsOJXiEu2XwJEXwVi2ELOKh4/\n03pBk3Kva3fIwEK+vCzDNnxShIQqBE76/2I1K1whOfoUehhYvKHGaXl2j70Zz9Ks\nrkGW1cx7p+yDqATDrwHBHTHFh5bUTTn8dN40n0e0W/llurpbBkJM\n-----END RSA PRIVATE KEY-----\n"
+JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd\nHYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3\nyCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW\nFJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS\nfiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//\nmBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO\nOQIDAQAB\n-----END PUBLIC KEY-----\n"
+SENDGRID_API_KEY=xxxxxxxxxxxxxxxx
+MAIL_FROM=xxxxx@xxxxx.xxxx
+NOTIFICATION_HUB_NAME=ntf-odms-dev
+NOTIFICATION_HUB_CONNECT_STRING=XXXXXXXXXXXXXXXXXX
+APP_DOMAIN=http://localhost:8081/
+STORAGE_TOKEN_EXPIRE_TIME=2
+STORAGE_ACCOUNT_NAME_US=saodmsusdev
+STORAGE_ACCOUNT_NAME_AU=saodmsaudev
+STORAGE_ACCOUNT_NAME_EU=saodmseudev
+STORAGE_ACCOUNT_KEY_US=XXXXXXXXXXXXXXXXXXXXXXX
+STORAGE_ACCOUNT_KEY_AU=XXXXXXXXXXXXXXXXXXXXXXX
+STORAGE_ACCOUNT_KEY_EU=XXXXXXXXXXXXXXXXXXXXXXX
+STORAGE_ACCOUNT_ENDPOINT_US=https://AAAAAAAAAAAAA
+STORAGE_ACCOUNT_ENDPOINT_AU=https://AAAAAAAAAAAAA
+STORAGE_ACCOUNT_ENDPOINT_EU=https://AAAAAAAAAAAAA
+ACCESS_TOKEN_LIFETIME_WEB=7200
+REFRESH_TOKEN_LIFETIME_WEB=86400
+REFRESH_TOKEN_LIFETIME_DEFAULT=2592000
+EMAIL_CONFIRM_LIFETIME=86400
+REDIS_HOST=redis-cache
+REDIS_PORT=6379
+REDIS_PASSWORD=omdsredispass
+ADB2C_CACHE_TTL=86400
\ No newline at end of file
diff --git a/data_migration_tools/server/buildTool.sh b/data_migration_tools/server/buildTool.sh
new file mode 100644
index 0000000..6a8930f
--- /dev/null
+++ b/data_migration_tools/server/buildTool.sh
@@ -0,0 +1,12 @@
+if [ ! -d "../tool" ]; then
+ mkdir "../tool"
+ echo "フォルダが作成されました。"
+else
+ rm -f ../tool/ODMS_DataTool.exe
+fi
+unzip ../baseNode.zip -d ../tool
+ncc build dist/main.js -o dist_single -a
+npx -y postject ../tool/ODMS_DataTool.exe NODE_JS_CODE dist_single/index.js --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --overwrite
+cp -r build ../tool
+cp .env ../tool
+cp .env.local.example ../tool
\ No newline at end of file
diff --git a/data_migration_tools/server/package-lock.json b/data_migration_tools/server/package-lock.json
index bc5a253..5987ef1 100644
--- a/data_migration_tools/server/package-lock.json
+++ b/data_migration_tools/server/package-lock.json
@@ -1,15 +1,17 @@
{
- "name": "server",
+ "name": "data_migration_tool",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
- "name": "server",
+ "name": "data_migration_tool",
"version": "0.0.1",
"license": "UNLICENSED",
"dependencies": {
+ "@azure/identity": "^4.0.1",
"@azure/storage-blob": "^12.14.0",
+ "@microsoft/microsoft-graph-client": "^3.0.7",
"@nestjs/common": "^9.3.9",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.3.9",
@@ -17,6 +19,7 @@
"@nestjs/serve-static": "^3.0.1",
"@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.3.9",
+ "@nestjs/typeorm": "^10.0.2",
"@openapitools/openapi-generator-cli": "^2.5.2",
"@vercel/ncc": "^0.36.1",
"axios": "^1.3.4",
@@ -24,9 +27,11 @@
"class-validator": "^0.14.0",
"cookie-parser": "^1.4.6",
"multer": "^1.4.5-lts.1",
+ "mysql2": "^2.3.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
- "swagger-cli": "^4.0.4"
+ "swagger-cli": "^4.0.4",
+ "typeorm": "^0.3.20"
},
"devDependencies": {
"@types/express": "^4.17.17",
@@ -239,11 +244,62 @@
}
},
"node_modules/@azure/core-auth": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
- "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.6.0.tgz",
+ "integrity": "sha512-3X9wzaaGgRaBCwhLQZDtFp5uLIXCPrGbwJNWPPugvL4xbIGgScv77YzzxToKGLAKvG9amDoofMoP+9hsH1vs1w==",
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-util": "^1.1.0",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
+ "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
+ "dependencies": {
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-client": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.8.0.tgz",
+ "integrity": "sha512-+gHS3gEzPlhyQBMoqVPOTeNH031R5DM/xpCvz72y38C09rg4Hui/1sJS/ujoisDZbbSHyuRLVWdFlwL0pIFwbg==",
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-auth": "^1.4.0",
+ "@azure/core-rest-pipeline": "^1.9.1",
+ "@azure/core-tracing": "^1.0.0",
+ "@azure/core-util": "^1.0.0",
+ "@azure/logger": "^1.0.0",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-client/node_modules/@azure/abort-controller": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
+ "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
+ "dependencies": {
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-client/node_modules/@azure/core-tracing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
+ "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
"dependencies": {
- "@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
@@ -307,6 +363,46 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@azure/core-rest-pipeline": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.14.0.tgz",
+ "integrity": "sha512-Tp4M6NsjCmn9L5p7HsW98eSOS7A0ibl3e5ntZglozT0XuD/0y6i36iW829ZbBq0qihlGgfaeFpkLjZ418KDm1Q==",
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-auth": "^1.4.0",
+ "@azure/core-tracing": "^1.0.1",
+ "@azure/core-util": "^1.3.0",
+ "@azure/logger": "^1.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
+ "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
+ "dependencies": {
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-rest-pipeline/node_modules/@azure/core-tracing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
+ "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
+ "dependencies": {
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/@azure/core-tracing": {
"version": "1.0.0-preview.13",
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz",
@@ -331,6 +427,41 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@azure/identity": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.0.1.tgz",
+ "integrity": "sha512-yRdgF03SFLqUMZZ1gKWt0cs0fvrDIkq2bJ6Oidqcoo5uM85YMBnXWMzYKK30XqIT76lkFyAaoAAy5knXhrG4Lw==",
+ "dependencies": {
+ "@azure/abort-controller": "^1.0.0",
+ "@azure/core-auth": "^1.5.0",
+ "@azure/core-client": "^1.4.0",
+ "@azure/core-rest-pipeline": "^1.1.0",
+ "@azure/core-tracing": "^1.0.0",
+ "@azure/core-util": "^1.3.0",
+ "@azure/logger": "^1.0.0",
+ "@azure/msal-browser": "^3.5.0",
+ "@azure/msal-node": "^2.5.1",
+ "events": "^3.0.0",
+ "jws": "^4.0.0",
+ "open": "^8.0.0",
+ "stoppable": "^1.1.0",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/identity/node_modules/@azure/core-tracing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
+ "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
+ "dependencies": {
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/@azure/logger": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
@@ -342,6 +473,46 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@azure/msal-browser": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.10.0.tgz",
+ "integrity": "sha512-mnmi8dCXVNZI+AGRq0jKQ3YiodlIC4W9npr6FCB9WN6NQT+6rq+cIlxgUb//BjLyzKsnYo+i4LROGeMyU+6v1A==",
+ "dependencies": {
+ "@azure/msal-common": "14.7.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-common": {
+ "version": "14.7.1",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.7.1.tgz",
+ "integrity": "sha512-v96btzjM7KrAu4NSEdOkhQSTGOuNUIIsUdB8wlyB9cdgl5KqEKnTonHUZ8+khvZ6Ap542FCErbnTyDWl8lZ2rA==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-node": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.4.tgz",
+ "integrity": "sha512-nNvEPx009/80UATCToF+29NZYocn01uKrB91xtFr7bSqkqO1PuQGXRyYwryWRztUrYZ1YsSbw9A+LmwOhpVvcg==",
+ "dependencies": {
+ "@azure/msal-common": "14.7.1",
+ "jsonwebtoken": "^9.0.0",
+ "uuid": "^8.3.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@azure/msal-node/node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/@azure/storage-blob": {
"version": "12.14.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz",
@@ -1026,7 +1197,7 @@
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
@@ -1038,7 +1209,7 @@
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
@@ -1173,6 +1344,95 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1588,7 +1848,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6.0.0"
}
@@ -1632,7 +1892,7 @@
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
- "dev": true
+ "devOptional": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.17",
@@ -1657,6 +1917,32 @@
"node": ">=8"
}
},
+ "node_modules/@microsoft/microsoft-graph-client": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz",
+ "integrity": "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@azure/identity": {
+ "optional": true
+ },
+ "@azure/msal-browser": {
+ "optional": true
+ },
+ "buffer": {
+ "optional": true
+ },
+ "stream-browserify": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@nestjs/axios": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz",
@@ -1968,6 +2254,33 @@
}
}
},
+ "node_modules/@nestjs/typeorm": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.2.tgz",
+ "integrity": "sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==",
+ "dependencies": {
+ "uuid": "9.0.1"
+ },
+ "peerDependencies": {
+ "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0",
+ "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0",
+ "reflect-metadata": "^0.1.13 || ^0.2.0",
+ "rxjs": "^7.2.0",
+ "typeorm": "^0.3.0"
+ }
+ },
+ "node_modules/@nestjs/typeorm/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2243,6 +2556,15 @@
"node": ">=8.0.0"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@sinclair/typebox": {
"version": "0.24.51",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
@@ -2267,29 +2589,42 @@
"@sinonjs/commons": "^1.7.0"
}
},
+ "node_modules/@sqltools/formatter": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
+ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
- "dev": true
+ "devOptional": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
- "dev": true
+ "devOptional": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
- "dev": true
+ "devOptional": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/babel__core": {
"version": "7.20.0",
@@ -2775,7 +3110,7 @@
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
- "dev": true,
+ "devOptional": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2806,11 +3141,22 @@
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=0.4.0"
}
},
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
@@ -2886,6 +3232,11 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+ },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -2899,6 +3250,14 @@
"node": ">= 8"
}
},
+ "node_modules/app-root-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
+ "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
@@ -2908,7 +3267,7 @@
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
- "dev": true
+ "devOptional": true
},
"node_modules/argparse": {
"version": "1.0.10",
@@ -3220,6 +3579,11 @@
"ieee754": "^1.1.13"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3380,6 +3744,26 @@
"node": ">=8"
}
},
+ "node_modules/cli-highlight": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
+ "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "highlight.js": "^10.7.1",
+ "mz": "^2.4.0",
+ "parse5": "^5.1.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.0",
+ "yargs": "^16.0.0"
+ },
+ "bin": {
+ "highlight": "bin/highlight"
+ },
+ "engines": {
+ "node": ">=8.0.0",
+ "npm": ">=5.0.0"
+ }
+ },
"node_modules/cli-spinners": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
@@ -3650,13 +4034,12 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -3681,11 +4064,15 @@
"url": "https://opencollective.com/date-fns"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.10",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -3747,6 +4134,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3755,6 +4150,14 @@
"node": ">=0.4.0"
}
},
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -3795,7 +4198,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=0.3.1"
}
@@ -3839,6 +4242,11 @@
"node": ">=12"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+ },
"node_modules/easy-table": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
@@ -3847,6 +4255,14 @@
"wcwidth": ">=1.0.1"
}
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -4598,6 +5014,32 @@
}
}
},
+ "node_modules/foreground-child": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+ "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/foreground-child/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -4680,6 +5122,14 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "node_modules/generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "dependencies": {
+ "is-property": "^1.0.2"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4822,6 +5272,14 @@
"node": ">=8"
}
},
+ "node_modules/highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -4849,6 +5307,31 @@
"node": ">= 0.8"
}
},
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -5019,6 +5502,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -5083,6 +5580,11 @@
"node": ">=8"
}
},
+ "node_modules/is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
+ },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -5106,6 +5608,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -5114,8 +5627,7 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.0",
@@ -5209,6 +5721,23 @@
"node": ">=6"
}
},
+ "node_modules/jackspeak": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+ "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
"node_modules/jest": {
"version": "28.0.3",
"resolved": "https://registry.npmjs.org/jest/-/jest-28.0.3.tgz",
@@ -6238,6 +6767,65 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "dependencies": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -6420,6 +7008,36 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -6432,6 +7050,11 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
"node_modules/log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -6447,6 +7070,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -6484,7 +7112,7 @@
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "dev": true
+ "devOptional": true
},
"node_modules/makeerror": {
"version": "1.0.12",
@@ -6589,6 +7217,14 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
+ "node_modules/minipass": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+ "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
@@ -6603,8 +7239,7 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/multer": {
"version": "1.4.5-lts.1",
@@ -6628,6 +7263,80 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
+ "node_modules/mysql2": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
+ "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
+ "dependencies": {
+ "denque": "^2.0.1",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.6.3",
+ "long": "^4.0.0",
+ "lru-cache": "^6.0.0",
+ "named-placeholders": "^1.1.2",
+ "seq-queue": "^0.0.5",
+ "sqlstring": "^2.3.2"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/mysql2/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mysql2/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mysql2/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/named-placeholders": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+ "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+ "dependencies": {
+ "lru-cache": "^7.14.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/named-placeholders/node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -6790,6 +7499,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/open": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+ "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/openapi-types": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz",
@@ -6930,6 +7655,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "dependencies": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -6958,7 +7701,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -6969,6 +7711,29 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
+ "node_modules/path-scurry": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
+ "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+ "dependencies": {
+ "lru-cache": "^9.1.1 || ^10.0.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
+ "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+ "engines": {
+ "node": "14 || >=16.14"
+ }
+ },
"node_modules/path-to-regexp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
@@ -7575,7 +8340,6 @@
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -7590,7 +8354,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -7601,8 +8364,7 @@
"node_modules/semver/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/send": {
"version": "0.18.0",
@@ -7645,6 +8407,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
+ "node_modules/seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+ },
"node_modules/serialize-javascript": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
@@ -7679,11 +8446,22 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -7695,7 +8473,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -7831,6 +8608,14 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
},
+ "node_modules/sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/stack-utils": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -7860,6 +8645,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/stoppable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
+ "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
+ "engines": {
+ "node": ">=4",
+ "npm": ">=6"
+ }
+ },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -7907,6 +8701,20 @@
"node": ">=8"
}
},
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -7918,6 +8726,18 @@
"node": ">=8"
}
},
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-bom": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
@@ -8217,6 +9037,25 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -8356,7 +9195,7 @@
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -8478,11 +9317,239 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
+ "node_modules/typeorm": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz",
+ "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==",
+ "dependencies": {
+ "@sqltools/formatter": "^1.2.5",
+ "app-root-path": "^3.1.0",
+ "buffer": "^6.0.3",
+ "chalk": "^4.1.2",
+ "cli-highlight": "^2.1.11",
+ "dayjs": "^1.11.9",
+ "debug": "^4.3.4",
+ "dotenv": "^16.0.3",
+ "glob": "^10.3.10",
+ "mkdirp": "^2.1.3",
+ "reflect-metadata": "^0.2.1",
+ "sha.js": "^2.4.11",
+ "tslib": "^2.5.0",
+ "uuid": "^9.0.0",
+ "yargs": "^17.6.2"
+ },
+ "bin": {
+ "typeorm": "cli.js",
+ "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js",
+ "typeorm-ts-node-esm": "cli-ts-node-esm.js"
+ },
+ "engines": {
+ "node": ">=16.13.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/typeorm"
+ },
+ "peerDependencies": {
+ "@google-cloud/spanner": "^5.18.0",
+ "@sap/hana-client": "^2.12.25",
+ "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0",
+ "hdb-pool": "^0.1.6",
+ "ioredis": "^5.0.4",
+ "mongodb": "^5.8.0",
+ "mssql": "^9.1.1 || ^10.0.1",
+ "mysql2": "^2.2.5 || ^3.0.1",
+ "oracledb": "^6.3.0",
+ "pg": "^8.5.1",
+ "pg-native": "^3.0.0",
+ "pg-query-stream": "^4.0.0",
+ "redis": "^3.1.1 || ^4.0.0",
+ "sql.js": "^1.4.0",
+ "sqlite3": "^5.0.3",
+ "ts-node": "^10.7.0",
+ "typeorm-aurora-data-api-driver": "^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@google-cloud/spanner": {
+ "optional": true
+ },
+ "@sap/hana-client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "hdb-pool": {
+ "optional": true
+ },
+ "ioredis": {
+ "optional": true
+ },
+ "mongodb": {
+ "optional": true
+ },
+ "mssql": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "oracledb": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "pg-native": {
+ "optional": true
+ },
+ "pg-query-stream": {
+ "optional": true
+ },
+ "redis": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ },
+ "typeorm-aurora-data-api-driver": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typeorm/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/typeorm/node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/typeorm/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/typeorm/node_modules/glob": {
+ "version": "10.3.10",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+ "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^2.3.5",
+ "minimatch": "^9.0.1",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+ "path-scurry": "^1.10.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typeorm/node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typeorm/node_modules/mkdirp": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
+ "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typeorm/node_modules/reflect-metadata": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
+ "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw=="
+ },
+ "node_modules/typeorm/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/typeorm/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
- "dev": true,
+ "devOptional": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -8583,7 +9650,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
- "dev": true
+ "devOptional": true
},
"node_modules/v8-to-istanbul": {
"version": "9.1.0",
@@ -8732,7 +9799,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -8773,6 +9839,23 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -8862,7 +9945,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
}
@@ -9032,12 +10115,55 @@
}
},
"@azure/core-auth": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
- "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.6.0.tgz",
+ "integrity": "sha512-3X9wzaaGgRaBCwhLQZDtFp5uLIXCPrGbwJNWPPugvL4xbIGgScv77YzzxToKGLAKvG9amDoofMoP+9hsH1vs1w==",
"requires": {
- "@azure/abort-controller": "^1.0.0",
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-util": "^1.1.0",
"tslib": "^2.2.0"
+ },
+ "dependencies": {
+ "@azure/abort-controller": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
+ "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ }
+ }
+ },
+ "@azure/core-client": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.8.0.tgz",
+ "integrity": "sha512-+gHS3gEzPlhyQBMoqVPOTeNH031R5DM/xpCvz72y38C09rg4Hui/1sJS/ujoisDZbbSHyuRLVWdFlwL0pIFwbg==",
+ "requires": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-auth": "^1.4.0",
+ "@azure/core-rest-pipeline": "^1.9.1",
+ "@azure/core-tracing": "^1.0.0",
+ "@azure/core-util": "^1.0.0",
+ "@azure/logger": "^1.0.0",
+ "tslib": "^2.2.0"
+ },
+ "dependencies": {
+ "@azure/abort-controller": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
+ "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ },
+ "@azure/core-tracing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
+ "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ }
}
},
"@azure/core-http": {
@@ -9087,6 +10213,39 @@
"tslib": "^2.2.0"
}
},
+ "@azure/core-rest-pipeline": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.14.0.tgz",
+ "integrity": "sha512-Tp4M6NsjCmn9L5p7HsW98eSOS7A0ibl3e5ntZglozT0XuD/0y6i36iW829ZbBq0qihlGgfaeFpkLjZ418KDm1Q==",
+ "requires": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-auth": "^1.4.0",
+ "@azure/core-tracing": "^1.0.1",
+ "@azure/core-util": "^1.3.0",
+ "@azure/logger": "^1.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "tslib": "^2.2.0"
+ },
+ "dependencies": {
+ "@azure/abort-controller": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
+ "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ },
+ "@azure/core-tracing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
+ "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ }
+ }
+ },
"@azure/core-tracing": {
"version": "1.0.0-preview.13",
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz",
@@ -9105,6 +10264,37 @@
"tslib": "^2.2.0"
}
},
+ "@azure/identity": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.0.1.tgz",
+ "integrity": "sha512-yRdgF03SFLqUMZZ1gKWt0cs0fvrDIkq2bJ6Oidqcoo5uM85YMBnXWMzYKK30XqIT76lkFyAaoAAy5knXhrG4Lw==",
+ "requires": {
+ "@azure/abort-controller": "^1.0.0",
+ "@azure/core-auth": "^1.5.0",
+ "@azure/core-client": "^1.4.0",
+ "@azure/core-rest-pipeline": "^1.1.0",
+ "@azure/core-tracing": "^1.0.0",
+ "@azure/core-util": "^1.3.0",
+ "@azure/logger": "^1.0.0",
+ "@azure/msal-browser": "^3.5.0",
+ "@azure/msal-node": "^2.5.1",
+ "events": "^3.0.0",
+ "jws": "^4.0.0",
+ "open": "^8.0.0",
+ "stoppable": "^1.1.0",
+ "tslib": "^2.2.0"
+ },
+ "dependencies": {
+ "@azure/core-tracing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
+ "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ }
+ }
+ },
"@azure/logger": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
@@ -9113,6 +10303,36 @@
"tslib": "^2.2.0"
}
},
+ "@azure/msal-browser": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.10.0.tgz",
+ "integrity": "sha512-mnmi8dCXVNZI+AGRq0jKQ3YiodlIC4W9npr6FCB9WN6NQT+6rq+cIlxgUb//BjLyzKsnYo+i4LROGeMyU+6v1A==",
+ "requires": {
+ "@azure/msal-common": "14.7.1"
+ }
+ },
+ "@azure/msal-common": {
+ "version": "14.7.1",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.7.1.tgz",
+ "integrity": "sha512-v96btzjM7KrAu4NSEdOkhQSTGOuNUIIsUdB8wlyB9cdgl5KqEKnTonHUZ8+khvZ6Ap542FCErbnTyDWl8lZ2rA=="
+ },
+ "@azure/msal-node": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.4.tgz",
+ "integrity": "sha512-nNvEPx009/80UATCToF+29NZYocn01uKrB91xtFr7bSqkqO1PuQGXRyYwryWRztUrYZ1YsSbw9A+LmwOhpVvcg==",
+ "requires": {
+ "@azure/msal-common": "14.7.1",
+ "jsonwebtoken": "^9.0.0",
+ "uuid": "^8.3.0"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+ }
+ }
+ },
"@azure/storage-blob": {
"version": "12.14.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz",
@@ -9640,7 +10860,7 @@
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jridgewell/trace-mapping": "0.3.9"
},
@@ -9649,7 +10869,7 @@
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
@@ -9753,6 +10973,64 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "requires": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="
+ },
+ "ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+ },
+ "emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+ },
+ "string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "requires": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ },
+ "wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "requires": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ }
+ }
+ }
+ },
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -10078,7 +11356,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
- "dev": true
+ "devOptional": true
},
"@jridgewell/set-array": {
"version": "1.1.2",
@@ -10115,7 +11393,7 @@
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
- "dev": true
+ "devOptional": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.17",
@@ -10137,6 +11415,15 @@
"resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.0.1.tgz",
"integrity": "sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g=="
},
+ "@microsoft/microsoft-graph-client": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz",
+ "integrity": "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==",
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "tslib": "^2.2.0"
+ }
+ },
"@nestjs/axios": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz",
@@ -10309,6 +11596,21 @@
"tslib": "2.5.0"
}
},
+ "@nestjs/typeorm": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.2.tgz",
+ "integrity": "sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==",
+ "requires": {
+ "uuid": "9.0.1"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
+ }
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -10493,6 +11795,12 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz",
"integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA=="
},
+ "@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "optional": true
+ },
"@sinclair/typebox": {
"version": "0.24.51",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
@@ -10517,29 +11825,39 @@
"@sinonjs/commons": "^1.7.0"
}
},
+ "@sqltools/formatter": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
+ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="
+ },
+ "@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
+ },
"@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
- "dev": true
+ "devOptional": true
},
"@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
- "dev": true
+ "devOptional": true
},
"@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
- "dev": true
+ "devOptional": true
},
"@tsconfig/node16": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
- "dev": true
+ "devOptional": true
},
"@types/babel__core": {
"version": "7.20.0",
@@ -11018,7 +12336,7 @@
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
- "dev": true
+ "devOptional": true
},
"acorn-import-assertions": {
"version": "1.8.0",
@@ -11039,7 +12357,15 @@
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
- "dev": true
+ "devOptional": true
+ },
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "requires": {
+ "debug": "4"
+ }
},
"ajv": {
"version": "8.12.0",
@@ -11086,6 +12412,11 @@
"color-convert": "^2.0.1"
}
},
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+ },
"anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -11096,6 +12427,11 @@
"picomatch": "^2.0.4"
}
},
+ "app-root-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
+ "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA=="
+ },
"append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
@@ -11105,7 +12441,7 @@
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
- "dev": true
+ "devOptional": true
},
"argparse": {
"version": "1.0.10",
@@ -11340,6 +12676,11 @@
"ieee754": "^1.1.13"
}
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -11451,6 +12792,19 @@
"restore-cursor": "^3.1.0"
}
},
+ "cli-highlight": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
+ "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
+ "requires": {
+ "chalk": "^4.0.0",
+ "highlight.js": "^10.7.1",
+ "mz": "^2.4.0",
+ "parse5": "^5.1.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.0",
+ "yargs": "^16.0.0"
+ }
+ },
"cli-spinners": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
@@ -11661,13 +13015,12 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "dev": true
+ "devOptional": true
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -11682,11 +13035,15 @@
"@babel/runtime": "^7.21.0"
}
},
+ "dayjs": {
+ "version": "1.11.10",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+ },
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -11728,11 +13085,21 @@
"clone": "^1.0.2"
}
},
+ "define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
+ },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
+ "denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
+ },
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -11763,7 +13130,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
+ "devOptional": true
},
"diff-sequences": {
"version": "27.5.1",
@@ -11792,6 +13159,11 @@
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
"integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A=="
},
+ "eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+ },
"easy-table": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
@@ -11800,6 +13172,14 @@
"wcwidth": ">=1.0.1"
}
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -12382,6 +13762,22 @@
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw=="
},
+ "foreground-child": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+ "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "dependencies": {
+ "signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
+ }
+ }
+ },
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -12445,6 +13841,14 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "requires": {
+ "is-property": "^1.0.2"
+ }
+ },
"gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -12542,6 +13946,11 @@
"integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
"dev": true
},
+ "highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
+ },
"hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -12566,6 +13975,25 @@
"toidentifier": "1.0.1"
}
},
+ "http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "requires": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
"human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -12685,6 +14113,11 @@
"has": "^1.0.3"
}
},
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
+ },
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -12728,6 +14161,11 @@
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true
},
+ "is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
+ },
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -12739,6 +14177,14 @@
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
},
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -12747,8 +14193,7 @@
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"istanbul-lib-coverage": {
"version": "3.2.0",
@@ -12822,6 +14267,15 @@
"resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz",
"integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q=="
},
+ "jackspeak": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+ "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+ "requires": {
+ "@isaacs/cliui": "^8.0.2",
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
"jest": {
"version": "28.0.3",
"resolved": "https://registry.npmjs.org/jest/-/jest-28.0.3.tgz",
@@ -13622,6 +15076,63 @@
"universalify": "^2.0.0"
}
},
+ "jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "dependencies": {
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ }
+ }
+ },
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -13767,6 +15278,36 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -13779,6 +15320,11 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
"log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -13788,6 +15334,11 @@
"is-unicode-supported": "^0.1.0"
}
},
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -13818,7 +15369,7 @@
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "dev": true
+ "devOptional": true
},
"makeerror": {
"version": "1.0.12",
@@ -13896,6 +15447,11 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
+ "minipass": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+ "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ=="
+ },
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
@@ -13907,8 +15463,7 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"multer": {
"version": "1.4.5-lts.1",
@@ -13929,6 +15484,69 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
+ "mysql2": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
+ "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
+ "requires": {
+ "denque": "^2.0.1",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.6.3",
+ "long": "^4.0.0",
+ "lru-cache": "^6.0.0",
+ "named-placeholders": "^1.1.2",
+ "seq-queue": "^0.0.5",
+ "sqlstring": "^2.3.2"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
+ "mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "requires": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "named-placeholders": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+ "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+ "requires": {
+ "lru-cache": "^7.14.1"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="
+ }
+ }
+ },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -14052,6 +15670,16 @@
"mimic-fn": "^2.1.0"
}
},
+ "open": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+ "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+ "requires": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ }
+ },
"openapi-types": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz",
@@ -14153,6 +15781,26 @@
"lines-and-columns": "^1.1.6"
}
},
+ "parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "requires": {
+ "parse5": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ }
+ }
+ },
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -14171,8 +15819,7 @@
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse": {
"version": "1.0.7",
@@ -14180,6 +15827,22 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
+ "path-scurry": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
+ "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+ "requires": {
+ "lru-cache": "^9.1.1 || ^10.0.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
+ "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="
+ }
+ }
+ },
"path-to-regexp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
@@ -14622,7 +16285,6 @@
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dev": true,
"requires": {
"lru-cache": "^6.0.0"
},
@@ -14631,7 +16293,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"requires": {
"yallist": "^4.0.0"
}
@@ -14639,8 +16300,7 @@
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
@@ -14686,6 +16346,11 @@
}
}
},
+ "seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+ },
"serialize-javascript": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
@@ -14717,11 +16382,19 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
@@ -14729,8 +16402,7 @@
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"side-channel": {
"version": "1.0.4",
@@ -14853,6 +16525,11 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
},
+ "sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="
+ },
"stack-utils": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -14875,6 +16552,11 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
},
+ "stoppable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
+ "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="
+ },
"streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -14915,6 +16597,16 @@
"strip-ansi": "^6.0.1"
}
},
+ "string-width-cjs": {
+ "version": "npm:string-width@4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -14923,6 +16615,14 @@
"ansi-regex": "^5.0.1"
}
},
+ "strip-ansi-cjs": {
+ "version": "npm:strip-ansi@6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
"strip-bom": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
@@ -15126,6 +16826,22 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "requires": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "requires": {
+ "thenify": ">= 3.1.0 < 4"
+ }
+ },
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -15213,7 +16929,7 @@
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -15294,11 +17010,111 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
+ "typeorm": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz",
+ "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==",
+ "requires": {
+ "@sqltools/formatter": "^1.2.5",
+ "app-root-path": "^3.1.0",
+ "buffer": "^6.0.3",
+ "chalk": "^4.1.2",
+ "cli-highlight": "^2.1.11",
+ "dayjs": "^1.11.9",
+ "debug": "^4.3.4",
+ "dotenv": "^16.0.3",
+ "glob": "^10.3.10",
+ "mkdirp": "^2.1.3",
+ "reflect-metadata": "^0.2.1",
+ "sha.js": "^2.4.11",
+ "tslib": "^2.5.0",
+ "uuid": "^9.0.0",
+ "yargs": "^17.6.2"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "glob": {
+ "version": "10.3.10",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+ "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "requires": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^2.3.5",
+ "minimatch": "^9.0.1",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+ "path-scurry": "^1.10.1"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "mkdirp": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
+ "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A=="
+ },
+ "reflect-metadata": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
+ "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw=="
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+ }
+ }
+ },
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
- "dev": true
+ "devOptional": true
},
"uid": {
"version": "2.0.1",
@@ -15361,7 +17177,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
- "dev": true
+ "devOptional": true
},
"v8-to-istanbul": {
"version": "9.1.0",
@@ -15480,7 +17296,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"requires": {
"isexe": "^2.0.0"
}
@@ -15506,6 +17321,16 @@
"strip-ansi": "^6.0.0"
}
},
+ "wrap-ansi-cjs": {
+ "version": "npm:wrap-ansi@7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -15574,7 +17399,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "dev": true
+ "devOptional": true
},
"yocto-queue": {
"version": "0.1.0",
diff --git a/data_migration_tools/server/package.json b/data_migration_tools/server/package.json
index 6135ae6..a2299cb 100644
--- a/data_migration_tools/server/package.json
+++ b/data_migration_tools/server/package.json
@@ -19,10 +19,13 @@
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
- "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
+ "apigen": "ts-node src/api/generate.ts && prettier --write \"src/api/odms/*.json\""
},
"dependencies": {
+ "@azure/identity": "^4.0.1",
"@azure/storage-blob": "^12.14.0",
+ "@microsoft/microsoft-graph-client": "^3.0.7",
"@nestjs/common": "^9.3.9",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.3.9",
@@ -30,6 +33,7 @@
"@nestjs/serve-static": "^3.0.1",
"@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.3.9",
+ "@nestjs/typeorm": "^10.0.2",
"@openapitools/openapi-generator-cli": "^2.5.2",
"@vercel/ncc": "^0.36.1",
"axios": "^1.3.4",
@@ -37,9 +41,11 @@
"class-validator": "^0.14.0",
"cookie-parser": "^1.4.6",
"multer": "^1.4.5-lts.1",
+ "mysql2": "^2.3.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
- "swagger-cli": "^4.0.4"
+ "swagger-cli": "^4.0.4",
+ "typeorm": "^0.3.20"
},
"devDependencies": {
"@types/express": "^4.17.17",
diff --git a/data_migration_tools/server/src/api/generate.ts b/data_migration_tools/server/src/api/generate.ts
new file mode 100644
index 0000000..5482a98
--- /dev/null
+++ b/data_migration_tools/server/src/api/generate.ts
@@ -0,0 +1,25 @@
+import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
+import { AppModule } from "../app.module";
+import { promises as fs } from "fs";
+import { NestFactory } from "@nestjs/core";
+async function bootstrap(): Promise {
+ const app = await NestFactory.create(AppModule, {
+ preview: true,
+ });
+
+ const options = new DocumentBuilder()
+ .setTitle("ODMSOpenAPI")
+ .setVersion("1.0.0")
+ .addBearerAuth({
+ type: "http",
+ scheme: "bearer",
+ bearerFormat: "JWT",
+ })
+ .build();
+ const document = SwaggerModule.createDocument(app, options);
+ await fs.writeFile(
+ "src/api/odms/openapi.json",
+ JSON.stringify(document, null, 0)
+ );
+}
+bootstrap();
diff --git a/data_migration_tools/server/src/api/odms/openapi.json b/data_migration_tools/server/src/api/odms/openapi.json
new file mode 100644
index 0000000..b783c09
--- /dev/null
+++ b/data_migration_tools/server/src/api/odms/openapi.json
@@ -0,0 +1,56 @@
+{
+ "openapi": "3.0.0",
+ "paths": {
+ "/delete": {
+ "post": {
+ "operationId": "deleteData",
+ "summary": "",
+ "description": "すべてのデータを削除します",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "成功時のレスポンス",
+ "content": {
+ "application/json": {
+ "schema": { "$ref": "#/components/schemas/DeleteResponse" }
+ }
+ }
+ },
+ "500": {
+ "description": "想定外のサーバーエラー",
+ "content": {
+ "application/json": {
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" }
+ }
+ }
+ }
+ },
+ "tags": ["delete"]
+ }
+ }
+ },
+ "info": {
+ "title": "ODMSOpenAPI",
+ "description": "",
+ "version": "1.0.0",
+ "contact": {}
+ },
+ "tags": [],
+ "servers": [],
+ "components": {
+ "securitySchemes": {
+ "bearer": { "scheme": "bearer", "bearerFormat": "JWT", "type": "http" }
+ },
+ "schemas": {
+ "DeleteResponse": { "type": "object", "properties": {} },
+ "ErrorResponse": {
+ "type": "object",
+ "properties": {
+ "message": { "type": "string" },
+ "code": { "type": "string" }
+ },
+ "required": ["message", "code"]
+ }
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/app.module.ts b/data_migration_tools/server/src/app.module.ts
index 9e2dfe3..8089b78 100644
--- a/data_migration_tools/server/src/app.module.ts
+++ b/data_migration_tools/server/src/app.module.ts
@@ -1,8 +1,29 @@
import { MiddlewareConsumer, Module } from "@nestjs/common";
import { ServeStaticModule } from "@nestjs/serve-static";
-import { ConfigModule } from "@nestjs/config";
+import { ConfigModule, ConfigService } from "@nestjs/config";
+import { TypeOrmModule } from "@nestjs/typeorm";
import { join } from "path";
import { LoggerMiddleware } from "./common/loggerMiddleware";
+import { AdB2cModule } from "./gateways/adb2c/adb2c.module";
+import { BlobstorageModule } from "./gateways/blobstorage/blobstorage.module";
+import { RegisterController } from "./features/register/register.controller";
+import { RegisterService } from "./features/register/register.service";
+import { RegisterModule } from "./features/register/register.module";
+import { AccountsRepositoryModule } from "./repositories/accounts/accounts.repository.module";
+import { UsersRepositoryModule } from "./repositories/users/users.repository.module";
+import { SortCriteriaRepositoryModule } from "./repositories/sort_criteria/sort_criteria.repository.module";
+import { LicensesRepositoryModule } from "./repositories/licenses/licenses.repository.module";
+import { WorktypesRepositoryModule } from "./repositories/worktypes/worktypes.repository.module";
+import { AccountsController } from "./features/accounts/accounts.controller";
+import { AccountsService } from "./features/accounts/accounts.service";
+import { AccountsModule } from "./features/accounts/accounts.module";
+import { UsersController } from "./features/users/users.controller";
+import { UsersService } from "./features/users/users.service";
+import { UsersModule } from "./features/users/users.module";
+import { DeleteModule } from "./features/delete/delete.module";
+import { DeleteRepositoryModule } from "./repositories/delete/delete.repository.module";
+import { DeleteController } from "./features/delete/delete.controller";
+import { DeleteService } from "./features/delete/delete.service";
@Module({
imports: [
@@ -13,9 +34,35 @@ import { LoggerMiddleware } from "./common/loggerMiddleware";
envFilePath: [".env.local", ".env"],
isGlobal: true,
}),
+ AdB2cModule,
+ AccountsModule,
+ UsersModule,
+ RegisterModule,
+ AccountsRepositoryModule,
+ UsersRepositoryModule,
+ SortCriteriaRepositoryModule,
+ LicensesRepositoryModule,
+ WorktypesRepositoryModule,
+ BlobstorageModule,
+ DeleteModule,
+ DeleteRepositoryModule,
+ TypeOrmModule.forRootAsync({
+ imports: [ConfigModule],
+ useFactory: async (configService: ConfigService) => ({
+ type: "mysql",
+ host: configService.get("DB_HOST"),
+ port: configService.get("DB_PORT"),
+ username: configService.get("DB_USERNAME"),
+ password: configService.get("DB_PASSWORD"),
+ database: configService.get("DB_NAME"),
+ autoLoadEntities: true, // forFeature()で登録されたEntityを自動的にロード
+ synchronize: false, // trueにすると自動的にmigrationが行われるため注意
+ }),
+ inject: [ConfigService],
+ }),
],
- controllers: [],
- providers: [],
+ controllers: [RegisterController, AccountsController, UsersController, DeleteController],
+ providers: [RegisterService, AccountsService, UsersService, DeleteService],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
diff --git a/data_migration_tools/server/src/common/entity/bigintTransformer.spec.ts b/data_migration_tools/server/src/common/entity/bigintTransformer.spec.ts
new file mode 100644
index 0000000..48c20d0
--- /dev/null
+++ b/data_migration_tools/server/src/common/entity/bigintTransformer.spec.ts
@@ -0,0 +1,61 @@
+import { bigintTransformer } from '.';
+
+describe('bigintTransformer', () => {
+ describe('to', () => {
+ it('number型を整数を表す文字列に変換できる', () => {
+ expect(bigintTransformer.to(0)).toBe('0');
+ expect(bigintTransformer.to(1)).toBe('1');
+ expect(bigintTransformer.to(1234567890)).toBe('1234567890');
+ expect(bigintTransformer.to(9007199254740991)).toBe('9007199254740991');
+ expect(bigintTransformer.to(-1)).toBe('-1');
+ });
+ it('少数点以下がある場合はエラーとなる', () => {
+ expect(() => bigintTransformer.to(1.1)).toThrowError(
+ '1.1 is not integer.',
+ );
+ });
+ it('Number.MAX_SAFE_INTEGERを超える値を変換しようとするとエラーになる', () => {
+ expect(() => bigintTransformer.to(9007199254740992)).toThrowError(
+ 'value is greater than 9007199254740991.',
+ );
+ expect(() => bigintTransformer.to(9223372036854775807)).toThrowError(
+ 'value is greater than 9007199254740991.',
+ );
+ });
+ });
+ describe('from', () => {
+ it('bigint型の文字列をnumber型に変換できる', () => {
+ expect(bigintTransformer.from('0')).toBe(0);
+ expect(bigintTransformer.from('1')).toBe(1);
+ expect(bigintTransformer.from('1234567890')).toBe(1234567890);
+ expect(bigintTransformer.from('-1')).toBe(-1);
+ });
+ it('Number.MAX_SAFE_INTEGERを超える値を変換しようとするとエラーになる', () => {
+ expect(() => bigintTransformer.from('9007199254740992')).toThrowError(
+ '9007199254740992 is greater than 9007199254740991.',
+ );
+ expect(() => bigintTransformer.from('9223372036854775807')).toThrowError(
+ '9223372036854775807 is greater than 9007199254740991.',
+ );
+ });
+ it('number型の場合はそのまま返す', () => {
+ expect(bigintTransformer.from(0)).toBe(0);
+ expect(bigintTransformer.from(1)).toBe(1);
+ expect(bigintTransformer.from(1234567890)).toBe(1234567890);
+ expect(bigintTransformer.from(-1)).toBe(-1);
+ });
+ it('nullの場合はそのまま返す', () => {
+ expect(bigintTransformer.from(null)).toBe(null);
+ });
+ it('number型に変換できない場合はエラーとなる', () => {
+ expect(() => bigintTransformer.from('a')).toThrowError('a is not int.');
+ expect(() => bigintTransformer.from('')).toThrowError(' is not int.');
+ expect(() => bigintTransformer.from(undefined)).toThrowError(
+ 'undefined is not string.',
+ );
+ expect(() => bigintTransformer.from({})).toThrowError(
+ '[object Object] is not string.',
+ );
+ });
+ });
+});
diff --git a/data_migration_tools/server/src/common/entity/index.ts b/data_migration_tools/server/src/common/entity/index.ts
new file mode 100644
index 0000000..26f5d5b
--- /dev/null
+++ b/data_migration_tools/server/src/common/entity/index.ts
@@ -0,0 +1,57 @@
+import { ValueTransformer } from 'typeorm';
+
+// DBのbigint型をnumber型に変換するためのtransformer
+// DBのBigInt型をそのまま扱うと、JSのNumber型の最大値を超えると誤差が発生するため、本来はNumber型に変換すべきではないが、
+// 影響範囲を最小限に抑えるため、Number型に変換する。使用するのはAutoIncrementされるIDのみの想定のため、
+// Number.MAX_SAFE_INTEGERより大きい値は現実的には発生しない想定で変換する。
+export const bigintTransformer: ValueTransformer = {
+ from: (value: any): number | null => {
+ // valueがnullであればそのまま返す
+ if (value === null) {
+ return value;
+ }
+ // valueがnumber型かどうかを判定
+ // 利用DBによってはbigint型であってもnumber型で返ってくる場合があるため、number型の場合はそのまま返す(sqliteの場合)
+ if (typeof value === 'number') {
+ return value;
+ }
+ // valueが文字列かどうかを判定
+ if (typeof value !== 'string') {
+ throw new Error(`${value} is not string.`);
+ }
+ // 数値に変換可能な文字列かどうかを判定
+ if (Number.isNaN(parseInt(value))) {
+ throw new Error(`${value} is not int.`);
+ }
+
+ // 文字列ならbigintに変換
+ // valueが整数でない場合は値が丸められてしまうが、TypeORMのEntityの定義上、整数を表す文字列以外はありえないため、少数点は考慮しない
+ const bigIntValue = BigInt(value);
+ // bigIntValueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
+ if (bigIntValue > Number.MAX_SAFE_INTEGER) {
+ throw new Error(`${value} is greater than ${Number.MAX_SAFE_INTEGER}.`);
+ }
+ // number型で表現できる整数であればnumber型に変換して返す
+ return Number(bigIntValue);
+ },
+ to: (value: any): string | null | undefined => {
+ // valueがnullまたはundefinedであればそのまま返す
+ if (value === null || value === undefined) {
+ return value;
+ }
+ // valueがnumber型かどうかを判定
+ if (typeof value !== 'number') {
+ throw new Error(`${value} is not number.`);
+ }
+
+ // valueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
+ if (value > Number.MAX_SAFE_INTEGER) {
+ throw new Error(`value is greater than ${Number.MAX_SAFE_INTEGER}.`);
+ }
+ // valueが整数かどうかを判定
+ if (!Number.isInteger(value)) {
+ throw new Error(`${value} is not integer.`);
+ }
+ return value.toString();
+ },
+};
diff --git a/data_migration_tools/server/src/common/error/code.ts b/data_migration_tools/server/src/common/error/code.ts
new file mode 100644
index 0000000..3c488da
--- /dev/null
+++ b/data_migration_tools/server/src/common/error/code.ts
@@ -0,0 +1,70 @@
+/*
+エラーコード作成方針
+E+6桁(数字)で構成する。
+- 1~2桁目の値は種類(業務エラー、システムエラー...)
+- 3~4桁目の値は原因箇所(トークン、DB、...)
+- 5~6桁目の値は任意の重複しない値
+ex)
+E00XXXX : システムエラー(通信エラーやDB接続失敗など)
+E01XXXX : 業務エラー
+EXX00XX : 内部エラー(内部プログラムのエラー)
+EXX01XX : トークンエラー(トークン認証関連)
+EXX02XX : DBエラー(DB関連)
+EXX03XX : ADB2Cエラー(DB関連)
+*/
+export const ErrorCodes = [
+ 'E009999', // 汎用エラー
+ 'E000101', // トークン形式不正エラー
+ 'E000102', // トークン有効期限切れエラー
+ 'E000103', // トークン非アクティブエラー
+ 'E000104', // トークン署名エラー
+ 'E000105', // トークン発行元エラー
+ 'E000106', // トークンアルゴリズムエラー
+ 'E000107', // トークン不足エラー
+ 'E000108', // トークン権限エラー
+ 'E000301', // ADB2Cへのリクエスト上限超過エラー
+ 'E000401', // IPアドレス未設定エラー
+ 'E000501', // リクエストID未設定エラー
+ 'E010001', // パラメータ形式不正エラー
+ 'E010201', // 未認証ユーザエラー
+ 'E010202', // 認証済ユーザエラー
+ 'E010203', // 管理ユーザ権限エラー
+ 'E010204', // ユーザ不在エラー
+ 'E010205', // DBのRoleが想定外の値エラー
+ 'E010206', // DBのTierが想定外の値エラー
+ 'E010207', // ユーザーのRole変更不可エラー
+ 'E010208', // ユーザーの暗号化パスワード不足エラー
+ 'E010209', // ユーザーの同意済み利用規約バージョンが最新でないエラー
+ 'E010301', // メールアドレス登録済みエラー
+ 'E010302', // authorId重複エラー
+ 'E010401', // PONumber重複エラー
+ 'E010501', // アカウント不在エラー
+ 'E010502', // アカウント情報変更不可エラー
+ 'E010503', // 代行操作不許可エラー
+ 'E010504', // アカウントロックエラー
+ 'E010601', // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
+ 'E010602', // タスク変更権限不足エラー
+ 'E010603', // タスク不在エラー
+ 'E010701', // Blobファイル不在エラー
+ 'E010801', // ライセンス不在エラー
+ 'E010802', // ライセンス取り込み済みエラー
+ 'E010803', // ライセンス発行済みエラー
+ 'E010804', // ライセンス不足エラー
+ 'E010805', // ライセンス有効期限切れエラー
+ 'E010806', // ライセンス割り当て不可エラー
+ 'E010807', // ライセンス割り当て解除済みエラー
+ 'E010808', // ライセンス注文キャンセル不可エラー
+ 'E010809', // ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
+ 'E010810', // ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
+ 'E010811', // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
+ 'E010812', // ライセンス未割当エラー
+ 'E010908', // タイピストグループ不在エラー
+ 'E010909', // タイピストグループ名重複エラー
+ 'E011001', // ワークタイプ重複エラー
+ 'E011002', // ワークタイプ登録上限超過エラー
+ 'E011003', // ワークタイプ不在エラー
+ 'E011004', // ワークタイプ使用中エラー
+ 'E012001', // テンプレートファイル不在エラー
+ 'E013001', // ワークフローのAuthorIDとWorktypeIDのペア重複エラー
+ 'E013002', // ワークフロー不在エラー
+] as const;
diff --git a/data_migration_tools/server/src/common/error/makeErrorResponse.ts b/data_migration_tools/server/src/common/error/makeErrorResponse.ts
new file mode 100644
index 0000000..0a677b4
--- /dev/null
+++ b/data_migration_tools/server/src/common/error/makeErrorResponse.ts
@@ -0,0 +1,10 @@
+import { errors } from './message';
+import { ErrorCodeType, ErrorResponse } from './types/types';
+
+export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => {
+ const msg = errors[errorcode];
+ return {
+ code: errorcode,
+ message: msg,
+ };
+};
diff --git a/data_migration_tools/server/src/common/error/message.ts b/data_migration_tools/server/src/common/error/message.ts
new file mode 100644
index 0000000..9383694
--- /dev/null
+++ b/data_migration_tools/server/src/common/error/message.ts
@@ -0,0 +1,59 @@
+import { Errors } from './types/types';
+
+// エラーコードとメッセージ対応表
+export const errors: Errors = {
+ E009999: 'Internal Server Error.',
+ E000101: 'Token invalid format Error.',
+ E000102: 'Token expired Error.',
+ E000103: 'Token not before Error',
+ E000104: 'Token invalid signature Error.',
+ E000105: 'Token invalid issuer Error.',
+ E000106: 'Token invalid algorithm Error.',
+ E000107: 'Token is not exist Error.',
+ E000108: 'Token authority failed Error.',
+ E000301: 'ADB2C request limit exceeded Error',
+ E000401: 'IP address not found Error.',
+ E000501: 'Request ID not found Error.',
+ E010001: 'Param invalid format Error.',
+ E010201: 'Email not verified user Error.',
+ E010202: 'Email already verified user Error.',
+ E010203: 'Administrator Permissions Error.',
+ E010204: 'User not Found Error.',
+ E010205: 'Role from DB is unexpected value Error.',
+ E010206: 'Tier from DB is unexpected value Error.',
+ E010207: 'User role change not allowed Error.',
+ E010208: 'User encryption password not found Error.',
+ E010209: 'Accepted term not latest Error.',
+ E010301: 'This email user already created Error',
+ E010302: 'This AuthorId already used Error',
+ E010401: 'This PoNumber already used Error',
+ E010501: 'Account not Found Error.',
+ E010502: 'Account information cannot be changed Error.',
+ E010503: 'Delegation not allowed Error.',
+ E010504: 'Account is locked Error.',
+ E010601: 'Task is not Editable Error',
+ E010602: 'No task edit permissions Error',
+ E010603: 'Task not found Error.',
+ E010701: 'File not found in Blob Storage Error.',
+ E010801: 'License not exist Error',
+ E010802: 'License already activated Error',
+ E010803: 'License already issued Error',
+ E010804: 'License shortage Error',
+ E010805: 'License is expired Error',
+ E010806: 'License is unavailable Error',
+ E010807: 'License is already deallocated Error',
+ E010808: 'Order cancel failed Error',
+ E010809: 'Already license order status changed Error',
+ E010810: 'Cancellation period expired error',
+ E010811: 'Already license allocated Error',
+ E010812: 'License not allocated Error',
+ E010908: 'Typist Group not exist Error',
+ E010909: 'Typist Group name already exist Error',
+ E011001: 'This WorkTypeID already used Error',
+ E011002: 'WorkTypeID create limit exceeded Error',
+ E011003: 'WorkTypeID not found Error',
+ E011004: 'WorkTypeID is in use Error',
+ E012001: 'Template file not found Error',
+ E013001: 'AuthorId and WorktypeId pair already exists Error',
+ E013002: 'Workflow not found Error',
+};
diff --git a/data_migration_tools/server/src/common/error/types/types.ts b/data_migration_tools/server/src/common/error/types/types.ts
new file mode 100644
index 0000000..8746924
--- /dev/null
+++ b/data_migration_tools/server/src/common/error/types/types.ts
@@ -0,0 +1,15 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { ErrorCodes } from '../code';
+
+export class ErrorResponse {
+ @ApiProperty()
+ message: string;
+ @ApiProperty()
+ code: string;
+}
+
+export type ErrorCodeType = (typeof ErrorCodes)[number];
+
+export type Errors = {
+ [P in ErrorCodeType]: string;
+};
diff --git a/data_migration_tools/server/src/common/errors/code.ts b/data_migration_tools/server/src/common/errors/code.ts
new file mode 100644
index 0000000..f16dd2a
--- /dev/null
+++ b/data_migration_tools/server/src/common/errors/code.ts
@@ -0,0 +1,17 @@
+/*
+エラーコード作成方針
+E+6桁(数字)で構成する。
+- 1~2桁目の値は種類(業務エラー、システムエラー...)
+- 3~4桁目の値は原因箇所(トークン、DB、...)
+- 5~6桁目の値は任意の重複しない値
+ex)
+E00XXXX : システムエラー(通信エラーやDB接続失敗など)
+E01XXXX : 業務エラー
+EXX00XX : 内部エラー(内部プログラムのエラー)
+EXX01XX : トークンエラー(トークン認証関連)
+EXX02XX : DBエラー(DB関連)
+EXX03XX : ADB2Cエラー(DB関連)
+*/
+export const ErrorCodes = [
+ "E009999", // 汎用エラー
+] as const;
diff --git a/data_migration_tools/server/src/common/errors/makeErrorResponse.ts b/data_migration_tools/server/src/common/errors/makeErrorResponse.ts
new file mode 100644
index 0000000..cd83472
--- /dev/null
+++ b/data_migration_tools/server/src/common/errors/makeErrorResponse.ts
@@ -0,0 +1,10 @@
+import { errors } from "./message";
+import { ErrorCodeType, ErrorResponse } from "./types/types";
+
+export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => {
+ const msg = errors[errorcode];
+ return {
+ code: errorcode,
+ message: msg,
+ };
+};
diff --git a/data_migration_tools/server/src/common/errors/message.ts b/data_migration_tools/server/src/common/errors/message.ts
new file mode 100644
index 0000000..f3d2fc1
--- /dev/null
+++ b/data_migration_tools/server/src/common/errors/message.ts
@@ -0,0 +1,6 @@
+import { Errors } from "./types/types";
+
+// エラーコードとメッセージ対応表
+export const errors: Errors = {
+ E009999: "Internal Server Error.",
+};
diff --git a/data_migration_tools/server/src/common/errors/types/types.ts b/data_migration_tools/server/src/common/errors/types/types.ts
new file mode 100644
index 0000000..f68eda7
--- /dev/null
+++ b/data_migration_tools/server/src/common/errors/types/types.ts
@@ -0,0 +1,15 @@
+import { ApiProperty } from "@nestjs/swagger";
+import { ErrorCodes } from "../code";
+
+export class ErrorResponse {
+ @ApiProperty()
+ message: string;
+ @ApiProperty()
+ code: string;
+}
+
+export type ErrorCodeType = (typeof ErrorCodes)[number];
+
+export type Errors = {
+ [P in ErrorCodeType]: string;
+};
diff --git a/data_migration_tools/server/src/common/log/context.ts b/data_migration_tools/server/src/common/log/context.ts
new file mode 100644
index 0000000..1b887c6
--- /dev/null
+++ b/data_migration_tools/server/src/common/log/context.ts
@@ -0,0 +1,32 @@
+import { Request } from 'express';
+import { Context } from './types';
+
+export const makeContext = (
+ externalId: string,
+ requestId: string,
+ delegationId?: string,
+): Context => {
+ return new Context(externalId, requestId, delegationId);
+};
+
+// リクエストヘッダーからrequestIdを取得する
+export const retrieveRequestId = (req: Request): string | undefined => {
+ return req.header('x-request-id');
+};
+
+/**
+ * リクエストのIPアドレスを取得します
+ * @param {Request}
+ * @return {string | undefined}
+ */
+export const retrieveIp = (req: Request): string | undefined => {
+ // ローカル環境では直近の送信元IPを取得する
+ if (process.env.STAGE === 'local') {
+ return req.ip;
+ }
+ const ip = req.header('x-forwarded-for');
+ if (typeof ip === 'string') {
+ return ip;
+ }
+ return undefined;
+};
diff --git a/data_migration_tools/server/src/common/log/index.ts b/data_migration_tools/server/src/common/log/index.ts
new file mode 100644
index 0000000..386f9cd
--- /dev/null
+++ b/data_migration_tools/server/src/common/log/index.ts
@@ -0,0 +1,4 @@
+import { Context } from "./types";
+import { makeContext, retrieveRequestId, retrieveIp } from "./context";
+
+export { Context, makeContext, retrieveRequestId, retrieveIp };
diff --git a/data_migration_tools/server/src/common/log/types.ts b/data_migration_tools/server/src/common/log/types.ts
new file mode 100644
index 0000000..6f56bc1
--- /dev/null
+++ b/data_migration_tools/server/src/common/log/types.ts
@@ -0,0 +1,34 @@
+export class Context {
+ /**
+ * APIの操作ユーザーを追跡するためのID
+ */
+ trackingId: string;
+ /**
+ * APIの操作ユーザーのIPアドレス
+ */
+ ip: string;
+ /**
+ * ユーザーの操作を一意に識別するためのID
+ */
+ requestId: string;
+ /**
+ * APIの代行操作ユーザーを追跡するためのID
+ */
+ delegationId?: string | undefined;
+
+ constructor(externalId: string, requestId: string, delegationId?: string) {
+ this.trackingId = externalId;
+ this.delegationId = delegationId;
+ this.requestId = requestId;
+ }
+ /**
+ * ログにユーザーを特定する情報を出力する
+ */
+ getTrackingId(): string {
+ if (this.delegationId) {
+ return `${this.requestId}_${this.trackingId} by ${this.delegationId}`;
+ } else {
+ return `${this.requestId}_${this.trackingId}`;
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/common/password/index.ts b/data_migration_tools/server/src/common/password/index.ts
new file mode 100644
index 0000000..1ba44a2
--- /dev/null
+++ b/data_migration_tools/server/src/common/password/index.ts
@@ -0,0 +1,3 @@
+import { makePassword } from "./password";
+
+export { makePassword };
diff --git a/data_migration_tools/server/src/common/password/password.ts b/data_migration_tools/server/src/common/password/password.ts
new file mode 100644
index 0000000..6fbe071
--- /dev/null
+++ b/data_migration_tools/server/src/common/password/password.ts
@@ -0,0 +1,35 @@
+export const makePassword = (): string => {
+ // パスワードの文字数を決定
+ const passLength = 8;
+
+ // パスワードに使用可能な文字を決定(今回はアルファベットの大文字と小文字 + 数字 + symbolsの記号)
+ const lowerCase = "abcdefghijklmnopqrstuvwxyz";
+ const upperCase = lowerCase.toLocaleUpperCase();
+ const numbers = "0123456789";
+ const symbols = "@#$%^&*\\-_+=[]{}|:',.?/`~\"();!";
+ const chars = lowerCase + upperCase + numbers + symbols;
+
+ // 英字の大文字、英字の小文字、アラビア数字、記号(@#$%^&*\-_+=[]{}|\:',.?/`~"();!)から2種類以上組み合わせ
+ const charaTypePattern =
+ /^((?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[\d])|(?=.*[a-z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[A-Z])(?=.*[\d])|(?=.*[A-Z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[\d])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]))[a-zA-Z\d@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]/;
+
+ // autoGeneratedPasswordが以上の条件を満たせばvalidがtrueになる
+ let valid = false;
+ let autoGeneratedPassword: string = "";
+
+ while (!valid) {
+ // パスワードをランダムに決定
+ while (autoGeneratedPassword.length < passLength) {
+ // 上で決定したcharsの中からランダムに1文字ずつ追加
+ const index = Math.floor(Math.random() * chars.length);
+ autoGeneratedPassword += chars[index];
+ }
+
+ // パスワードが上で決定した条件をすべて満たしているかチェック
+ // 条件を満たすまでループ
+ valid =
+ autoGeneratedPassword.length == passLength &&
+ charaTypePattern.test(autoGeneratedPassword);
+ }
+ return autoGeneratedPassword;
+};
diff --git a/data_migration_tools/server/src/common/repository/index.ts b/data_migration_tools/server/src/common/repository/index.ts
new file mode 100644
index 0000000..b3e21fa
--- /dev/null
+++ b/data_migration_tools/server/src/common/repository/index.ts
@@ -0,0 +1,143 @@
+import {
+ ObjectLiteral,
+ Repository,
+ EntityTarget,
+ UpdateResult,
+ DeleteResult,
+ UpdateQueryBuilder,
+ Brackets,
+ FindOptionsWhere,
+} from 'typeorm';
+import { Context } from '../log';
+
+/**
+ * VS Code上で型解析エラーが発生するため、typeorm内の型定義と同一の型定義をここに記述する
+ */
+type QueryDeepPartialEntity = _QueryDeepPartialEntity<
+ ObjectLiteral extends T ? unknown : T
+>;
+type _QueryDeepPartialEntity = {
+ [P in keyof T]?:
+ | (T[P] extends Array
+ ? Array<_QueryDeepPartialEntity>
+ : T[P] extends ReadonlyArray
+ ? ReadonlyArray<_QueryDeepPartialEntity>
+ : _QueryDeepPartialEntity)
+ | (() => string);
+};
+
+interface InsertEntityOptions {
+ id: number;
+}
+
+const insertEntity = async (
+ entity: EntityTarget,
+ repository: Repository,
+ value: QueryDeepPartialEntity,
+ isCommentOut: boolean,
+ context: Context,
+): Promise => {
+ let query = repository.createQueryBuilder().insert().into(entity);
+ if (isCommentOut) {
+ query = query.comment(
+ `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ );
+ }
+ const result = await query.values(value).execute();
+ // result.identifiers[0].idがnumber型でない場合はエラー
+ if (typeof result.identifiers[0].id !== 'number') {
+ throw new Error('Failed to insert entity');
+ }
+ const where: FindOptionsWhere = { id: result.identifiers[0].id } as T;
+
+ // 結果をもとにセレクトする
+ const inserted = await repository.findOne({
+ where,
+ comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ });
+ if (!inserted) {
+ throw new Error('Failed to insert entity');
+ }
+ return inserted;
+};
+
+const insertEntities = async (
+ entity: EntityTarget,
+ repository: Repository,
+ values: QueryDeepPartialEntity[],
+ isCommentOut: boolean,
+ context: Context,
+): Promise => {
+ let query = repository.createQueryBuilder().insert().into(entity);
+ if (isCommentOut) {
+ query = query.comment(
+ `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ );
+ }
+ const result = await query.values(values).execute();
+
+ // 挿入するレコードが0で、結果も0であれば、からの配列を返す
+ if (values.length === 0 && result.identifiers.length === 0) {
+ return [];
+ }
+
+ // 挿入するレコード数と挿入されたレコード数が一致しない場合はエラー
+ if (result.identifiers.length !== values.length) {
+ throw new Error('Failed to insert entities');
+ }
+ const where: FindOptionsWhere[] = result.identifiers.map((i) => {
+ // idがnumber型でない場合はエラー
+ if (typeof i.id !== 'number') {
+ throw new Error('Failed to insert entities');
+ }
+ return { id: i.id } as T;
+ });
+
+ // 結果をもとにセレクトする
+ const inserted = await repository.find({
+ where,
+ comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ });
+ if (!inserted) {
+ throw new Error('Failed to insert entity');
+ }
+ return inserted;
+};
+
+const updateEntity = async (
+ repository: Repository,
+ criteria:
+ | string
+ | ((qb: UpdateQueryBuilder) => string)
+ | Brackets
+ | ObjectLiteral
+ | ObjectLiteral[],
+ values: QueryDeepPartialEntity,
+ isCommentOut: boolean,
+ context: Context,
+): Promise => {
+ let query = repository.createQueryBuilder().update();
+ if (isCommentOut) {
+ query = query.comment(
+ `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ );
+ }
+ return await query.set(values).where(criteria).execute();
+};
+
+const deleteEntity = async (
+ repository: Repository,
+ criteria: string | Brackets | ObjectLiteral | ObjectLiteral[],
+ isCommentOut: boolean,
+ context: Context,
+): Promise => {
+ let query = repository.createQueryBuilder().delete();
+ if (isCommentOut) {
+ query = query.comment(
+ `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ );
+ }
+ return await query.where(criteria).execute();
+};
+
+export { insertEntity, insertEntities, updateEntity, deleteEntity };
diff --git a/data_migration_tools/server/src/common/types/role/index.ts b/data_migration_tools/server/src/common/types/role/index.ts
new file mode 100644
index 0000000..d29201f
--- /dev/null
+++ b/data_migration_tools/server/src/common/types/role/index.ts
@@ -0,0 +1,10 @@
+import { ADMIN_ROLES, USER_ROLES } from '../../../constants';
+
+/**
+ * Token.roleに配置されうる文字列リテラル型
+ */
+export type Roles =
+ | (typeof ADMIN_ROLES)[keyof typeof ADMIN_ROLES]
+ | (typeof USER_ROLES)[keyof typeof USER_ROLES];
+
+export type UserRoles = (typeof USER_ROLES)[keyof typeof USER_ROLES];
diff --git a/data_migration_tools/server/src/common/types/sort/index.ts b/data_migration_tools/server/src/common/types/sort/index.ts
new file mode 100644
index 0000000..b505e50
--- /dev/null
+++ b/data_migration_tools/server/src/common/types/sort/index.ts
@@ -0,0 +1,27 @@
+import {
+ TASK_LIST_SORTABLE_ATTRIBUTES,
+ SORT_DIRECTIONS,
+} from '../../../constants';
+
+export type TaskListSortableAttribute =
+ (typeof TASK_LIST_SORTABLE_ATTRIBUTES)[number];
+
+export type SortDirection = (typeof SORT_DIRECTIONS)[number];
+
+export const isTaskListSortableAttribute = (
+ arg: string,
+): arg is TaskListSortableAttribute => {
+ const param = arg as TaskListSortableAttribute;
+ if (TASK_LIST_SORTABLE_ATTRIBUTES.includes(param)) {
+ return true;
+ }
+ return false;
+};
+
+export const isSortDirection = (arg: string): arg is SortDirection => {
+ const param = arg as SortDirection;
+ if (SORT_DIRECTIONS.includes(param)) {
+ return true;
+ }
+ return false;
+};
diff --git a/data_migration_tools/server/src/common/types/sort/util.ts b/data_migration_tools/server/src/common/types/sort/util.ts
new file mode 100644
index 0000000..2ccf33d
--- /dev/null
+++ b/data_migration_tools/server/src/common/types/sort/util.ts
@@ -0,0 +1,11 @@
+import { SortDirection, TaskListSortableAttribute } from '.';
+
+export const getDirection = (direction: SortDirection): SortDirection => {
+ return direction;
+};
+
+export const getTaskListSortableAttribute = (
+ TaskListSortableAttribute: TaskListSortableAttribute,
+): TaskListSortableAttribute => {
+ return TaskListSortableAttribute;
+};
diff --git a/data_migration_tools/server/src/common/types/types.ts b/data_migration_tools/server/src/common/types/types.ts
new file mode 100644
index 0000000..660b942
--- /dev/null
+++ b/data_migration_tools/server/src/common/types/types.ts
@@ -0,0 +1,231 @@
+export class csvInputFile {
+ type: string;
+ account_id: string;
+ parent_id: string;
+ email: string;
+ company_name: string;
+ first_name: string;
+ last_name: string;
+ country: string;
+ state: string;
+ start_date: Date;
+ expired_date: Date;
+ user_email: string;
+ author_id: string;
+ recording_mode: string;
+ wt1: string;
+ wt2: string;
+ wt3: string;
+ wt4: string;
+ wt5: string;
+ wt6: string;
+ wt7: string;
+ wt8: string;
+ wt9: string;
+ wt10: string;
+ wt11: string;
+ wt12: string;
+ wt13: string;
+ wt14: string;
+ wt15: string;
+ wt16: string;
+ wt17: string;
+ wt18: string;
+ wt19: string;
+ wt20: string;
+}
+export class AccountsOutputFileStep1 {
+ accountId: number;
+ type: string;
+ companyName: string;
+ country: string;
+ dealerAccountId?: number;
+ adminName: string;
+ adminMail: string;
+ userId: number;
+}
+
+export class AccountsOutputFile {
+ accountId: number;
+ type: number;
+ companyName: string;
+ country: string;
+ dealerAccountId?: number;
+ adminName: string;
+ adminMail: string;
+ userId: number;
+}
+export class AccountsInputFile {
+ accountId: number;
+ type: number;
+ companyName: string;
+ country: string;
+ dealerAccountId?: number;
+ adminName: string;
+ adminMail: string;
+ userId: number;
+}
+export class UsersOutputFile {
+ accountId: number;
+ userId: number;
+ name: string;
+ role: string;
+ authorId: string;
+ email: string;
+}
+
+export class UsersInputFile {
+ accountId: number;
+ userId: number;
+ name: string;
+ role: string;
+ authorId: string;
+ email: string;
+}
+
+export class LicensesOutputFile {
+ expiry_date: string;
+ account_id: number;
+ type: string;
+ status: string;
+ allocated_user_id?: number;
+}
+export class LicensesInputFile {
+ expiry_date: string;
+ account_id: number;
+ type: string;
+ status: string;
+ allocated_user_id?: number;
+}
+
+export class WorktypesOutputFile {
+ account_id: number;
+ custom_worktype_id: string;
+}
+export class WorktypesInputFile {
+ account_id: number;
+ custom_worktype_id: string;
+}
+
+export class CardLicensesInputFile {
+ license_id: number;
+ issue_id: number;
+ card_license_key: string;
+ activated_at?: string;
+ created_at?: string;
+ created_by?: string;
+ updated_at?: string;
+ updated_by?: string;
+}
+
+export function isAccountsInputFileArray(obj: any): obj is AccountsInputFile[] {
+ return Array.isArray(obj) && obj.every((item) => isAccountsInputFile(item));
+}
+export function isAccountsInputFile(obj: any): obj is AccountsInputFile {
+ return (
+ typeof obj === "object" &&
+ obj !== null &&
+ "accountId" in obj &&
+ typeof obj.accountId === "number" &&
+ "type" in obj &&
+ typeof obj.type === "number" &&
+ "companyName" in obj &&
+ typeof obj.companyName === "string" &&
+ "country" in obj &&
+ typeof obj.country === "string" &&
+ ("dealerAccountId" in obj
+ ? typeof obj.dealerAccountId === "number"
+ : true) &&
+ "adminName" in obj &&
+ typeof obj.adminName === "string" &&
+ "adminMail" in obj &&
+ typeof obj.adminMail === "string" &&
+ "userId" in obj &&
+ typeof obj.userId === "number"
+ );
+}
+
+export function isUsersInputFileArray(obj: any): obj is UsersInputFile[] {
+ return Array.isArray(obj) && obj.every((item) => isUsersInputFile(item));
+}
+export function isUsersInputFile(obj: any): obj is UsersInputFile {
+ return (
+ typeof obj === "object" &&
+ obj !== null &&
+ "accountId" in obj &&
+ "userId" in obj &&
+ "name" in obj &&
+ "role" in obj &&
+ "authorId" in obj &&
+ "email" in obj &&
+ typeof obj.accountId === "number" &&
+ typeof obj.userId === "number" &&
+ typeof obj.name === "string" &&
+ typeof obj.role === "string" &&
+ typeof obj.authorId === "string" &&
+ typeof obj.email === "string"
+ );
+}
+
+export function isLicensesInputFileArray(obj: any): obj is LicensesInputFile[] {
+ return Array.isArray(obj) && obj.every((item) => isLicensesInputFile(item));
+}
+export function isLicensesInputFile(obj: any): obj is LicensesInputFile {
+ return (
+ typeof obj === "object" &&
+ obj !== null &&
+ "expiry_date" in obj &&
+ "account_id" in obj &&
+ "type" in obj &&
+ "status" in obj &&
+ typeof obj.expiry_date === "string" &&
+ typeof obj.account_id === "number" &&
+ typeof obj.type === "string" &&
+ typeof obj.status === "string" &&
+ (obj.allocated_user_id === null ||
+ typeof obj.allocated_user_id === "number")
+ );
+}
+
+export function isWorktypesInputFileArray(
+ obj: any
+): obj is WorktypesInputFile[] {
+ return Array.isArray(obj) && obj.every((item) => isWorktypesInputFile(item));
+}
+export function isWorktypesInputFile(obj: any): obj is WorktypesInputFile {
+ return (
+ typeof obj === "object" &&
+ obj !== null &&
+ "account_id" in obj &&
+ "custom_worktype_id" in obj &&
+ typeof obj.account_id === "number" &&
+ typeof obj.custom_worktype_id === "string"
+ );
+}
+
+export function isCardLicensesInputFileArray(
+ obj: any
+): obj is CardLicensesInputFile[] {
+ return (
+ Array.isArray(obj) && obj.every((item) => isCardLicensesInputFile(item))
+ );
+}
+export function isCardLicensesInputFile(
+ obj: any
+): obj is CardLicensesInputFile {
+ return (
+ typeof obj === "object" &&
+ obj !== null &&
+ "license_id" in obj &&
+ "issue_id" in obj &&
+ "card_license_key" in obj &&
+ typeof obj.license_id === "number" &&
+ typeof obj.issue_id === "number" &&
+ typeof obj.card_license_key === "string" &&
+ (obj.activated_at === null || typeof obj.activated_at === "string") &&
+ (obj.created_at === null || typeof obj.created_at === "string") &&
+ (obj.created_by === null || typeof obj.created_by === "string") &&
+ (obj.updated_at === null || typeof obj.updated_at === "string") &&
+ (obj.updated_by === null || typeof obj.updated_by === "string")
+ );
+}
diff --git a/data_migration_tools/server/src/common/validators/admin.validator.ts b/data_migration_tools/server/src/common/validators/admin.validator.ts
new file mode 100644
index 0000000..d904154
--- /dev/null
+++ b/data_migration_tools/server/src/common/validators/admin.validator.ts
@@ -0,0 +1,39 @@
+import {
+ registerDecorator,
+ ValidationOptions,
+ ValidatorConstraint,
+ ValidatorConstraintInterface,
+} from "class-validator";
+
+@ValidatorConstraint()
+export class IsAdminPassword implements ValidatorConstraintInterface {
+ validate(value: string): boolean {
+ // 8文字~64文字でなければ早期に不合格
+ const minLength = 8;
+ const maxLength = 64;
+ if (value.length < minLength || value.length > maxLength) {
+ return false;
+ }
+
+ // 英字の大文字、英字の小文字、アラビア数字、記号(@#$%^&*\-_+=[]{}|\:',.?/`~"();!)から2種類以上組み合わせ
+ const charaTypePattern =
+ /^((?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[\d])|(?=.*[a-z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[A-Z])(?=.*[\d])|(?=.*[A-Z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[\d])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]))[a-zA-Z\d@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]/;
+ return new RegExp(charaTypePattern).test(value);
+ }
+ defaultMessage(): string {
+ return "Admin password rule not satisfied";
+ }
+}
+
+export const IsAdminPasswordvalid = (validationOptions?: ValidationOptions) => {
+ return (object: any, propertyName: string) => {
+ registerDecorator({
+ name: "IsAdminPasswordvalid",
+ target: object.constructor,
+ propertyName: propertyName,
+ constraints: [],
+ options: validationOptions,
+ validator: IsAdminPassword,
+ });
+ };
+};
diff --git a/data_migration_tools/server/src/constants/index.ts b/data_migration_tools/server/src/constants/index.ts
new file mode 100644
index 0000000..4330bb6
--- /dev/null
+++ b/data_migration_tools/server/src/constants/index.ts
@@ -0,0 +1,406 @@
+/**
+ * 階層
+ * @const {number}
+ */
+export const TIERS = {
+ //OMDS東京
+ TIER1: 1,
+ //OMDS現地法人
+ TIER2: 2,
+ //代理店
+ TIER3: 3,
+ //販売店
+ TIER4: 4,
+ //エンドユーザー
+ TIER5: 5,
+} as const;
+
+/**
+ * 音声ファイルをEast USに保存する国リスト
+ * @const {number}
+ */
+export const BLOB_STORAGE_REGION_US = ["CA", "KY", "US"];
+
+/**
+ * 音声ファイルをAustralia Eastに保存する国リスト
+ * @const {number}
+ */
+export const BLOB_STORAGE_REGION_AU = ["AU", "NZ"];
+
+/**
+ * 音声ファイルをNorth Europeに保存する国リスト
+ * @const {number}
+ */
+export const BLOB_STORAGE_REGION_EU = [
+ "AT",
+ "BE",
+ "BG",
+ "HR",
+ "CY",
+ "CZ",
+ "DK",
+ "EE",
+ "FI",
+ "FR",
+ "DE",
+ "GR",
+ "HU",
+ "IS",
+ "IE",
+ "IT",
+ "LV",
+ "LI",
+ "LT",
+ "LU",
+ "MT",
+ "NL",
+ "NO",
+ "PL",
+ "PT",
+ "RO",
+ "RS",
+ "SK",
+ "SI",
+ "ZA",
+ "ES",
+ "SE",
+ "CH",
+ "TR",
+ "GB",
+];
+
+/**
+ * 管理ロール
+ * @const {string[]}
+ */
+export const ADMIN_ROLES = {
+ ADMIN: "admin",
+ STANDARD: "standard",
+} as const;
+
+/**
+ * ロール
+ * @const {string[]}
+ */
+export const USER_ROLES = {
+ NONE: "none",
+ AUTHOR: "author",
+ TYPIST: "typist",
+} as const;
+
+/**
+ * ロールのソート順
+ * @const {string[]}
+ */
+export const USER_ROLE_ORDERS = [
+ USER_ROLES.AUTHOR,
+ USER_ROLES.TYPIST,
+ USER_ROLES.NONE,
+] as string[];
+
+/**
+ * ライセンス注文状態
+ * @const {string[]}
+ */
+export const LICENSE_ISSUE_STATUS = {
+ ISSUE_REQUESTING: "Issue Requesting",
+ ISSUED: "Issued",
+ CANCELED: "Order Canceled",
+};
+
+/**
+ * ライセンス種別
+ * @const {string[]}
+ */
+export const LICENSE_TYPE = {
+ TRIAL: "TRIAL",
+ NORMAL: "NORMAL",
+ CARD: "CARD",
+} as const;
+/**
+ * ライセンス状態
+ * @const {string[]}
+ */
+export const LICENSE_ALLOCATED_STATUS = {
+ UNALLOCATED: "Unallocated",
+ ALLOCATED: "Allocated",
+ REUSABLE: "Reusable",
+ DELETED: "Deleted",
+} as const;
+/**
+ * 切り替え元種別
+ * @const {string[]}
+ */
+export const SWITCH_FROM_TYPE = {
+ NONE: "NONE",
+ CARD: "CARD",
+ TRIAL: "TRIAL",
+} as const;
+
+/**
+ * ライセンスの期限切れが近いと見なす日数のしきい値
+ * @const {number}
+ */
+export const LICENSE_EXPIRATION_THRESHOLD_DAYS = 14;
+
+/**
+ * ライセンスの有効期間
+ * @const {number}
+ */
+export const LICENSE_EXPIRATION_DAYS = 365;
+
+/**
+ * タイムゾーンを加味したライセンスの有効期間(8時間)
+ * @const {number}
+ */
+export const LICENSE_EXPIRATION_TIME_WITH_TIMEZONE = 8;
+
+/**
+ * カードライセンスの桁数
+ * @const {number}
+ */
+export const CARD_LICENSE_LENGTH = 20;
+
+/**
+ * 音声ファイルに紐づくオプションアイテムの数
+ * @const {string}
+ */
+export const OPTION_ITEM_NUM = 10;
+
+/**
+ * 文字起こしタスクのステータス
+ * @const {string[]}
+ */
+export const TASK_STATUS = {
+ UPLOADED: "Uploaded",
+ PENDING: "Pending",
+ IN_PROGRESS: "InProgress",
+ FINISHED: "Finished",
+ BACKUP: "Backup",
+} as const;
+
+/**
+ * タスク一覧でソート可能な属性の一覧
+ */
+export const TASK_LIST_SORTABLE_ATTRIBUTES = [
+ "JOB_NUMBER",
+ "STATUS",
+ "ENCRYPTION",
+ "AUTHOR_ID",
+ "WORK_TYPE",
+ "FILE_NAME",
+ "FILE_LENGTH",
+ "FILE_SIZE",
+ "RECORDING_STARTED_DATE",
+ "RECORDING_FINISHED_DATE",
+ "UPLOAD_DATE",
+ "TRANSCRIPTION_STARTED_DATE",
+ "TRANSCRIPTION_FINISHED_DATE",
+] as const;
+
+/**
+ * タスク一覧のソート条件(昇順・降順)
+ */
+export const SORT_DIRECTIONS = ["ASC", "DESC"] as const;
+
+/**
+ * 通知タグの最大個数
+ * NotificationHubの仕様上タグ式のOR条件で使えるタグは20個まで
+ * https://learn.microsoft.com/ja-jp/azure/notification-hubs/notification-hubs-tags-segment-push-message#tag-expressions
+ */
+export const TAG_MAX_COUNT = 20;
+
+/**
+ * 通知のプラットフォーム種別文字列
+ */
+export const PNS = {
+ WNS: "wns",
+ APNS: "apns",
+};
+
+/**
+ * ユーザーのライセンスの有効期限の状態
+ */
+export const USER_LICENSE_EXPIRY_STATUS = {
+ NORMAL: "Normal",
+ NO_LICENSE: "NoLicense",
+ ALERT: "Alert",
+ RENEW: "Renew",
+};
+
+/**
+ *トライアルライセンスの有効期限(日数)
+ * @const {number}
+ */
+export const TRIAL_LICENSE_EXPIRATION_DAYS = 30;
+
+/**
+ * ライセンスの発行数
+ * @const {number}
+ */
+export const TRIAL_LICENSE_ISSUE_NUM = 100;
+
+/**
+ * worktypeの最大登録数
+ * @const {number}
+ */
+export const WORKTYPE_MAX_COUNT = 20;
+
+/**
+ * worktypeのDefault値の取りうる値
+ **/
+export const OPTION_ITEM_VALUE_TYPE = {
+ DEFAULT: "Default",
+ BLANK: "Blank",
+ LAST_INPUT: "LastInput",
+} as const;
+
+/**
+ * オプションアイテムのタイプ文字列と数値の対応
+ **/
+export const OPTION_ITEM_VALUE_TYPE_NUMBER: {
+ type: string;
+ value: number;
+}[] = [
+ {
+ type: OPTION_ITEM_VALUE_TYPE.BLANK,
+ value: 1,
+ },
+ {
+ type: OPTION_ITEM_VALUE_TYPE.DEFAULT,
+ value: 2,
+ },
+ {
+ type: OPTION_ITEM_VALUE_TYPE.LAST_INPUT,
+ value: 3,
+ },
+];
+
+/**
+ * ADB2Cユーザのidentity.signInType
+ * @const {string[]}
+ */
+export const ADB2C_SIGN_IN_TYPE = {
+ EMAILADDRESS: "emailAddress",
+} as const;
+
+/**
+ * MANUAL_RECOVERY_REQUIRED
+ * @const {string}
+ */
+export const MANUAL_RECOVERY_REQUIRED = "[MANUAL_RECOVERY_REQUIRED]";
+
+/**
+ * 利用規約種別
+ * @const {string[]}
+ */
+export const TERM_TYPE = {
+ EULA: "EULA",
+ DPA: "DPA",
+ PRIVACY_NOTICE: "PrivacyNotice",
+} as const;
+
+/**
+ * 音声ファイルのフォーマット
+ * @const {string}
+ */
+export const USER_AUDIO_FORMAT = "DS2(QP)";
+
+/**
+ * ユニットテスト実行をしている場合のNODE_ENVの値
+ * @const {string[]}
+ */
+export const NODE_ENV_TEST = "test";
+
+/**
+ * ユーザに対するライセンスの状態
+ * @const {string[]}
+ */
+export const USER_LICENSE_STATUS = {
+ UNALLOCATED: "unallocated",
+ ALLOCATED: "allocated",
+ EXPIRED: "expired",
+} as const;
+
+/**
+ * typeの取りうる値(移行元CSV)
+ * @const {string[]}
+ */
+export const MIGRATION_TYPE = {
+ ADMINISTRATOR: "Administrator",
+ BC: "BC",
+ COUNTRY: "Country",
+ CUSTOMER: "Customer",
+ DEALER: "Dealer",
+ DISTRIBUTOR: "Distributor",
+ USER: "USER",
+} as const;
+
+/**
+ * 移行先の名称と移行元の値
+ * @const {string[]}
+ */
+export const COUNTRY_LIST = [
+ { value: "CA", label: "Canada" },
+ { value: "KY", label: "Cayman Islands" },
+ { value: "US", label: "U.S.A." },
+ { value: "AU", label: "Australia" },
+ { value: "NZ", label: "New Zealand" },
+ { value: "AT", label: "Austria" },
+ { value: "BE", label: "Belgium" },
+ { value: "BG", label: "Bulgaria" },
+ { value: "HR", label: "Croatia" },
+ { value: "CY", label: "Cyprus" },
+ { value: "CZ", label: "Czech Republic" },
+ { value: "DK", label: "Denmark" },
+ { value: "EE", label: "Estonia" },
+ { value: "FI", label: "Finland" },
+ { value: "FR", label: "France" },
+ { value: "DE", label: "Germany" },
+ { value: "GR", label: "Greece" },
+ { value: "HU", label: "Hungary" },
+ { value: "IS", label: "Iceland" },
+ { value: "IE", label: "Ireland" },
+ { value: "IT", label: "Italy" },
+ { value: "LV", label: "Latvia" },
+ { value: "LI", label: "Liechtenstein" },
+ { value: "LT", label: "Lithuania" },
+ { value: "LU", label: "Luxembourg" },
+ { value: "MT", label: "Malta" },
+ { value: "NL", label: "Netherlands" },
+ { value: "NO", label: "Norway" },
+ { value: "PL", label: "Poland" },
+ { value: "PT", label: "Portugal" },
+ { value: "RO", label: "Romania" },
+ { value: "RS", label: "Serbia" },
+ { value: "SK", label: "Slovakia" },
+ { value: "SI", label: "Slovenia" },
+ { value: "ZA", label: "South Africa" },
+ { value: "ES", label: "Spain" },
+ { value: "SE", label: "Sweden" },
+ { value: "CH", label: "Switzerland" },
+ { value: "TR", label: "Turkey" },
+ { value: "GB", label: "United Kingdom" },
+];
+
+/**
+ * recording_modeの取りうる値(移行元CSV)
+ * @const {string[]}
+ */
+export const RECORDING_MODE = {
+ DS2_QP: "DS2 (QP)",
+ DS2_SP: "DS2 (SP)",
+ DSS: "DSS",
+} as const;
+
+/**
+ * AutoIncrementの初期値
+ * @const {number}
+ */
+export const AUTO_INCREMENT_START = 853211;
+
+/**
+ * 移行データ登録時のsleep間隔
+ * @const {number}
+ */
+export const MIGRATION_DATA_REGISTER_INTERVAL_MILLISEC = 13;
diff --git a/data_migration_tools/server/src/features/accounts/accounts.controller.ts b/data_migration_tools/server/src/features/accounts/accounts.controller.ts
new file mode 100644
index 0000000..97ae0b9
--- /dev/null
+++ b/data_migration_tools/server/src/features/accounts/accounts.controller.ts
@@ -0,0 +1,12 @@
+import { Controller, Logger } from "@nestjs/common";
+import { ApiTags } from "@nestjs/swagger";
+import { AccountsService } from "./accounts.service";
+
+@ApiTags("accounts")
+@Controller("accounts")
+export class AccountsController {
+ private readonly logger = new Logger(AccountsController.name);
+ constructor(
+ private readonly accountService: AccountsService //private readonly cryptoService: CryptoService,
+ ) {}
+}
diff --git a/data_migration_tools/server/src/features/accounts/accounts.module.ts b/data_migration_tools/server/src/features/accounts/accounts.module.ts
new file mode 100644
index 0000000..cb6db2b
--- /dev/null
+++ b/data_migration_tools/server/src/features/accounts/accounts.module.ts
@@ -0,0 +1,19 @@
+import { Module } from "@nestjs/common";
+import { UsersRepositoryModule } from "../../repositories/users/users.repository.module";
+import { AccountsRepositoryModule } from "../../repositories/accounts/accounts.repository.module";
+import { AccountsController } from "./accounts.controller";
+import { AccountsService } from "./accounts.service";
+import { AdB2cModule } from "../../gateways/adb2c/adb2c.module";
+import { BlobstorageModule } from "../../gateways/blobstorage/blobstorage.module";
+
+@Module({
+ imports: [
+ AccountsRepositoryModule,
+ UsersRepositoryModule,
+ AdB2cModule,
+ BlobstorageModule,
+ ],
+ controllers: [AccountsController],
+ providers: [AccountsService],
+})
+export class AccountsModule {}
diff --git a/data_migration_tools/server/src/features/accounts/accounts.service.ts b/data_migration_tools/server/src/features/accounts/accounts.service.ts
new file mode 100644
index 0000000..4a7dd65
--- /dev/null
+++ b/data_migration_tools/server/src/features/accounts/accounts.service.ts
@@ -0,0 +1,227 @@
+import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
+import { AccountsRepositoryService } from "../../repositories/accounts/accounts.repository.service";
+import {
+ AdB2cService,
+ ConflictError,
+ isConflictError,
+} from "../../gateways/adb2c/adb2c.service";
+import { Account } from "../../repositories/accounts/entity/account.entity";
+import { User } from "../../repositories/users/entity/user.entity";
+import { MANUAL_RECOVERY_REQUIRED } from "../../constants";
+import { makeErrorResponse } from "../../common/error/makeErrorResponse";
+import { Context } from "../../common/log";
+import { BlobstorageService } from "../../gateways/blobstorage/blobstorage.service";
+
+@Injectable()
+export class AccountsService {
+ constructor(
+ private readonly accountRepository: AccountsRepositoryService,
+ private readonly adB2cService: AdB2cService,
+ private readonly blobStorageService: BlobstorageService
+ ) {}
+ private readonly logger = new Logger(AccountsService.name);
+
+ /**
+ * アカウント情報をDBに作成する
+ * @param companyName
+ * @param country
+ * @param [dealerAccountId]
+ * @returns account
+ */
+ async createAccount(
+ context: Context,
+ companyName: string,
+ country: string,
+ dealerAccountId: number | undefined,
+ email: string,
+ password: string,
+ username: string,
+ role: string,
+ acceptedEulaVersion: string,
+ acceptedPrivacyNoticeVersion: string,
+ acceptedDpaVersion: string,
+ type: number,
+ accountId: number,
+ userId: number
+ ): Promise<{ accountId: number; userId: number; externalUserId: string }> {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.createAccount.name
+ } | params: { ` +
+ `dealerAccountId: ${dealerAccountId}, ` +
+ `role: ${role}, ` +
+ `acceptedEulaVersion: ${acceptedEulaVersion}, ` +
+ `acceptedPrivacyNoticeVersion: ${acceptedPrivacyNoticeVersion}, ` +
+ `acceptedDpaVersion: ${acceptedDpaVersion}, ` +
+ `type: ${type}, ` +
+ `accountId: ${accountId}, ` +
+ `userId: ${userId} };`
+ );
+ try {
+ let externalUser: { sub: string } | ConflictError;
+ try {
+ // idpにユーザーを作成
+ externalUser = await this.adB2cService.createUser(
+ context,
+ email,
+ password,
+ username
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ this.logger.error(
+ `[${context.getTrackingId()}] create externalUser failed`
+ );
+
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ // メールアドレス重複エラー
+ if (isConflictError(externalUser)) {
+ this.logger.error(
+ `[${context.getTrackingId()}] email conflict. externalUser: ${externalUser}`
+ );
+ throw new HttpException(
+ makeErrorResponse("E010301"),
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ let account: Account;
+ let user: User;
+ try {
+ // アカウントと管理者をセットで作成
+ const { newAccount, adminUser } =
+ await this.accountRepository.createAccount(
+ context,
+ companyName,
+ country,
+ dealerAccountId,
+ type,
+ externalUser.sub,
+ role,
+ accountId,
+ userId,
+ acceptedEulaVersion,
+ acceptedPrivacyNoticeVersion,
+ acceptedDpaVersion
+ );
+ account = newAccount;
+ user = adminUser;
+ this.logger.log(
+ `[${context.getTrackingId()}] adminUser.external_id: ${
+ user.external_id
+ }`
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ this.logger.error(`[${context.getTrackingId()}] create account failed`);
+ //リカバリ処理
+ // idpのユーザーを削除
+ await this.deleteAdB2cUser(externalUser.sub, context);
+
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ // 新規作成アカウント用のBlobコンテナを作成
+ try {
+ await this.blobStorageService.createContainer(
+ context,
+ account.id,
+ country
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ this.logger.error(
+ `[${context.getTrackingId()}] create container failed`
+ );
+ //リカバリ処理
+ // idpのユーザーを削除
+ await this.deleteAdB2cUser(externalUser.sub, context);
+
+ // DBのアカウントを削除
+ await this.deleteAccount(account.id, user.id, context);
+
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ return {
+ accountId: account.id,
+ userId: user.id,
+ externalUserId: user.external_id,
+ };
+ } catch (e) {
+ throw e;
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.createAccount.name}`
+ );
+ }
+ }
+
+ // AdB2cのユーザーを削除
+ // TODO「タスク 2452: リトライ処理を入れる箇所を検討し、実装する」の候補
+ private async deleteAdB2cUser(
+ externalUserId: string,
+ context: Context
+ ): Promise {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.createAccount.name
+ } | params: { ` + `externalUserId: ${externalUserId}};`
+ );
+ try {
+ await this.adB2cService.deleteUser(externalUserId, context);
+ this.logger.log(
+ `[${context.getTrackingId()}] delete externalUser: ${externalUserId} | params: { ` +
+ `externalUserId: ${externalUserId}, };`
+ );
+ } catch (error) {
+ this.logger.error(`[${context.getTrackingId()}] error=${error}`);
+ this.logger.error(
+ `${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete externalUser: ${externalUserId}`
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.deleteAdB2cUser.name}`
+ );
+ }
+ }
+
+ // DBのアカウントを削除
+ private async deleteAccount(
+ accountId: number,
+ userId: number,
+ context: Context
+ ): Promise {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.deleteAccount.name
+ } | params: { accountId: ${accountId}, userId: ${userId} };`
+ );
+ try {
+ await this.accountRepository.deleteAccount(context, accountId, userId);
+ this.logger.log(
+ `[${context.getTrackingId()}] delete account: ${accountId}, user: ${userId}`
+ );
+ } catch (error) {
+ this.logger.error(`[${context.getTrackingId()}] error=${error}`);
+ this.logger.error(
+ `${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete account: ${accountId}, user: ${userId}`
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.deleteAccount.name}`
+ );
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/features/delete/delete.controller.spec.ts b/data_migration_tools/server/src/features/delete/delete.controller.spec.ts
new file mode 100644
index 0000000..ad763f0
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/delete.controller.spec.ts
@@ -0,0 +1,30 @@
+import { Test, TestingModule } from "@nestjs/testing";
+import { ConfigModule } from "@nestjs/config";
+import { DeleteService } from "./delete.service";
+import { DeleteController } from "./delete.controller";
+
+describe("DeleteController", () => {
+ let controller: DeleteController;
+ const mockTemplatesService = {};
+ beforeEach(async () => {
+ const module: TestingModule = await Test.createTestingModule({
+ imports: [
+ ConfigModule.forRoot({
+ envFilePath: [".env.test", ".env"],
+ isGlobal: true,
+ }),
+ ],
+ controllers: [DeleteController],
+ providers: [DeleteService],
+ })
+ .overrideProvider(DeleteService)
+ .useValue(mockTemplatesService)
+ .compile();
+
+ controller = module.get(DeleteController);
+ });
+
+ it("should be defined", () => {
+ expect(controller).toBeDefined();
+ });
+});
diff --git a/data_migration_tools/server/src/features/delete/delete.controller.ts b/data_migration_tools/server/src/features/delete/delete.controller.ts
new file mode 100644
index 0000000..94567ca
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/delete.controller.ts
@@ -0,0 +1,39 @@
+import {
+ Controller,
+ HttpException,
+ HttpStatus,
+ Logger,
+ Post,
+ Req,
+} from "@nestjs/common";
+import { ErrorResponse } from "../../common/errors/types/types";
+import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
+import { Request } from "express";
+import { DeleteService } from "./delete.service";
+import { DeleteResponse } from "./types/types";
+
+@ApiTags("delete")
+@Controller("delete")
+export class DeleteController {
+ constructor(private readonly deleteService: DeleteService) {}
+
+ @ApiResponse({
+ status: HttpStatus.OK,
+ type: DeleteResponse,
+ description: "成功時のレスポンス",
+ })
+ @ApiResponse({
+ status: HttpStatus.INTERNAL_SERVER_ERROR,
+ description: "想定外のサーバーエラー",
+ type: ErrorResponse,
+ })
+ @ApiOperation({
+ operationId: "deleteData",
+ description: "すべてのデータを削除します",
+ })
+ @Post()
+ async deleteData(): Promise<{}> {
+ await this.deleteService.deleteData();
+ return {};
+ }
+}
diff --git a/data_migration_tools/server/src/features/delete/delete.module.ts b/data_migration_tools/server/src/features/delete/delete.module.ts
new file mode 100644
index 0000000..ce27643
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/delete.module.ts
@@ -0,0 +1,13 @@
+import { Module } from "@nestjs/common";
+import { DeleteRepositoryModule } from "../../repositories/delete/delete.repository.module";
+import { DeleteController } from "./delete.controller";
+import { DeleteService } from "./delete.service";
+import { AdB2cModule } from "../../gateways/adb2c/adb2c.module";
+import { BlobstorageModule } from "../../gateways/blobstorage/blobstorage.module";
+
+@Module({
+ imports: [DeleteRepositoryModule, AdB2cModule, BlobstorageModule],
+ providers: [DeleteService],
+ controllers: [DeleteController],
+})
+export class DeleteModule {}
diff --git a/data_migration_tools/server/src/features/delete/delete.service.spec.ts b/data_migration_tools/server/src/features/delete/delete.service.spec.ts
new file mode 100644
index 0000000..3257293
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/delete.service.spec.ts
@@ -0,0 +1,29 @@
+import { DataSource } from "typeorm";
+import { ConfigModule } from "@nestjs/config";
+import { DeleteService } from "./delete.service";
+import { Test, TestingModule } from "@nestjs/testing";
+
+describe("DeleteController", () => {
+ let service: DeleteService;
+ const mockTemplatesService = {};
+ beforeEach(async () => {
+ const module: TestingModule = await Test.createTestingModule({
+ imports: [
+ ConfigModule.forRoot({
+ envFilePath: [".env.test", ".env"],
+ isGlobal: true,
+ }),
+ ],
+ providers: [DeleteService],
+ })
+ .overrideProvider(DeleteService)
+ .useValue(mockTemplatesService)
+ .compile();
+
+ service = module.get(DeleteService);
+ });
+
+ it("should be defined", () => {
+ expect(service).toBeDefined();
+ });
+});
diff --git a/data_migration_tools/server/src/features/delete/delete.service.ts b/data_migration_tools/server/src/features/delete/delete.service.ts
new file mode 100644
index 0000000..9ade573
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/delete.service.ts
@@ -0,0 +1,54 @@
+import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
+import { DeleteRepositoryService } from "../../repositories/delete/delete.repository.service";
+import { makeErrorResponse } from "../../common/errors/makeErrorResponse";
+import { AdB2cService } from "../../gateways/adb2c/adb2c.service";
+import { BlobstorageService } from "../../gateways/blobstorage/blobstorage.service";
+
+@Injectable()
+export class DeleteService {
+ private readonly logger = new Logger(DeleteService.name);
+ constructor(
+ private readonly deleteRepositoryService: DeleteRepositoryService,
+ private readonly blobstorageService: BlobstorageService,
+ private readonly adB2cService: AdB2cService
+ ) {}
+
+ /**
+ * データを削除する
+ * @returns data
+ */
+ async deleteData(): Promise {
+ this.logger.log(`[IN] ${this.deleteData.name}`);
+ try {
+ // BlobStorageからデータを削除する
+ await this.blobstorageService.deleteContainers();
+
+ // ADB2Cからユーザ情報を取得する
+ const users = await this.adB2cService.getUsers();
+ const externalIds = users.map((user) => user.id);
+ await this.adB2cService.deleteUsers(externalIds);
+
+ // データベースからデータを削除する
+ await this.deleteRepositoryService.deleteData();
+ // AutoIncrementの値をリセットする
+ await this.deleteRepositoryService.resetAutoIncrement();
+ } catch (e) {
+ this.logger.error(`error=${e}`);
+ if (e instanceof Error) {
+ switch (e.constructor) {
+ default:
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+ }
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ } finally {
+ this.logger.log(`[OUT] ${this.deleteData.name}`);
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/features/delete/test/utility.ts b/data_migration_tools/server/src/features/delete/test/utility.ts
new file mode 100644
index 0000000..e185d27
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/test/utility.ts
@@ -0,0 +1 @@
+import { DataSource } from "typeorm";
diff --git a/data_migration_tools/server/src/features/delete/types/types.ts b/data_migration_tools/server/src/features/delete/types/types.ts
new file mode 100644
index 0000000..1042758
--- /dev/null
+++ b/data_migration_tools/server/src/features/delete/types/types.ts
@@ -0,0 +1 @@
+export class DeleteResponse {}
diff --git a/data_migration_tools/server/src/features/register/register.controller.ts b/data_migration_tools/server/src/features/register/register.controller.ts
new file mode 100644
index 0000000..c55d548
--- /dev/null
+++ b/data_migration_tools/server/src/features/register/register.controller.ts
@@ -0,0 +1,209 @@
+import {
+ Body,
+ Controller,
+ HttpStatus,
+ Post,
+ Req,
+ Logger,
+ HttpException,
+} from "@nestjs/common";
+import { makeErrorResponse } from "../../common/error/makeErrorResponse";
+import fs from "fs";
+import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
+import { Request } from "express";
+import { RegisterRequest, RegisterResponse } from "./types/types";
+import { RegisterService } from "./register.service";
+import { AccountsService } from "../accounts/accounts.service";
+import { UsersService } from "../users/users.service";
+import { makeContext } from "../../common/log";
+import {
+ isAccountsInputFileArray,
+ isUsersInputFileArray,
+ isLicensesInputFileArray,
+ isWorktypesInputFileArray,
+ isCardLicensesInputFileArray,
+} from "../../common/types/types";
+import { makePassword } from "../../common/password/password";
+import {
+ USER_ROLES,
+ MIGRATION_DATA_REGISTER_INTERVAL_MILLISEC,
+} from "../../constants";
+
+@ApiTags("register")
+@Controller("register")
+export class RegisterController {
+ private readonly logger = new Logger(RegisterController.name);
+ constructor(
+ private readonly registerService: RegisterService,
+ private readonly accountsService: AccountsService,
+ private readonly usersService: UsersService
+ ) {}
+
+ @Post()
+ @ApiResponse({
+ status: HttpStatus.OK,
+ type: RegisterResponse,
+ description: "成功時のレスポンス",
+ })
+ @ApiResponse({
+ status: HttpStatus.INTERNAL_SERVER_ERROR,
+ description: "想定外のサーバーエラー",
+ })
+ @ApiOperation({ operationId: "dataRegist" })
+ async dataRegist(
+ @Body() body: RegisterRequest,
+ @Req() req: Request
+ ): Promise {
+ const context = makeContext("iko", "register");
+
+ const inputFilePath = body.inputFilePath;
+
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.dataRegist.name
+ } | params: { inputFilePath: ${inputFilePath}};`
+ );
+
+ try {
+ // 読み込みファイルのフルパス
+ const accouncsFileFullPath = inputFilePath + "accounts.json";
+ const usersFileFullPath = inputFilePath + "users.json";
+ const licensesFileFullPath = inputFilePath + "licenses.json";
+ const worktypesFileFullPath = inputFilePath + "worktypes.json";
+ const cardLicensesFileFullPath = inputFilePath + "cardLicenses.json";
+
+ // ファイル存在チェックと読み込み
+ if (
+ !fs.existsSync(accouncsFileFullPath) ||
+ !fs.existsSync(usersFileFullPath) ||
+ !fs.existsSync(licensesFileFullPath) ||
+ !fs.existsSync(worktypesFileFullPath) ||
+ !fs.existsSync(cardLicensesFileFullPath)
+ ) {
+ this.logger.error(`file not exists from ${inputFilePath}`);
+ throw new Error(`file not exists from ${inputFilePath}`);
+ }
+
+ // アカウントの登録用ファイル読み込み
+ const accountsObject = JSON.parse(
+ fs.readFileSync(accouncsFileFullPath, "utf8")
+ );
+
+ // 型ガード(account)
+ if (!isAccountsInputFileArray(accountsObject)) {
+ throw new Error("input file is not accountsInputFiles");
+ }
+
+ for (const accountsInputFile of accountsObject) {
+ // ランダムなパスワードを生成する
+ const ramdomPassword = makePassword();
+ await this.accountsService.createAccount(
+ context,
+ accountsInputFile.companyName,
+ accountsInputFile.country,
+ accountsInputFile.dealerAccountId,
+ accountsInputFile.adminMail,
+ ramdomPassword,
+ accountsInputFile.adminName,
+ "none",
+ null,
+ null,
+ null,
+ accountsInputFile.type,
+ accountsInputFile.accountId,
+ accountsInputFile.userId
+ );
+
+ // ratelimit対応のためsleepを行う
+ await sleep(MIGRATION_DATA_REGISTER_INTERVAL_MILLISEC);
+ }
+ // const accountsInputFiles = accountsObject as AccountsInputFile[];
+
+ // ユーザの登録用ファイル読み込み
+ const usersObject = JSON.parse(
+ fs.readFileSync(usersFileFullPath, "utf8")
+ );
+
+ // 型ガード(user)
+ if (!isUsersInputFileArray(usersObject)) {
+ throw new Error("input file is not usersInputFiles");
+ }
+
+ for (const usersInputFile of usersObject) {
+ this.logger.log(usersInputFile.name);
+ await this.usersService.createUser(
+ context,
+ usersInputFile.name,
+ usersInputFile.role === USER_ROLES.AUTHOR
+ ? USER_ROLES.AUTHOR
+ : USER_ROLES.NONE,
+ usersInputFile.email,
+ true,
+ true,
+ usersInputFile.accountId,
+ usersInputFile.userId,
+ usersInputFile.authorId,
+ false,
+ null,
+ true
+ );
+ // ratelimit対応のためsleepを行う
+ await sleep(MIGRATION_DATA_REGISTER_INTERVAL_MILLISEC);
+ }
+
+ // ライセンスの登録用ファイル読み込み
+ const licensesObject = JSON.parse(
+ fs.readFileSync(licensesFileFullPath, "utf8")
+ );
+
+ // 型ガード(license)
+ if (!isLicensesInputFileArray(licensesObject)) {
+ throw new Error("input file is not licensesInputFiles");
+ }
+
+ // ワークタイプの登録用ファイル読み込み
+ const worktypesObject = JSON.parse(
+ fs.readFileSync(worktypesFileFullPath, "utf8")
+ );
+
+ // 型ガード(Worktypes)
+ if (!isWorktypesInputFileArray(worktypesObject)) {
+ throw new Error("input file is not WorktypesInputFiles");
+ }
+
+ // カードライセンスの登録用ファイル読み込み
+ const cardLicensesObject = JSON.parse(
+ fs.readFileSync(cardLicensesFileFullPath, "utf8")
+ );
+
+ // 型ガード(cardLicenses)
+ if (!isCardLicensesInputFileArray(cardLicensesObject)) {
+ throw new Error("input file is not cardLicensesInputFiles");
+ }
+
+ // ライセンス・ワークタイプ・カードライセンスの登録
+ await this.registerService.registLicenseAndWorktypeData(
+ context,
+ licensesObject,
+ worktypesObject,
+ cardLicensesObject
+ );
+
+ return {};
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.dataRegist.name}`
+ );
+ }
+ }
+}
+
+function sleep(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
diff --git a/data_migration_tools/server/src/features/register/register.module.ts b/data_migration_tools/server/src/features/register/register.module.ts
new file mode 100644
index 0000000..10015dd
--- /dev/null
+++ b/data_migration_tools/server/src/features/register/register.module.ts
@@ -0,0 +1,25 @@
+import { Module } from "@nestjs/common";
+import { RegisterController } from "./register.controller";
+import { RegisterService } from "./register.service";
+import { AccountsService } from "../accounts/accounts.service";
+import { UsersService } from "../users/users.service";
+import { LicensesRepositoryModule } from "../../repositories/licenses/licenses.repository.module";
+import { WorktypesRepositoryModule } from "../../repositories/worktypes/worktypes.repository.module";
+import { UsersRepositoryModule } from "../../repositories/users/users.repository.module";
+import { AccountsRepositoryModule } from "../../repositories/accounts/accounts.repository.module";
+import { AdB2cModule } from "../../gateways/adb2c/adb2c.module";
+import { BlobstorageModule } from "../../gateways/blobstorage/blobstorage.module";
+
+@Module({
+ imports: [
+ LicensesRepositoryModule,
+ WorktypesRepositoryModule,
+ AccountsRepositoryModule,
+ UsersRepositoryModule,
+ AdB2cModule,
+ BlobstorageModule,
+ ],
+ controllers: [RegisterController],
+ providers: [RegisterService, AccountsService, UsersService],
+})
+export class RegisterModule {}
diff --git a/data_migration_tools/server/src/features/register/register.service.ts b/data_migration_tools/server/src/features/register/register.service.ts
new file mode 100644
index 0000000..6e42dc1
--- /dev/null
+++ b/data_migration_tools/server/src/features/register/register.service.ts
@@ -0,0 +1,68 @@
+import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
+import { Context } from "../../common/log";
+import {
+ LicensesInputFile,
+ WorktypesInputFile,
+ CardLicensesInputFile,
+} from "../../common/types/types";
+import { LicensesRepositoryService } from "../../repositories/licenses/licenses.repository.service";
+import { WorktypesRepositoryService } from "../../repositories/worktypes/worktypes.repository.service";
+import { makeErrorResponse } from "../../common/error/makeErrorResponse";
+@Injectable()
+export class RegisterService {
+ constructor(
+ private readonly licensesRepository: LicensesRepositoryService,
+ private readonly worktypesRepository: WorktypesRepositoryService
+ ) {}
+ private readonly logger = new Logger(RegisterService.name);
+
+ /**
+ * Regist Data
+ * @param inputFilePath: string
+ */
+ async registLicenseAndWorktypeData(
+ context: Context,
+ licensesInputFiles: LicensesInputFile[],
+ worktypesInputFiles: WorktypesInputFile[],
+ cardlicensesInputFiles: CardLicensesInputFile[]
+ ): Promise {
+ // パラメータ内容が長大なのでログには出さない
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.registLicenseAndWorktypeData.name
+ }`
+ );
+
+ try {
+ this.logger.log("Licenses register start");
+ await this.licensesRepository.insertLicenses(context, licensesInputFiles);
+ this.logger.log("Licenses register end");
+
+ this.logger.log("Worktypes register start");
+ await this.worktypesRepository.createWorktype(
+ context,
+ worktypesInputFiles
+ );
+ this.logger.log("Worktypes register end");
+
+ this.logger.log("CardLicenses register start");
+ await this.licensesRepository.insertCardLicenses(
+ context,
+ cardlicensesInputFiles
+ );
+ this.logger.log("CardLicenses register end");
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${
+ this.registLicenseAndWorktypeData.name
+ }`
+ );
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/features/register/types/types.ts b/data_migration_tools/server/src/features/register/types/types.ts
new file mode 100644
index 0000000..30fe678
--- /dev/null
+++ b/data_migration_tools/server/src/features/register/types/types.ts
@@ -0,0 +1,10 @@
+import { ApiProperty } from '@nestjs/swagger';
+
+export class RegisterRequest {
+ @ApiProperty()
+ inputFilePath: string;
+
+}
+
+export class RegisterResponse {}
+
diff --git a/data_migration_tools/server/src/features/transfer/transfer.controller.ts b/data_migration_tools/server/src/features/transfer/transfer.controller.ts
new file mode 100644
index 0000000..8ac9923
--- /dev/null
+++ b/data_migration_tools/server/src/features/transfer/transfer.controller.ts
@@ -0,0 +1,174 @@
+import {
+ Body,
+ Controller,
+ HttpStatus,
+ Post,
+ Req,
+ HttpException,
+ Logger,
+} from "@nestjs/common";
+import fs from "fs";
+import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
+import { Request } from "express";
+import { transferRequest, transferResponse } from "./types/types";
+import { transferService } from "./transfer.service";
+import { makeContext } from "../../common/log";
+import { csvInputFile } from "../../common/types/types";
+import { makeErrorResponse } from "src/common/errors/makeErrorResponse";
+import {
+ COUNTRY_LIST,
+ MIGRATION_TYPE,
+ TIERS,
+ WORKTYPE_MAX_COUNT,
+ RECORDING_MODE,
+ LICENSE_ALLOCATED_STATUS,
+ USER_ROLES,
+ AUTO_INCREMENT_START,
+} from "../../../src/constants";
+@ApiTags("transfer")
+@Controller("transfer")
+export class transferController {
+ private readonly logger = new Logger(transferController.name);
+ constructor(private readonly transferService: transferService) {}
+
+ @Post()
+ @ApiResponse({
+ status: HttpStatus.OK,
+ type: transferResponse,
+ description: "成功時のレスポンス",
+ })
+ @ApiResponse({
+ status: HttpStatus.INTERNAL_SERVER_ERROR,
+ description: "想定外のサーバーエラー",
+ })
+ @ApiOperation({ operationId: "dataRegist" })
+ async dataRegist(
+ @Body() body: transferRequest,
+ @Req() req: Request
+ ): Promise {
+ const context = makeContext("iko", "transfer");
+
+ const inputFilePath = body.inputFilePath;
+
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.dataRegist.name
+ } | params: { inputFilePath: ${inputFilePath}};`
+ );
+ try {
+ // 読み込みファイルのフルパス
+ const csvFileFullPath = inputFilePath + ".csv";
+
+ // ファイル存在チェックと読み込み
+ if (!fs.existsSync(csvFileFullPath)) {
+ this.logger.error(`file not exists from ${inputFilePath}`);
+ throw new Error(`file not exists from ${inputFilePath}`);
+ }
+
+ // CSVファイルを全行読み込む
+ const inputFile = fs.readFileSync(csvFileFullPath, "utf-8");
+
+ // レコードごとに分割
+ const csvInputFileLines = inputFile.split("\n");
+
+ // ヘッダー行を削除
+ csvInputFileLines.shift();
+
+ // 項目ごとに切り分ける
+ let csvInputFile: csvInputFile[] = [];
+ csvInputFileLines.forEach((line) => {
+ const data = line.split(",");
+ csvInputFile.push({
+ type: data[0],
+ account_id: data[1],
+ parent_id: data[2],
+ email: data[3],
+ company_name: data[4],
+ first_name: data[5],
+ last_name: data[6],
+ country: data[7],
+ state: data[8],
+ start_date: new Date(data[9]),
+ expired_date: new Date(data[10]),
+ user_email: data[11],
+ author_id: data[12],
+ recording_mode: data[13],
+ wt1: data[14],
+ wt2: data[15],
+ wt3: data[16],
+ wt4: data[17],
+ wt5: data[18],
+ wt6: data[19],
+ wt7: data[20],
+ wt8: data[21],
+ wt9: data[22],
+ wt10: data[23],
+ wt11: data[24],
+ wt12: data[25],
+ wt13: data[26],
+ wt14: data[27],
+ wt15: data[28],
+ wt16: data[29],
+ wt17: data[30],
+ wt18: data[31],
+ wt19: data[32],
+ wt20: data[33],
+ });
+ });
+
+ // 各データのバリデーションチェック
+ await this.transferService.validateInputData(context, csvInputFile);
+
+ // account_idを通番に変換し、変換前account_id: 変換後accountId配列を作成する。
+ const accountIdList = csvInputFile.map((line) => line.account_id);
+ const accountIdListSet = new Set(accountIdList);
+ const accountIdListArray = Array.from(accountIdListSet);
+ const accountIdMap = new Map();
+ accountIdListArray.forEach((accountId, index) => {
+ accountIdMap.set(accountId, index + AUTO_INCREMENT_START);
+ });
+ // CSVファイルの変換
+ const transferResponse = await this.transferService.registInputData(
+ context,
+ csvInputFile,
+ accountIdMap
+ );
+
+ // countryを除いた階層の再配置
+ const accountsOutputFileStep1Lines =
+ transferResponse.accountsOutputFileStep1Lines;
+ const accountsOutputFile = await this.transferService.relocateHierarchy(
+ context,
+ accountsOutputFileStep1Lines
+ );
+ // メールアドレスの重複を削除
+ // デモライセンスの削除
+ // いったんこのままコミットしてテストを実施する
+
+ // transferResponseを4つのJSONファイルの出力する(出力先はinputと同じにする)
+ const outputFilePath = body.inputFilePath;
+ const usersOutputFile = transferResponse.usersOutputFileLines;
+ const licensesOutputFile = transferResponse.licensesOutputFileLines;
+ const worktypesOutputFile = transferResponse.worktypesOutputFileLines;
+ this.transferService.outputJsonFile(
+ context,
+ outputFilePath,
+ accountsOutputFile,
+ usersOutputFile,
+ licensesOutputFile,
+ worktypesOutputFile
+ );
+ return {};
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.dataRegist.name}`
+ );
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/features/transfer/transfer.module.ts b/data_migration_tools/server/src/features/transfer/transfer.module.ts
new file mode 100644
index 0000000..b2a7893
--- /dev/null
+++ b/data_migration_tools/server/src/features/transfer/transfer.module.ts
@@ -0,0 +1,9 @@
+import { Module } from "@nestjs/common";
+import { transferController } from "./transfer.controller";
+import { transferService } from "./transfer.service";
+@Module({
+ imports: [],
+ controllers: [transferController],
+ providers: [transferService],
+})
+export class transferModule {}
diff --git a/data_migration_tools/server/src/features/transfer/transfer.service.ts b/data_migration_tools/server/src/features/transfer/transfer.service.ts
new file mode 100644
index 0000000..70b5d26
--- /dev/null
+++ b/data_migration_tools/server/src/features/transfer/transfer.service.ts
@@ -0,0 +1,357 @@
+import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
+import { Context } from "../../common/log";
+import {
+ AccountsOutputFileStep1,
+ UsersOutputFile,
+ LicensesOutputFile,
+ WorktypesOutputFile,
+ csvInputFile,
+ AccountsOutputFile,
+} from "../../common/types/types";
+import {
+ COUNTRY_LIST,
+ MIGRATION_TYPE,
+ TIERS,
+ WORKTYPE_MAX_COUNT,
+ RECORDING_MODE,
+ LICENSE_ALLOCATED_STATUS,
+ USER_ROLES,
+ SWITCH_FROM_TYPE,
+} from "src/constants";
+import { registInputDataResponse } from "./types/types";
+import fs from "fs";
+
+@Injectable()
+export class transferService {
+ constructor() {}
+ private readonly logger = new Logger(transferService.name);
+
+ /**
+ * Regist Data
+ * @param OutputFilePath: string
+ * @param csvInputFile: csvInputFile[]
+ */
+ async registInputData(
+ context: Context,
+ csvInputFile: csvInputFile[],
+ accountIdMap: Map
+ ): Promise {
+ // パラメータ内容が長大なのでログには出さない
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${this.registInputData.name}`
+ );
+
+ try {
+ let accountsOutputFileStep1Lines: AccountsOutputFileStep1[] = [];
+ let usersOutputFileLines: UsersOutputFile[] = [];
+ let licensesOutputFileLines: LicensesOutputFile[] = [];
+ let worktypesOutputFileLines: WorktypesOutputFile[] = [];
+
+ let userIdIndex = 0;
+ // csvInputFileを一行読み込みする
+ csvInputFile.forEach((line) => {
+ // typeが"USER"以外の場合、アカウントデータの作成を行う
+ if (line.type !== MIGRATION_TYPE.USER) {
+ // userIdのインクリメント
+ userIdIndex = userIdIndex + 1;
+ // line.countryの値を読み込みCOUNTRY_LISTのlabelからvalueに変換する
+ const country = COUNTRY_LIST.find(
+ (country) => country.label === line.country
+ )?.value;
+ // adminNameの変換(last_name + " "+ first_name)
+ const adminName = `${line.last_name} ${line.first_name}`;
+
+ // ランダムパスワードの生成(データ登録ツール側で行うのでやらない)
+ // common/password/password.tsのmakePasswordを使用
+ // const autoGeneratedPassword = makePassword();
+
+ // parentAccountIdの設定
+ // parent_idが存在する場合、accountIdMapを参照し、accountIdに変換する
+ let parentAccountId: number | null = null;
+ if (line.parent_id) {
+ parentAccountId = accountIdMap.get(line.parent_id);
+ }
+ // AccountsOutputFile配列にPush
+ accountsOutputFileStep1Lines.push({
+ // accountIdはaccountIdMapから取得する
+ accountId: accountIdMap.get(line.account_id),
+ type: line.type,
+ companyName: line.company_name,
+ country: country,
+ dealerAccountId: parentAccountId,
+ adminName: adminName,
+ adminMail: line.email,
+ userId: userIdIndex,
+ });
+ } else {
+ // typeが"USER"の場合、ユーザデータの作成を行う
+ // userIdのインクリメント
+ userIdIndex = userIdIndex + 1;
+ // nameの変換
+ // もしline.last_nameとline.first_nameが存在しない場合、line.emailをnameにする
+ // 存在する場合は、last_name + " " + first_name
+ let name = line.email;
+ if (line.last_name && line.first_name) {
+ name = `${line.last_name} ${line.first_name}`;
+ }
+ // roleの変換
+ // authorIdが設定されてる場合はauthor、されていない場合は移行しないので次の行に進む
+ if (line.author_id) {
+ usersOutputFileLines.push({
+ accountId: accountIdMap.get(line.account_id),
+ userId: userIdIndex,
+ name: name,
+ role: USER_ROLES.AUTHOR,
+ authorId: line.author_id,
+ email: line.user_email,
+ });
+ } else {
+ return;
+ }
+ // ライセンスのデータの作成を行う
+ // authorIdが設定されてる場合、statusは"allocated"、allocated_user_idは対象のユーザID
+ // されていない場合、statusは"reusable"、allocated_user_idはnull
+ licensesOutputFileLines.push({
+ expiry_date: line.expired_date.toISOString(),
+ account_id: accountIdMap.get(line.account_id),
+ type: SWITCH_FROM_TYPE.NONE,
+ status: line.author_id
+ ? LICENSE_ALLOCATED_STATUS.ALLOCATED
+ : LICENSE_ALLOCATED_STATUS.REUSABLE,
+ allocated_user_id: line.author_id ? userIdIndex : null,
+ });
+ // WorktypesOutputFileの作成
+ // wt1~wt20まで読み込み、account単位で作成する
+ // 作成したWorktypesOutputFileを配列にPush
+ for (let i = 1; i <= WORKTYPE_MAX_COUNT; i++) {
+ const wt = `wt${i}`;
+ if (line[wt]) {
+ // 既に存在する場合は、作成しない
+ if (
+ worktypesOutputFileLines.find(
+ (worktype) =>
+ worktype.account_id === accountIdMap.get(line.account_id) &&
+ worktype.custom_worktype_id === line[wt].toString()
+ )
+ ) {
+ continue;
+ }
+ }
+ }
+ }
+ // つぎの行に進む
+ });
+ return {
+ accountsOutputFileStep1Lines,
+ usersOutputFileLines,
+ licensesOutputFileLines,
+ worktypesOutputFileLines,
+ };
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.registInputData.name}`
+ );
+ }
+ }
+
+ /**
+ * 階層の付け替えを行う
+ * @param accountsOutputFileStep1: AccountsOutputFileStep1[]
+ * @returns AccountsOutputFile[]
+ */
+ async relocateHierarchy(
+ context: Context,
+ accountsOutputFileStep1: AccountsOutputFileStep1[]
+ ): Promise {
+ // パラメータ内容が長大なのでログには出さない
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${this.relocateHierarchy.name}`
+ );
+
+ try {
+ // dealerAccountIdを検索し、typeがCountryの場合
+ accountsOutputFileStep1.forEach((account) => {
+ if (account.type === MIGRATION_TYPE.COUNTRY) {
+ // そのacccountIdをdealerAccountIdにもつアカウント(Distributor)を検索する
+ const distributor = accountsOutputFileStep1.find(
+ (distributor) =>
+ account.type === MIGRATION_TYPE.DISTRIBUTOR &&
+ distributor.dealerAccountId === account.accountId
+ );
+ // DistributorのdealerAccountIdをBC(Countryの親)に付け替える
+ distributor.dealerAccountId = account.dealerAccountId;
+ }
+ });
+ // typeがCountryのアカウントを取り除く
+ accountsOutputFileStep1 = accountsOutputFileStep1.filter(
+ (account) => account.type !== MIGRATION_TYPE.COUNTRY
+ );
+
+ // typeをtierに変換し、AccountsOutputFileに変換する
+ let accountsOutputFile: AccountsOutputFile[] = [];
+ accountsOutputFileStep1.forEach((account) => {
+ let tier = 0;
+ switch (account.type) {
+ case MIGRATION_TYPE.ADMINISTRATOR:
+ tier = TIERS.TIER1;
+ break;
+ case MIGRATION_TYPE.BC:
+ tier = TIERS.TIER2;
+ break;
+ case MIGRATION_TYPE.DISTRIBUTOR:
+ tier = TIERS.TIER3;
+ break;
+ case MIGRATION_TYPE.DEALER:
+ tier = TIERS.TIER4;
+ break;
+ case MIGRATION_TYPE.CUSTOMER:
+ tier = TIERS.TIER5;
+ break;
+ }
+ accountsOutputFile.push({
+ accountId: account.accountId,
+ type: tier,
+ companyName: account.companyName,
+ country: account.country,
+ dealerAccountId: account.dealerAccountId,
+ adminName: account.adminName,
+ adminMail: account.adminMail,
+ userId: account.userId,
+ });
+ });
+ return accountsOutputFile;
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.relocateHierarchy.name}`
+ );
+ }
+ }
+
+ /**
+ * JSONファイルの出力
+ * @param outputFilePath: string
+ * @param accountsOutputFile: AccountsOutputFile[]
+ * @param usersOutputFile: UsersOutputFile[]
+ * @param licensesOutputFile: LicensesOutputFile[]
+ * @param worktypesOutputFile: WorktypesOutputFile[]
+ */
+ async outputJsonFile(
+ context: Context,
+ outputFilePath: string,
+ accountsOutputFile: AccountsOutputFile[],
+ usersOutputFile: UsersOutputFile[],
+ licensesOutputFile: LicensesOutputFile[],
+ worktypesOutputFile: WorktypesOutputFile[]
+ ): Promise {
+ // パラメータ内容が長大なのでログには出さない
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${this.outputJsonFile.name}`
+ );
+
+ try {
+ // JSONファイルの出力を行う
+ // accountsOutputFile配列の出力
+ const accountsOutputFileJson = JSON.stringify(accountsOutputFile);
+ fs.writeFileSync(
+ `${outputFilePath}_accounts.json`,
+ accountsOutputFileJson
+ );
+ // usersOutputFile
+ const usersOutputFileJson = JSON.stringify(usersOutputFile);
+ fs.writeFileSync(`${outputFilePath}_users.json`, usersOutputFileJson);
+ // licensesOutputFile
+ const licensesOutputFileJson = JSON.stringify(licensesOutputFile);
+ fs.writeFileSync(
+ `${outputFilePath}_licenses.json`,
+ licensesOutputFileJson
+ );
+ // worktypesOutputFile
+ const worktypesOutputFileJson = JSON.stringify(worktypesOutputFile);
+ fs.writeFileSync(
+ `${outputFilePath}_worktypes.json`,
+ worktypesOutputFileJson
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.outputJsonFile.name}`
+ );
+ }
+ }
+
+ /**
+ * データのバリデーションチェック
+ * @param csvInputFile: csvInputFile[]
+ */
+ async validateInputData(
+ context: Context,
+ csvInputFile: csvInputFile[]
+ ): Promise {
+ // パラメータ内容が長大なのでログには出さない
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${this.validateInputData.name}`
+ );
+
+ try {
+ // csvInputFileのバリデーションチェックを行う
+ csvInputFile.forEach((line, index) => {
+ // typeのバリデーションチェック
+ if (
+ line.type !== MIGRATION_TYPE.ADMINISTRATOR &&
+ line.type !== MIGRATION_TYPE.BC &&
+ line.type !== MIGRATION_TYPE.DISTRIBUTOR &&
+ line.type !== MIGRATION_TYPE.DEALER &&
+ line.type !== MIGRATION_TYPE.CUSTOMER &&
+ line.type !== MIGRATION_TYPE.USER
+ ) {
+ throw new HttpException(
+ `type is invalid. index=${index} type=${line.type}`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ // countryのバリデーションチェック
+ if (!COUNTRY_LIST.find((country) => country.label === line.country)) {
+ throw new HttpException(
+ `country is invalid. index=${index} country=${line.country}`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ // mailのバリデーションチェック
+ // メールアドレスの形式が正しいかどうかのチェック
+ const mailRegExp = new RegExp(
+ /^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/
+ );
+ if (!mailRegExp.test(line.email)) {
+ throw new HttpException(
+ `email is invalid. index=${index} email=${line.email}`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ // recording_modeのバリデーションチェック
+ // RECORDING_MODEに存在するかどうかのチェック
+ if (
+ line.recording_mode !== RECORDING_MODE.DS2_QP &&
+ line.recording_mode !== RECORDING_MODE.DS2_SP &&
+ line.recording_mode !== RECORDING_MODE.DSS &&
+ line.recording_mode !== null
+ ) {
+ throw new HttpException(
+ `recording_mode is invalid. index=${index} recording_mode=${line.recording_mode}`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ });
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.validateInputData.name}`
+ );
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/features/transfer/types/types.ts b/data_migration_tools/server/src/features/transfer/types/types.ts
new file mode 100644
index 0000000..63eb3ab
--- /dev/null
+++ b/data_migration_tools/server/src/features/transfer/types/types.ts
@@ -0,0 +1,25 @@
+import { ApiProperty } from "@nestjs/swagger";
+import {
+ AccountsOutputFileStep1,
+ LicensesOutputFile,
+ UsersOutputFile,
+ WorktypesOutputFile,
+} from "src/common/types/types";
+
+export class transferRequest {
+ @ApiProperty()
+ inputFilePath: string;
+}
+
+export class transferResponse {}
+
+export class registInputDataResponse {
+ @ApiProperty()
+ accountsOutputFileStep1Lines: AccountsOutputFileStep1[];
+ @ApiProperty()
+ usersOutputFileLines: UsersOutputFile[];
+ @ApiProperty()
+ licensesOutputFileLines: LicensesOutputFile[];
+ @ApiProperty()
+ worktypesOutputFileLines: WorktypesOutputFile[];
+}
diff --git a/data_migration_tools/server/src/features/users/users.controller.ts b/data_migration_tools/server/src/features/users/users.controller.ts
new file mode 100644
index 0000000..14c5cad
--- /dev/null
+++ b/data_migration_tools/server/src/features/users/users.controller.ts
@@ -0,0 +1,10 @@
+import { Controller, Logger } from "@nestjs/common";
+import { ApiTags } from "@nestjs/swagger";
+import { UsersService } from "./users.service";
+
+@ApiTags("users")
+@Controller("users")
+export class UsersController {
+ private readonly logger = new Logger(UsersController.name);
+ constructor(private readonly usersService: UsersService) {}
+}
diff --git a/data_migration_tools/server/src/features/users/users.module.ts b/data_migration_tools/server/src/features/users/users.module.ts
new file mode 100644
index 0000000..3f0da57
--- /dev/null
+++ b/data_migration_tools/server/src/features/users/users.module.ts
@@ -0,0 +1,12 @@
+import { Module } from "@nestjs/common";
+import { AdB2cModule } from "../../gateways/adb2c/adb2c.module";
+import { UsersRepositoryModule } from "../../repositories/users/users.repository.module";
+import { UsersController } from "./users.controller";
+import { UsersService } from "./users.service";
+
+@Module({
+ imports: [UsersRepositoryModule, AdB2cModule],
+ controllers: [UsersController],
+ providers: [UsersService],
+})
+export class UsersModule {}
diff --git a/data_migration_tools/server/src/features/users/users.service.ts b/data_migration_tools/server/src/features/users/users.service.ts
new file mode 100644
index 0000000..cb639cd
--- /dev/null
+++ b/data_migration_tools/server/src/features/users/users.service.ts
@@ -0,0 +1,306 @@
+import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
+import { makeErrorResponse } from "../../common/error/makeErrorResponse";
+import { makePassword } from "../../common/password/password";
+import {
+ AdB2cService,
+ ConflictError,
+ isConflictError,
+} from "../../gateways/adb2c/adb2c.service";
+import {
+ User as EntityUser,
+ newUser,
+} from "../../repositories/users/entity/user.entity";
+import { UsersRepositoryService } from "../../repositories/users/users.repository.service";
+import { MANUAL_RECOVERY_REQUIRED, USER_ROLES } from "../../constants";
+import { Context } from "../../common/log";
+import { UserRoles } from "../../common/types/role";
+
+@Injectable()
+export class UsersService {
+ private readonly logger = new Logger(UsersService.name);
+ constructor(
+ private readonly usersRepository: UsersRepositoryService,
+ private readonly adB2cService: AdB2cService
+ ) {}
+
+ /**
+ * Creates user
+ * @param context
+ * @param name
+ * @param role
+ * @param email
+ * @param autoRenew
+ * @param notification
+ * @param accountId
+ * @param userid
+ * @param [authorId]
+ * @param [encryption]
+ * @param [encryptionPassword]
+ * @param [prompt]
+ * @returns user
+ */
+ async createUser(
+ context: Context,
+ name: string,
+ role: UserRoles,
+ email: string,
+ autoRenew: boolean,
+ notification: boolean,
+ accountId: number,
+ userid: number,
+ authorId?: string | undefined,
+ encryption?: boolean | undefined,
+ encryptionPassword?: string | undefined,
+ prompt?: boolean | undefined
+ ): Promise {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${this.createUser.name} | params: { ` +
+ `role: ${role}, ` +
+ `autoRenew: ${autoRenew}, ` +
+ `notification: ${notification}, ` +
+ `accountId: ${accountId}, ` +
+ `userid: ${userid}, ` +
+ `authorId: ${authorId}, ` +
+ `encryption: ${encryption}, ` +
+ `prompt: ${prompt} };`
+ );
+
+ //authorIdが重複していないかチェックする
+ if (authorId) {
+ let isAuthorIdDuplicated = false;
+ try {
+ isAuthorIdDuplicated = await this.usersRepository.existsAuthorId(
+ context,
+ accountId,
+ authorId
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+ if (isAuthorIdDuplicated) {
+ throw new HttpException(
+ makeErrorResponse("E010302"),
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ }
+
+ // ランダムなパスワードを生成する
+ const ramdomPassword = makePassword();
+
+ //Azure AD B2Cにユーザーを新規登録する
+ let externalUser: { sub: string } | ConflictError;
+ try {
+ this.logger.log(`name=${name}`);
+ // idpにユーザーを作成
+ externalUser = await this.adB2cService.createUser(
+ context,
+ email,
+ ramdomPassword,
+ name
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ this.logger.error(
+ `[${context.getTrackingId()}] create externalUser failed`
+ );
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ // メールアドレス重複エラー
+ if (isConflictError(externalUser)) {
+ throw new HttpException(
+ makeErrorResponse("E010301"),
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ //Azure AD B2Cに登録したユーザー情報のID(sub)と受け取った情報を使ってDBにユーザーを登録する
+ let newUser: EntityUser;
+
+ try {
+ //roleに応じてユーザー情報を作成する
+ const newUserInfo = this.createNewUserInfo(
+ context,
+ userid,
+ role,
+ accountId,
+ externalUser.sub,
+ autoRenew,
+ notification,
+ authorId,
+ encryption,
+ encryptionPassword,
+ prompt
+ );
+ // ユーザ作成
+ newUser = await this.usersRepository.createNormalUser(
+ context,
+ newUserInfo
+ );
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ this.logger.error(`[${context.getTrackingId()}]create user failed`);
+ //リカバリー処理
+ //Azure AD B2Cに登録したユーザー情報を削除する
+ await this.deleteB2cUser(externalUser.sub, context);
+
+ switch (e.code) {
+ case "ER_DUP_ENTRY":
+ //AuthorID重複エラー
+ throw new HttpException(
+ makeErrorResponse("E010302"),
+ HttpStatus.BAD_REQUEST
+ );
+ default:
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+ }
+
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.createUser.name}`
+ );
+ return;
+ }
+
+ // Azure AD B2Cに登録したユーザー情報を削除する
+ // TODO 「タスク 2452: リトライ処理を入れる箇所を検討し、実装する」の候補
+ private async deleteB2cUser(externalUserId: string, context: Context) {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.deleteB2cUser.name
+ } | params: { externalUserId: ${externalUserId} }`
+ );
+ try {
+ await this.adB2cService.deleteUser(externalUserId, context);
+ this.logger.log(
+ `[${context.getTrackingId()}] delete externalUser: ${externalUserId}`
+ );
+ } catch (error) {
+ this.logger.error(`[${context.getTrackingId()}] error=${error}`);
+ this.logger.error(
+ `${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete externalUser: ${externalUserId}`
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.deleteB2cUser.name}`
+ );
+ }
+ }
+
+ // DBに登録したユーザー情報を削除する
+ private async deleteUser(userId: number, context: Context) {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.deleteUser.name
+ } | params: { userId: ${userId} }`
+ );
+ try {
+ await this.usersRepository.deleteNormalUser(context, userId);
+ this.logger.log(`[${context.getTrackingId()}] delete user: ${userId}`);
+ } catch (error) {
+ this.logger.error(`[${context.getTrackingId()}] error=${error}`);
+ this.logger.error(
+ `${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete user: ${userId}`
+ );
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.deleteUser.name}`
+ );
+ }
+ }
+
+ // roleを受け取って、roleに応じたnewUserを作成して返却する
+ private createNewUserInfo(
+ context: Context,
+ id: number,
+ role: UserRoles,
+ accountId: number,
+ externalId: string,
+ autoRenew: boolean,
+ notification: boolean,
+ authorId?: string | undefined,
+ encryption?: boolean | undefined,
+ encryptionPassword?: string | undefined,
+ prompt?: boolean | undefined
+ ): newUser {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.createNewUserInfo.name
+ } | params: { ` +
+ `id: ${id}, ` +
+ `role: ${role}, ` +
+ `accountId: ${accountId}, ` +
+ `authorId: ${authorId}, ` +
+ `externalId: ${externalId}, ` +
+ `autoRenew: ${autoRenew}, ` +
+ `notification: ${notification}, ` +
+ `authorId: ${authorId}, ` +
+ `encryption: ${encryption}, ` +
+ `prompt: ${prompt} };`
+ );
+ try {
+ switch (role) {
+ case USER_ROLES.NONE:
+ case USER_ROLES.TYPIST:
+ return {
+ id,
+ account_id: accountId,
+ external_id: externalId,
+ auto_renew: autoRenew,
+ notification,
+ role,
+ accepted_dpa_version: null,
+ accepted_eula_version: null,
+ accepted_privacy_notice_version: null,
+ encryption: false,
+ encryption_password: null,
+ prompt: false,
+ author_id: null,
+ };
+ case USER_ROLES.AUTHOR:
+ return {
+ id,
+ account_id: accountId,
+ external_id: externalId,
+ auto_renew: autoRenew,
+ notification,
+ role,
+ author_id: authorId ?? null,
+ encryption: encryption ?? false,
+ encryption_password: encryptionPassword ?? null,
+ prompt: prompt ?? false,
+ accepted_dpa_version: null,
+ accepted_eula_version: null,
+ accepted_privacy_notice_version: null,
+ };
+ default:
+ //不正なroleが指定された場合はログを出力してエラーを返す
+ this.logger.error(
+ `[${context.getTrackingId()}] [NOT IMPLEMENT] [RECOVER] role: ${role}`
+ );
+ throw new HttpException(
+ makeErrorResponse("E009999"),
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ return e;
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.createNewUserInfo.name}`
+ );
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/gateways/adb2c/adb2c.module.ts b/data_migration_tools/server/src/gateways/adb2c/adb2c.module.ts
new file mode 100644
index 0000000..f54582b
--- /dev/null
+++ b/data_migration_tools/server/src/gateways/adb2c/adb2c.module.ts
@@ -0,0 +1,10 @@
+import { Module } from "@nestjs/common";
+import { ConfigModule } from "@nestjs/config";
+import { AdB2cService } from "./adb2c.service";
+
+@Module({
+ imports: [ConfigModule],
+ exports: [AdB2cService],
+ providers: [AdB2cService],
+})
+export class AdB2cModule {}
diff --git a/data_migration_tools/server/src/gateways/adb2c/adb2c.service.ts b/data_migration_tools/server/src/gateways/adb2c/adb2c.service.ts
new file mode 100644
index 0000000..b561f42
--- /dev/null
+++ b/data_migration_tools/server/src/gateways/adb2c/adb2c.service.ts
@@ -0,0 +1,218 @@
+import { ClientSecretCredential } from "@azure/identity";
+import { Client } from "@microsoft/microsoft-graph-client";
+import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials";
+import { Injectable, Logger } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+import { AdB2cResponse, AdB2cUser } from "./types/types";
+import { isPromiseRejectedResult } from "./utils/utils";
+import { Context } from "../../common/log";
+import { ADB2C_SIGN_IN_TYPE } from "../../constants";
+
+export type ConflictError = {
+ reason: "email";
+ message: string;
+};
+
+export class Adb2cTooManyRequestsError extends Error {}
+
+export const isConflictError = (arg: unknown): arg is ConflictError => {
+ const value = arg as ConflictError;
+ if (value.message === undefined) {
+ return false;
+ }
+ if (value.reason === "email") {
+ return true;
+ }
+ return false;
+};
+
+@Injectable()
+export class AdB2cService {
+ private readonly logger = new Logger(AdB2cService.name);
+ private readonly tenantName: string;
+ private readonly flowName: string;
+ private readonly ttl: number;
+ private graphClient: Client;
+
+ constructor(private readonly configService: ConfigService) {
+ this.tenantName = this.configService.getOrThrow("TENANT_NAME");
+ this.flowName = this.configService.getOrThrow("SIGNIN_FLOW_NAME");
+ this.ttl = this.configService.getOrThrow("ADB2C_CACHE_TTL");
+
+ // ADB2Cへの認証情報
+ const credential = new ClientSecretCredential(
+ this.configService.getOrThrow("ADB2C_TENANT_ID"),
+ this.configService.getOrThrow("ADB2C_CLIENT_ID"),
+ this.configService.getOrThrow("ADB2C_CLIENT_SECRET")
+ );
+ const authProvider = new TokenCredentialAuthenticationProvider(credential, {
+ scopes: ["https://graph.microsoft.com/.default"],
+ });
+
+ this.graphClient = Client.initWithMiddleware({ authProvider });
+ }
+
+ /**
+ * Creates user AzureADB2Cにユーザーを追加する
+ * @param email 管理ユーザーのメールアドレス
+ * @param password 管理ユーザーのパスワード
+ * @param username 管理ユーザーの名前
+ * @returns user
+ */
+ async createUser(
+ context: Context,
+ email: string,
+ password: string,
+ username: string
+ ): Promise<{ sub: string } | ConflictError> {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${this.createUser.name}`
+ );
+ try {
+ // ユーザをADB2Cに登録
+ const newUser = await this.graphClient.api("users/").post({
+ accountEnabled: true,
+ displayName: username,
+ passwordPolicies: "DisableStrongPassword",
+ passwordProfile: {
+ forceChangePasswordNextSignIn: false,
+ password: password,
+ },
+ identities: [
+ {
+ signinType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: `${this.tenantName}.onmicrosoft.com`,
+ issuerAssignedId: email,
+ },
+ ],
+ });
+ return { sub: newUser.id };
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ if (e?.statusCode === 400 && e?.body) {
+ const error = JSON.parse(e.body);
+
+ // エラーが競合エラーである場合は、メールアドレス重複としてエラーを返す
+ if (error?.details?.find((x) => x.code === "ObjectConflict")) {
+ return { reason: "email", message: "ObjectConflict" };
+ }
+ }
+
+ throw e;
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.createUser.name}`
+ );
+ }
+ }
+
+ /**
+ * Gets users
+ * @param externalIds
+ * @returns users
+ */
+ async getUsers(): Promise {
+ this.logger.log(`[IN] ${this.getUsers.name}`);
+
+ try {
+ const res: AdB2cResponse = await this.graphClient
+ .api(`users/`)
+ .select(["id", "displayName", "identities"])
+ .filter(`creationType eq 'LocalAccount'`)
+ .get();
+
+ return res.value;
+ } catch (e) {
+ this.logger.error(`error=${e}`);
+ const { statusCode } = e;
+ if (statusCode === 429) {
+ throw new Adb2cTooManyRequestsError();
+ }
+
+ throw e;
+ } finally {
+ this.logger.log(`[OUT] ${this.getUsers.name}`);
+ }
+ }
+
+ /**
+ * Azure AD B2Cからユーザ情報を削除する
+ * @param externalId 外部ユーザーID
+ * @param context コンテキスト
+ */
+ async deleteUser(externalId: string, context: Context): Promise {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.deleteUser.name
+ } | params: { externalId: ${externalId} };`
+ );
+
+ try {
+ // https://learn.microsoft.com/en-us/graph/api/user-delete?view=graph-rest-1.0&tabs=javascript#example
+ await this.graphClient.api(`users/${externalId}`).delete();
+ this.logger.log(
+ `[${context.getTrackingId()}] [ADB2C DELETE] externalId: ${externalId}`
+ );
+
+ // キャッシュからも削除する
+ // 移行ツール特別対応:キャッシュ登録は行わないので削除も不要
+ /*
+ try {
+ await this.redisService.del(context, makeADB2CKey(externalId));
+ } catch (e) {
+ // キャッシュからの削除に失敗しても、ADB2Cからの削除は成功しているため例外はスローしない
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ }
+ */
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ throw e;
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.deleteUser.name}`
+ );
+ }
+ }
+ /**
+ * Azure AD B2Cからユーザ情報を削除する(複数)
+ * @param externalIds 外部ユーザーID
+ */
+ async deleteUsers(externalIds: string[]): Promise {
+ this.logger.log(
+ `[IN]${this.deleteUsers.name} | params: { externalIds: ${externalIds} };`
+ );
+
+ try {
+ // 複数ユーザーを一括削除する方法がないため、1人ずつで削除を行う
+ const results = await Promise.allSettled(
+ externalIds.map(async (externalId) => {
+ await this.graphClient.api(`users/${externalId}`).delete();
+ await new Promise((resolve) => setTimeout(resolve, 15)); // 15ms待つ
+ this.logger.log(`[[ADB2C DELETE] externalId: ${externalId}`);
+ })
+ );
+
+ // 失敗したプロミスのエラーをログに記録
+ results.forEach((result, index) => {
+ // statusがrejectedでない場合は、エラーが発生していないためログに記録しない
+ if (result.status !== "rejected") {
+ return;
+ }
+
+ const failedId = externalIds[index];
+ if (isPromiseRejectedResult(result)) {
+ const error = result.reason.toString();
+
+ this.logger.error(`Failed to delete user ${failedId}: ${error}`);
+ } else {
+ this.logger.error(`Failed to delete user ${failedId}`);
+ }
+ });
+ } catch (e) {
+ this.logger.error(`error=${e}`);
+ throw e;
+ } finally {
+ this.logger.log(`[OUT] ${this.deleteUsers.name}`);
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/gateways/adb2c/types/types.ts b/data_migration_tools/server/src/gateways/adb2c/types/types.ts
new file mode 100644
index 0000000..a7261ef
--- /dev/null
+++ b/data_migration_tools/server/src/gateways/adb2c/types/types.ts
@@ -0,0 +1,15 @@
+export type AdB2cResponse = {
+ '@odata.context': string;
+ value: AdB2cUser[];
+};
+export type AdB2cUser = {
+ id: string;
+ displayName: string;
+ identities?: UserIdentity[];
+};
+
+export type UserIdentity = {
+ signInType: string;
+ issuer: string;
+ issuerAssignedId: string;
+};
diff --git a/data_migration_tools/server/src/gateways/adb2c/utils/utils.ts b/data_migration_tools/server/src/gateways/adb2c/utils/utils.ts
new file mode 100644
index 0000000..e1b640e
--- /dev/null
+++ b/data_migration_tools/server/src/gateways/adb2c/utils/utils.ts
@@ -0,0 +1,22 @@
+import { ADB2C_SIGN_IN_TYPE } from '../../../constants';
+import { AdB2cUser } from '../types/types';
+
+export const isPromiseRejectedResult = (
+ data: unknown,
+): data is PromiseRejectedResult => {
+ return (
+ data !== null &&
+ typeof data === 'object' &&
+ 'status' in data &&
+ 'reason' in data
+ );
+};
+
+// 生のAdB2cUserのレスポンスから表示名とメールアドレスを取得する
+export const getUserNameAndMailAddress = (user: AdB2cUser) => {
+ const { displayName, identities } = user;
+ const emailAddress = identities?.find(
+ (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ )?.issuerAssignedId;
+ return { displayName, emailAddress };
+};
diff --git a/data_migration_tools/server/src/gateways/blobstorage/blobstorage.module.ts b/data_migration_tools/server/src/gateways/blobstorage/blobstorage.module.ts
new file mode 100644
index 0000000..bc4f4a7
--- /dev/null
+++ b/data_migration_tools/server/src/gateways/blobstorage/blobstorage.module.ts
@@ -0,0 +1,10 @@
+import { Module } from '@nestjs/common';
+import { BlobstorageService } from './blobstorage.service';
+import { ConfigModule } from '@nestjs/config';
+
+@Module({
+ exports: [BlobstorageService],
+ imports: [ConfigModule],
+ providers: [BlobstorageService],
+})
+export class BlobstorageModule {}
diff --git a/data_migration_tools/server/src/gateways/blobstorage/blobstorage.service.ts b/data_migration_tools/server/src/gateways/blobstorage/blobstorage.service.ts
new file mode 100644
index 0000000..7c26388
--- /dev/null
+++ b/data_migration_tools/server/src/gateways/blobstorage/blobstorage.service.ts
@@ -0,0 +1,155 @@
+import { Injectable, Logger } from "@nestjs/common";
+import {
+ ContainerClient,
+ BlobServiceClient,
+ StorageSharedKeyCredential,
+} from "@azure/storage-blob";
+import {
+ BLOB_STORAGE_REGION_AU,
+ BLOB_STORAGE_REGION_EU,
+ BLOB_STORAGE_REGION_US,
+} from "../../constants";
+import { ConfigService } from "@nestjs/config";
+import { Context } from "../../common/log";
+@Injectable()
+export class BlobstorageService {
+ private readonly logger = new Logger(BlobstorageService.name);
+ private readonly blobServiceClientUS: BlobServiceClient;
+ private readonly blobServiceClientEU: BlobServiceClient;
+ private readonly blobServiceClientAU: BlobServiceClient;
+ private readonly sharedKeyCredentialUS: StorageSharedKeyCredential;
+ private readonly sharedKeyCredentialAU: StorageSharedKeyCredential;
+ private readonly sharedKeyCredentialEU: StorageSharedKeyCredential;
+ constructor(private readonly configService: ConfigService) {
+ this.sharedKeyCredentialUS = new StorageSharedKeyCredential(
+ this.configService.getOrThrow("STORAGE_ACCOUNT_NAME_US"),
+ this.configService.getOrThrow("STORAGE_ACCOUNT_KEY_US")
+ );
+ this.sharedKeyCredentialAU = new StorageSharedKeyCredential(
+ this.configService.getOrThrow("STORAGE_ACCOUNT_NAME_AU"),
+ this.configService.getOrThrow("STORAGE_ACCOUNT_KEY_AU")
+ );
+ this.sharedKeyCredentialEU = new StorageSharedKeyCredential(
+ this.configService.getOrThrow("STORAGE_ACCOUNT_NAME_EU"),
+ this.configService.getOrThrow("STORAGE_ACCOUNT_KEY_EU")
+ );
+ this.blobServiceClientUS = new BlobServiceClient(
+ this.configService.getOrThrow("STORAGE_ACCOUNT_ENDPOINT_US"),
+ this.sharedKeyCredentialUS
+ );
+ this.blobServiceClientAU = new BlobServiceClient(
+ this.configService.getOrThrow("STORAGE_ACCOUNT_ENDPOINT_AU"),
+ this.sharedKeyCredentialAU
+ );
+ this.blobServiceClientEU = new BlobServiceClient(
+ this.configService.getOrThrow("STORAGE_ACCOUNT_ENDPOINT_EU"),
+ this.sharedKeyCredentialEU
+ );
+ }
+
+ /**
+ * Creates container
+ * @param context
+ * @param accountId
+ * @param country
+ * @returns container
+ */
+ async createContainer(
+ context: Context,
+ accountId: number,
+ country: string
+ ): Promise {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.createContainer.name
+ } | params: { ` + `accountId: ${accountId} };`
+ );
+
+ // 国に応じたリージョンでコンテナ名を指定してClientを取得
+ const containerClient = this.getContainerClient(
+ context,
+ accountId,
+ country
+ );
+
+ try {
+ // コンテナ作成
+ await containerClient.create();
+ } catch (e) {
+ this.logger.error(`[${context.getTrackingId()}] error=${e}`);
+ throw e;
+ } finally {
+ this.logger.log(
+ `[OUT] [${context.getTrackingId()}] ${this.createContainer.name}`
+ );
+ }
+ }
+
+ /**
+ * すべてのコンテナを削除します。
+ * @returns containers
+ */
+ async deleteContainers(): Promise {
+ this.logger.log(`[IN] ${this.deleteContainers.name}`);
+
+ try {
+ for await (const container of this.blobServiceClientAU.listContainers({
+ prefix: "account-",
+ })) {
+ const client = this.blobServiceClientAU.getContainerClient(
+ container.name
+ );
+ await client.deleteIfExists();
+ }
+ for await (const container of this.blobServiceClientEU.listContainers({
+ prefix: "account-",
+ })) {
+ const client = this.blobServiceClientEU.getContainerClient(
+ container.name
+ );
+ await client.deleteIfExists();
+ }
+ for await (const container of this.blobServiceClientUS.listContainers({
+ prefix: "account-",
+ })) {
+ const client = this.blobServiceClientUS.getContainerClient(
+ container.name
+ );
+ await client.deleteIfExists();
+ }
+ } catch (e) {
+ this.logger.error(`error=${e}`);
+ throw e;
+ } finally {
+ this.logger.log(`[OUT] ${this.deleteContainers.name}`);
+ }
+ }
+
+ /**
+ * Gets container client
+ * @param companyName
+ * @returns container client
+ */
+ private getContainerClient(
+ context: Context,
+ accountId: number,
+ country: string
+ ): ContainerClient {
+ this.logger.log(
+ `[IN] [${context.getTrackingId()}] ${
+ this.getContainerClient.name
+ } | params: { ` + `accountId: ${accountId} };`
+ );
+
+ const containerName = `account-${accountId}`;
+ if (BLOB_STORAGE_REGION_US.includes(country)) {
+ return this.blobServiceClientUS.getContainerClient(containerName);
+ } else if (BLOB_STORAGE_REGION_AU.includes(country)) {
+ return this.blobServiceClientAU.getContainerClient(containerName);
+ } else if (BLOB_STORAGE_REGION_EU.includes(country)) {
+ return this.blobServiceClientEU.getContainerClient(containerName);
+ } else {
+ throw new Error("invalid country");
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/main.ts b/data_migration_tools/server/src/main.ts
index 39d5e39..218ab27 100644
--- a/data_migration_tools/server/src/main.ts
+++ b/data_migration_tools/server/src/main.ts
@@ -1,35 +1,35 @@
-import { NestFactory } from "@nestjs/core";
-import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
-import { AppModule } from "./app.module";
-import { ValidationPipe } from "@nestjs/common";
-import { LoggerMiddleware } from "./common/loggerMiddleware";
-import cookieParser from "cookie-parser";
+import { NestFactory } from '@nestjs/core';
+import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
+import { AppModule } from './app.module';
+import { ValidationPipe } from '@nestjs/common';
+import { LoggerMiddleware } from './common/loggerMiddleware';
+import cookieParser from 'cookie-parser';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
- cors: process.env.CORS === "TRUE",
+ cors: process.env.CORS === 'TRUE',
});
app.use(new LoggerMiddleware(), cookieParser());
// バリデーター(+型の自動変換機能)を適用
app.useGlobalPipes(
- new ValidationPipe({ transform: true, forbidUnknownValues: false })
+ new ValidationPipe({ transform: true, forbidUnknownValues: false }),
);
- if (process.env.STAGE === "local") {
+ if (process.env.STAGE === 'local') {
const options = new DocumentBuilder()
- .setTitle("data_migration_toolsOpenAPI")
- .setVersion("1.0.0")
+ .setTitle('data_migration_toolsOpenAPI')
+ .setVersion('1.0.0')
.addBearerAuth({
- type: "http",
- scheme: "bearer",
- bearerFormat: "JWT",
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT',
})
.build();
const document = SwaggerModule.createDocument(app, options);
- SwaggerModule.setup("api", app, document);
+ SwaggerModule.setup('api', app, document);
}
- await app.listen(process.env.PORT || 8180);
+ await app.listen(process.env.PORT || 8280);
}
bootstrap();
diff --git a/data_migration_tools/server/src/repositories/accounts/accounts.repository.module.ts b/data_migration_tools/server/src/repositories/accounts/accounts.repository.module.ts
new file mode 100644
index 0000000..ddd0efd
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/accounts/accounts.repository.module.ts
@@ -0,0 +1,11 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { Account } from './entity/account.entity';
+import { AccountsRepositoryService } from './accounts.repository.service';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([Account])],
+ providers: [AccountsRepositoryService],
+ exports: [AccountsRepositoryService],
+})
+export class AccountsRepositoryModule {}
diff --git a/data_migration_tools/server/src/repositories/accounts/accounts.repository.service.ts b/data_migration_tools/server/src/repositories/accounts/accounts.repository.service.ts
new file mode 100644
index 0000000..ec24808
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/accounts/accounts.repository.service.ts
@@ -0,0 +1,164 @@
+import { Injectable } from '@nestjs/common';
+import {
+ DataSource,
+} from 'typeorm';
+import { User } from '../users/entity/user.entity';
+import { Account } from './entity/account.entity';
+import {
+ getDirection,
+ getTaskListSortableAttribute,
+} from '../../common/types/sort/util';
+import { SortCriteria } from "../sort_criteria/entity/sort_criteria.entity";
+import {
+ insertEntity,
+ updateEntity,
+ deleteEntity,
+} from '../../common/repository';
+import { Context } from '../../common/log';
+
+@Injectable()
+export class AccountsRepositoryService {
+ // クエリログにコメントを出力するかどうか
+ private readonly isCommentOut = process.env.STAGE !== "local";
+ constructor(private dataSource: DataSource) {}
+
+ /**
+ * プライマリ管理者とアカウント、ソート条件を同時に作成する
+ * @param companyName
+ * @param country
+ * @param dealerAccountId
+ * @param tier
+ * @param adminExternalUserId
+ * @param adminUserRole
+ * @param accountId
+ * @param userId
+ * @param adminUserAcceptedEulaVersion
+ * @param adminUserAcceptedPrivacyNoticeVersion
+ * @param adminUserAcceptedDpaVersion
+ * @returns account/admin user
+ */
+ async createAccount(
+ context: Context,
+ companyName: string,
+ country: string,
+ dealerAccountId: number | undefined,
+ tier: number,
+ adminExternalUserId: string,
+ adminUserRole: string,
+ accountId: number,
+ userId: number,
+ adminUserAcceptedEulaVersion?: string,
+ adminUserAcceptedPrivacyNoticeVersion?: string,
+ adminUserAcceptedDpaVersion?: string
+ ): Promise<{ newAccount: Account; adminUser: User }> {
+ return await this.dataSource.transaction(async (entityManager) => {
+ const account = new Account();
+ {
+ account.id = accountId;
+ account.parent_account_id = dealerAccountId ?? null;
+ account.company_name = companyName;
+ account.country = country;
+ account.tier = tier;
+ }
+ const accountsRepo = entityManager.getRepository(Account);
+ const newAccount = accountsRepo.create(account);
+ const persistedAccount = await insertEntity(
+ Account,
+ accountsRepo,
+ newAccount,
+ this.isCommentOut,
+ context
+ );
+
+ // 作成されたAccountのIDを使用してユーザーを作成
+ const user = new User();
+ {
+ user.id = userId;
+ user.account_id = persistedAccount.id;
+ user.external_id = adminExternalUserId;
+ user.role = adminUserRole;
+ user.accepted_eula_version = adminUserAcceptedEulaVersion ?? null;
+ user.accepted_privacy_notice_version =
+ adminUserAcceptedPrivacyNoticeVersion ?? null;
+ user.accepted_dpa_version = adminUserAcceptedDpaVersion ?? null;
+ }
+ const usersRepo = entityManager.getRepository(User);
+ const newUser = usersRepo.create(user);
+ const persistedUser = await insertEntity(
+ User,
+ usersRepo,
+ newUser,
+ this.isCommentOut,
+ context
+ );
+
+ // アカウントに管理者を設定して更新
+ persistedAccount.primary_admin_user_id = persistedUser.id;
+
+ const result = await updateEntity(
+ accountsRepo,
+ { id: persistedAccount.id },
+ persistedAccount,
+ this.isCommentOut,
+ context
+ );
+
+ // 想定外の更新が行われた場合はロールバックを行った上でエラー送出
+ if (result.affected !== 1) {
+ throw new Error(`invalid update. result.affected=${result.affected}`);
+ }
+
+ // ユーザーのタスクソート条件を作成
+ const sortCriteria = new SortCriteria();
+ {
+ sortCriteria.parameter = getTaskListSortableAttribute("JOB_NUMBER");
+ sortCriteria.direction = getDirection("ASC");
+ sortCriteria.user_id = persistedUser.id;
+ }
+ const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
+ const newSortCriteria = sortCriteriaRepo.create(sortCriteria);
+ await insertEntity(
+ SortCriteria,
+ sortCriteriaRepo,
+ newSortCriteria,
+ this.isCommentOut,
+ context
+ );
+
+ return { newAccount: persistedAccount, adminUser: persistedUser };
+ });
+ }
+
+ /**
+ * プライマリ管理者とアカウント、ソート条件を同時に削除する
+ * @param accountId
+ * @returns delete
+ */
+ async deleteAccount(
+ context: Context,
+ accountId: number,
+ userId: number
+ ): Promise {
+ await this.dataSource.transaction(async (entityManager) => {
+ const accountsRepo = entityManager.getRepository(Account);
+ const usersRepo = entityManager.getRepository(User);
+ const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
+ // ソート条件を削除
+ await deleteEntity(
+ sortCriteriaRepo,
+ { user_id: userId },
+ this.isCommentOut,
+ context
+ );
+ // プライマリ管理者を削除
+ await deleteEntity(usersRepo, { id: userId }, this.isCommentOut, context);
+ // アカウントを削除
+ await deleteEntity(
+ accountsRepo,
+ { id: accountId },
+ this.isCommentOut,
+ context
+ );
+ });
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/accounts/entity/account.entity.ts b/data_migration_tools/server/src/repositories/accounts/entity/account.entity.ts
new file mode 100644
index 0000000..3c40a03
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/accounts/entity/account.entity.ts
@@ -0,0 +1,70 @@
+import { bigintTransformer } from '../../../common/entity';
+import { User } from '../../../repositories/users/entity/user.entity';
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ OneToMany,
+} from 'typeorm';
+
+@Entity({ name: 'accounts' })
+export class Account {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ parent_account_id: number | null;
+
+ @Column()
+ tier: number;
+
+ @Column()
+ country: string;
+
+ @Column({ default: false })
+ delegation_permission: boolean;
+
+ @Column({ default: false })
+ locked: boolean;
+
+ @Column()
+ company_name: string;
+
+ @Column({ default: false })
+ verified: boolean;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ primary_admin_user_id: number | null;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ secondary_admin_user_id: number | null;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ active_worktype_id: number | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @OneToMany(() => User, (user) => user.id)
+ user: User[] | null;
+}
diff --git a/data_migration_tools/server/src/repositories/accounts/errors/types.ts b/data_migration_tools/server/src/repositories/accounts/errors/types.ts
new file mode 100644
index 0000000..826700c
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/accounts/errors/types.ts
@@ -0,0 +1,28 @@
+// アカウント未発見エラー
+export class AccountNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AccountNotFoundError';
+ }
+}
+// ディーラーアカウント未存在エラー
+export class DealerAccountNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'DealerAccountNotFoundError';
+ }
+}
+// 管理者ユーザ未存在エラー
+export class AdminUserNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AdminUserNotFoundError';
+ }
+}
+// アカウントロックエラー
+export class AccountLockedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AccountLockedError';
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/delete/delete.repository.module.ts b/data_migration_tools/server/src/repositories/delete/delete.repository.module.ts
new file mode 100644
index 0000000..e1797fc
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/delete.repository.module.ts
@@ -0,0 +1,60 @@
+import { Module } from "@nestjs/common";
+import { TypeOrmModule } from "@nestjs/typeorm";
+import { DeleteRepositoryService } from "./delete.repository.service";
+import { Account } from "./entity/account.entity";
+import { AudioFile } from "./entity/audio_file.entity";
+import { AudioOptionItem } from "./entity/audio_option_item.entity";
+import { CheckoutPermission } from "./entity/checkout_permission.entity";
+import {
+ CardLicense,
+ CardLicenseIssue,
+ License,
+ LicenseAllocationHistory,
+ LicenseAllocationHistoryArchive,
+ LicenseArchive,
+ LicenseOrder,
+} from "./entity/license.entity";
+import { OptionItem } from "./entity/option_item.entity";
+import { SortCriteria } from "./entity/sort_criteria.entity";
+import { Task } from "./entity/task.entity";
+import { TemplateFile } from "./entity/template_file.entity";
+import { Term } from "./entity/term.entity";
+import { UserGroupMember } from "./entity/user_group_member.entity";
+import { UserGroup } from "./entity/user_group.entity";
+import { User, UserArchive } from "./entity/user.entity";
+import { WorkflowTypist } from "./entity/workflow_typists.entity";
+import { Workflow } from "./entity/workflow.entity";
+import { Worktype } from "./entity/worktype.entity";
+
+@Module({
+ imports: [
+ TypeOrmModule.forFeature([
+ Account,
+ AudioFile,
+ AudioOptionItem,
+ CheckoutPermission,
+ License,
+ LicenseOrder,
+ CardLicense,
+ CardLicenseIssue,
+ LicenseArchive,
+ LicenseAllocationHistory,
+ LicenseAllocationHistoryArchive,
+ OptionItem,
+ SortCriteria,
+ Task,
+ TemplateFile,
+ Term,
+ UserGroupMember,
+ UserGroup,
+ User,
+ UserArchive,
+ WorkflowTypist,
+ Workflow,
+ Worktype,
+ ]),
+ ],
+ providers: [DeleteRepositoryService],
+ exports: [DeleteRepositoryService],
+})
+export class DeleteRepositoryModule {}
diff --git a/data_migration_tools/server/src/repositories/delete/delete.repository.service.ts b/data_migration_tools/server/src/repositories/delete/delete.repository.service.ts
new file mode 100644
index 0000000..52539c0
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/delete.repository.service.ts
@@ -0,0 +1,57 @@
+import { Injectable } from "@nestjs/common";
+import { DataSource } from "typeorm";
+import { logger } from "@azure/identity";
+import { Account } from "./entity/account.entity";
+import { AUTO_INCREMENT_START } from "../../constants";
+
+@Injectable()
+export class DeleteRepositoryService {
+ constructor(private dataSource: DataSource) {}
+
+ /**
+ * 全テーブルをTrancateする
+ * @returns data
+ */
+ async deleteData(): Promise {
+ const entities = this.dataSource.entityMetadatas;
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ try {
+ await queryRunner.startTransaction();
+ await queryRunner.query("SET FOREIGN_KEY_CHECKS=0");
+ for (const entity of entities) {
+ await queryRunner.query(`TRUNCATE TABLE \`${entity.tableName}\``);
+ }
+ await queryRunner.query("SET FOREIGN_KEY_CHECKS=1");
+ await queryRunner.commitTransaction();
+ } catch (err) {
+ await queryRunner.rollbackTransaction();
+ logger.error(err);
+ throw err;
+ } finally {
+ await queryRunner.release();
+ }
+ }
+
+ /**
+ * AutoIncrementの値をリセットする
+ * @returns data
+ */
+ async resetAutoIncrement(): Promise {
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ try {
+ await queryRunner.startTransaction();
+ await queryRunner.query(
+ `ALTER TABLE accounts AUTO_INCREMENT = ${AUTO_INCREMENT_START}`
+ );
+ await queryRunner.commitTransaction();
+ } catch (err) {
+ await queryRunner.rollbackTransaction();
+ logger.error(err);
+ throw err;
+ } finally {
+ await queryRunner.release();
+ }
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/account.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/account.entity.ts
new file mode 100644
index 0000000..de92919
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/account.entity.ts
@@ -0,0 +1,70 @@
+import { bigintTransformer } from "../../../common/entity";
+import { User } from "./user.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ OneToMany,
+} from "typeorm";
+
+@Entity({ name: "accounts" })
+export class Account {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ parent_account_id: number | null;
+
+ @Column()
+ tier: number;
+
+ @Column()
+ country: string;
+
+ @Column({ default: false })
+ delegation_permission: boolean;
+
+ @Column({ default: false })
+ locked: boolean;
+
+ @Column()
+ company_name: string;
+
+ @Column({ default: false })
+ verified: boolean;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ primary_admin_user_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ secondary_admin_user_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ active_worktype_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @OneToMany(() => User, (user) => user.id)
+ user: User[] | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/audio_file.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/audio_file.entity.ts
new file mode 100644
index 0000000..e606af1
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/audio_file.entity.ts
@@ -0,0 +1,43 @@
+import { Task } from "./task.entity";
+import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm";
+
+@Entity({ name: "audio_files" })
+export class AudioFile {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ account_id: number;
+ @Column()
+ owner_user_id: number;
+ @Column()
+ url: string;
+ @Column()
+ file_name: string;
+ @Column()
+ author_id: string;
+ @Column()
+ work_type_id: string;
+ @Column()
+ started_at: Date;
+ @Column({ type: "time" })
+ duration: string;
+ @Column()
+ finished_at: Date;
+ @Column()
+ uploaded_at: Date;
+ @Column()
+ file_size: number;
+ @Column()
+ priority: string;
+ @Column()
+ audio_format: string;
+ @Column({ nullable: true, type: "varchar" })
+ comment: string | null;
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+ @Column()
+ is_encrypted: boolean;
+ @OneToOne(() => Task, (task) => task.file)
+ task: Task | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/audio_option_item.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/audio_option_item.entity.ts
new file mode 100644
index 0000000..51e65ff
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/audio_option_item.entity.ts
@@ -0,0 +1,23 @@
+import { Task } from "./task.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ ManyToOne,
+ JoinColumn,
+} from "typeorm";
+
+@Entity({ name: "audio_option_items" })
+export class AudioOptionItem {
+ @PrimaryGeneratedColumn()
+ id: number;
+ @Column()
+ audio_file_id: number;
+ @Column()
+ label: string;
+ @Column()
+ value: string;
+ @ManyToOne(() => Task, (task) => task.audio_file_id)
+ @JoinColumn({ name: "audio_file_id", referencedColumnName: "audio_file_id" })
+ task: Task | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/checkout_permission.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/checkout_permission.entity.ts
new file mode 100644
index 0000000..ecd0781
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/checkout_permission.entity.ts
@@ -0,0 +1,38 @@
+import { bigintTransformer } from "../../../common/entity";
+import { Task } from "./task.entity";
+import { UserGroup } from "./user_group.entity";
+import { User } from "./user.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ JoinColumn,
+ ManyToOne,
+} from "typeorm";
+
+@Entity({ name: "checkout_permission" })
+export class CheckoutPermission {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column({})
+ task_id: number;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ user_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ user_group_id: number | null;
+
+ @ManyToOne(() => User, (user) => user.id)
+ @JoinColumn({ name: "user_id" })
+ user: User | null;
+
+ @ManyToOne(() => UserGroup, (group) => group.id)
+ @JoinColumn({ name: "user_group_id" })
+ user_group: UserGroup | null;
+
+ @ManyToOne(() => Task, (task) => task.id)
+ @JoinColumn({ name: "task_id" })
+ task: Task | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/license.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/license.entity.ts
new file mode 100644
index 0000000..2ce6edf
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/license.entity.ts
@@ -0,0 +1,322 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ OneToOne,
+ JoinColumn,
+ ManyToOne,
+ PrimaryColumn,
+} from "typeorm";
+import { User } from "./user.entity";
+import { bigintTransformer } from "../../../common/entity";
+
+@Entity({ name: "license_orders" })
+export class LicenseOrder {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ po_number: string;
+
+ @Column()
+ from_account_id: number;
+
+ @Column()
+ to_account_id: number;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ ordered_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ issued_at: Date | null;
+
+ @Column()
+ quantity: number;
+
+ @Column()
+ status: string;
+
+ @Column({ nullable: true, type: "datetime" })
+ canceled_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ updated_at: Date;
+}
+
+@Entity({ name: "licenses" })
+export class License {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column({ nullable: true, type: "datetime" })
+ expiry_date: Date | null;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ type: string;
+
+ @Column()
+ status: string;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ allocated_user_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ order_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ delete_order_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ updated_at: Date;
+
+ @OneToOne(() => User, (user) => user.license, {
+ createForeignKeyConstraints: false,
+ }) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
+ @JoinColumn({ name: "allocated_user_id" })
+ user: User | null;
+}
+
+@Entity({ name: "card_license_issue" })
+export class CardLicenseIssue {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ issued_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ updated_at: Date;
+}
+
+@Entity({ name: "card_licenses" })
+export class CardLicense {
+ @PrimaryGeneratedColumn()
+ license_id: number;
+
+ @Column()
+ issue_id: number;
+
+ @Column()
+ card_license_key: string;
+
+ @Column({ nullable: true, type: "datetime" })
+ activated_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ updated_at: Date;
+}
+
+@Entity({ name: "license_allocation_history" })
+export class LicenseAllocationHistory {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ user_id: number;
+
+ @Column()
+ license_id: number;
+
+ @Column()
+ is_allocated: boolean;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ executed_at: Date;
+
+ @Column()
+ switch_from_type: string;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ updated_at: Date;
+
+ @ManyToOne(() => License, (licenses) => licenses.id, {
+ createForeignKeyConstraints: false,
+ }) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
+ @JoinColumn({ name: "license_id" })
+ license: License | null;
+}
+
+@Entity({ name: "licenses_archive" })
+export class LicenseArchive {
+ @PrimaryColumn()
+ id: number;
+
+ @Column({ nullable: true, type: "datetime" })
+ expiry_date: Date | null;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ type: string;
+
+ @Column()
+ status: string;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ allocated_user_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ order_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ delete_order_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @Column()
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @Column()
+ updated_at: Date;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ archived_at: Date;
+}
+
+@Entity({ name: "license_allocation_history_archive" })
+export class LicenseAllocationHistoryArchive {
+ @PrimaryColumn()
+ id: number;
+
+ @Column()
+ user_id: number;
+
+ @Column()
+ license_id: number;
+
+ @Column()
+ is_allocated: boolean;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ executed_at: Date;
+
+ @Column()
+ switch_from_type: string;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @Column()
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @Column()
+ updated_at: Date;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ archived_at: Date;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/option_item.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/option_item.entity.ts
new file mode 100644
index 0000000..f9e7ac4
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/option_item.entity.ts
@@ -0,0 +1,42 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ UpdateDateColumn,
+ CreateDateColumn,
+ ManyToOne,
+ JoinColumn,
+} from 'typeorm';
+import { Worktype } from './worktype.entity';
+
+@Entity({ name: 'option_items' })
+export class OptionItem {
+ @PrimaryGeneratedColumn()
+ id: number;
+ @Column()
+ worktype_id: number;
+ @Column()
+ item_label: string;
+ @Column()
+ default_value_type: string;
+ @Column()
+ initial_value: string;
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date | null;
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date | null;
+
+ @ManyToOne(() => Worktype, (worktype) => worktype.id)
+ @JoinColumn({ name: 'worktype_id' })
+ worktype: Worktype;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/sort_criteria.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/sort_criteria.entity.ts
new file mode 100644
index 0000000..260c9a9
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/sort_criteria.entity.ts
@@ -0,0 +1,16 @@
+import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
+
+@Entity({ name: 'sort_criteria' })
+export class SortCriteria {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ user_id: number;
+
+ @Column()
+ parameter: string;
+
+ @Column()
+ direction: string;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/task.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/task.entity.ts
new file mode 100644
index 0000000..1daca02
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/task.entity.ts
@@ -0,0 +1,71 @@
+import { AudioOptionItem } from "./audio_option_item.entity";
+import { AudioFile } from "./audio_file.entity";
+import { User } from "./user.entity";
+import { TemplateFile } from "./template_file.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ OneToOne,
+ JoinColumn,
+ OneToMany,
+ ManyToOne,
+ CreateDateColumn,
+ UpdateDateColumn,
+} from "typeorm";
+import { bigintTransformer } from "../../../common/entity";
+
+@Entity({ name: "tasks" })
+export class Task {
+ @PrimaryGeneratedColumn()
+ id: number;
+ @Column()
+ job_number: string;
+ @Column()
+ account_id: number;
+ @Column({ nullable: true, type: "boolean" })
+ is_job_number_enabled: boolean | null;
+ @Column()
+ audio_file_id: number;
+ @Column()
+ status: string;
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ typist_user_id: number | null;
+ @Column()
+ priority: string;
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ template_file_id: number | null;
+ @Column({ nullable: true, type: "datetime" })
+ started_at: Date | null;
+ @Column({ nullable: true, type: "datetime" })
+ finished_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+ @OneToOne(() => AudioFile, (audiofile) => audiofile.task)
+ @JoinColumn({ name: "audio_file_id" })
+ file: AudioFile | null;
+ @OneToMany(() => AudioOptionItem, (option) => option.task)
+ option_items: AudioOptionItem[] | null;
+ @OneToOne(() => User, (user) => user.id)
+ @JoinColumn({ name: "typist_user_id" })
+ typist_user: User | null;
+ @ManyToOne(() => TemplateFile, (templateFile) => templateFile.id)
+ @JoinColumn({ name: "template_file_id" })
+ template_file: TemplateFile | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/template_file.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/template_file.entity.ts
new file mode 100644
index 0000000..79c14f5
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/template_file.entity.ts
@@ -0,0 +1,31 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ OneToMany,
+} from "typeorm";
+import { Task } from "./task.entity";
+
+@Entity({ name: "template_files" })
+export class TemplateFile {
+ @PrimaryGeneratedColumn()
+ id: number;
+ @Column()
+ account_id: number;
+ @Column()
+ url: string;
+ @Column()
+ file_name: string;
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+ @CreateDateColumn()
+ created_at: Date;
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+ @UpdateDateColumn()
+ updated_at: Date;
+ @OneToMany(() => Task, (task) => task.template_file)
+ tasks: Task[] | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/term.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/term.entity.ts
new file mode 100644
index 0000000..7a2097b
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/term.entity.ts
@@ -0,0 +1,37 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+} from 'typeorm';
+
+@Entity({ name: 'terms' })
+export class Term {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ document_type: string;
+
+ @Column()
+ version: string;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: 'varchar' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/user.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/user.entity.ts
new file mode 100644
index 0000000..f7fa3d7
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/user.entity.ts
@@ -0,0 +1,170 @@
+import { Account } from "./account.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ ManyToOne,
+ JoinColumn,
+ OneToOne,
+ OneToMany,
+ PrimaryColumn,
+} from "typeorm";
+import { License } from "./license.entity";
+import { UserGroupMember } from "./user_group_member.entity";
+
+@Entity({ name: "users" })
+export class User {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ external_id: string;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ role: string;
+
+ @Column({ nullable: true, type: "varchar" })
+ author_id: string | null;
+
+ @Column({ nullable: true, type: "varchar" })
+ accepted_eula_version: string | null;
+
+ @Column({ nullable: true, type: "varchar" })
+ accepted_privacy_notice_version: string | null;
+
+ @Column({ nullable: true, type: "varchar" })
+ accepted_dpa_version: string | null;
+
+ @Column({ default: false })
+ email_verified: boolean;
+
+ @Column({ default: true })
+ auto_renew: boolean;
+
+ @Column({ default: true })
+ notification: boolean;
+
+ @Column({ default: false })
+ encryption: boolean;
+
+ @Column({ nullable: true, type: "varchar" })
+ encryption_password: string | null;
+
+ @Column({ default: false })
+ prompt: boolean;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @ManyToOne(() => Account, (account) => account.user, {
+ createForeignKeyConstraints: false,
+ }) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
+ @JoinColumn({ name: "account_id" })
+ account: Account | null;
+
+ @OneToOne(() => License, (license) => license.user)
+ license: License | null;
+
+ @OneToMany(() => UserGroupMember, (userGroupMember) => userGroupMember.user)
+ userGroupMembers: UserGroupMember[] | null;
+}
+
+@Entity({ name: "users_archive" })
+export class UserArchive {
+ @PrimaryColumn()
+ id: number;
+
+ @Column()
+ external_id: string;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ role: string;
+
+ @Column({ nullable: true, type: "varchar" })
+ author_id: string | null;
+
+ @Column({ nullable: true, type: "varchar" })
+ accepted_eula_version: string | null;
+
+ @Column({ nullable: true, type: "varchar" })
+ accepted_privacy_notice_version: string | null;
+
+ @Column({ nullable: true, type: "varchar" })
+ accepted_dpa_version: string | null;
+
+ @Column()
+ email_verified: boolean;
+
+ @Column()
+ auto_renew: boolean;
+
+ @Column()
+ notification: boolean;
+
+ @Column()
+ encryption: boolean;
+
+ @Column()
+ prompt: boolean;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @Column()
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @Column()
+ updated_at: Date;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ archived_at: Date;
+}
+
+export type newUser = Omit<
+ User,
+ | "id"
+ | "deleted_at"
+ | "created_at"
+ | "updated_at"
+ | "updated_by"
+ | "created_by"
+ | "account"
+ | "license"
+ | "userGroupMembers"
+ | "email_verified"
+>;
diff --git a/data_migration_tools/server/src/repositories/delete/entity/user_group.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/user_group.entity.ts
new file mode 100644
index 0000000..2a1fbce
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/user_group.entity.ts
@@ -0,0 +1,48 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ OneToMany,
+ CreateDateColumn,
+ UpdateDateColumn,
+} from 'typeorm';
+import { UserGroupMember } from './user_group_member.entity';
+
+@Entity({ name: 'user_group' })
+export class UserGroup {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ name: string;
+
+ @Column({ nullable: true, type: 'datetime' })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date | null;
+
+ @OneToMany(
+ () => UserGroupMember,
+ (userGroupMember) => userGroupMember.userGroup,
+ )
+ userGroupMembers: UserGroupMember[] | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/user_group_member.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/user_group_member.entity.ts
new file mode 100644
index 0000000..1afa807
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/user_group_member.entity.ts
@@ -0,0 +1,52 @@
+import { User } from "./user.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ JoinColumn,
+ ManyToOne,
+ CreateDateColumn,
+ UpdateDateColumn,
+} from "typeorm";
+import { UserGroup } from "./user_group.entity";
+
+@Entity({ name: "user_group_member" })
+export class UserGroupMember {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ user_group_id: number;
+
+ @Column()
+ user_id: number;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date | null;
+
+ @ManyToOne(() => User, (user) => user.id)
+ @JoinColumn({ name: "user_id" })
+ user: User | null;
+
+ @ManyToOne(() => UserGroup, (userGroup) => userGroup.id)
+ @JoinColumn({ name: "user_group_id" })
+ userGroup: UserGroup | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/workflow.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/workflow.entity.ts
new file mode 100644
index 0000000..83cdaae
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/workflow.entity.ts
@@ -0,0 +1,66 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ OneToMany,
+ JoinColumn,
+ ManyToOne,
+} from "typeorm";
+import { WorkflowTypist } from "./workflow_typists.entity";
+import { Worktype } from "./worktype.entity";
+import { TemplateFile } from "./template_file.entity";
+import { User } from "./user.entity";
+import { bigintTransformer } from "../../../common/entity";
+
+@Entity({ name: "workflows" })
+export class Workflow {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ author_id: number;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ worktype_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ template_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @ManyToOne(() => User, (user) => user.id)
+ @JoinColumn({ name: "author_id" })
+ author: User | null;
+
+ @ManyToOne(() => Worktype, (worktype) => worktype.id)
+ @JoinColumn({ name: "worktype_id" })
+ worktype: Worktype | null;
+
+ @ManyToOne(() => TemplateFile, (templateFile) => templateFile.id)
+ @JoinColumn({ name: "template_id" })
+ template: TemplateFile | null;
+
+ @OneToMany(() => WorkflowTypist, (workflowTypist) => workflowTypist.workflow)
+ workflowTypists: WorkflowTypist[] | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/workflow_typists.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/workflow_typists.entity.ts
new file mode 100644
index 0000000..e404ba0
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/workflow_typists.entity.ts
@@ -0,0 +1,58 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ ManyToOne,
+ JoinColumn,
+} from "typeorm";
+import { Workflow } from "./workflow.entity";
+import { User } from "./user.entity";
+import { UserGroup } from "./user_group.entity";
+import { bigintTransformer } from "../../../common/entity";
+
+@Entity({ name: "workflow_typists" })
+export class WorkflowTypist {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ workflow_id: number;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ typist_id: number | null;
+
+ @Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
+ typist_group_id: number | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @ManyToOne(() => Workflow, (workflow) => workflow.id)
+ @JoinColumn({ name: "workflow_id" })
+ workflow: Workflow | null;
+
+ @ManyToOne(() => User, (user) => user.id)
+ @JoinColumn({ name: "typist_id" })
+ typist: User | null;
+
+ @ManyToOne(() => UserGroup, (userGroup) => userGroup.id)
+ @JoinColumn({ name: "typist_group_id" })
+ typistGroup: UserGroup | null;
+}
diff --git a/data_migration_tools/server/src/repositories/delete/entity/worktype.entity.ts b/data_migration_tools/server/src/repositories/delete/entity/worktype.entity.ts
new file mode 100644
index 0000000..d444a00
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/delete/entity/worktype.entity.ts
@@ -0,0 +1,49 @@
+import { Account } from "./account.entity";
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ OneToMany,
+} from "typeorm";
+import { OptionItem } from "./option_item.entity";
+
+@Entity({ name: "worktypes" })
+export class Worktype {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ custom_worktype_id: string;
+
+ @Column({ nullable: true, type: "varchar" })
+ description: string | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @OneToMany(() => OptionItem, (optionItem) => optionItem.worktype)
+ option_items: OptionItem[];
+}
diff --git a/data_migration_tools/server/src/repositories/delete/errors/types.ts b/data_migration_tools/server/src/repositories/delete/errors/types.ts
new file mode 100644
index 0000000..e69de29
diff --git a/data_migration_tools/server/src/repositories/licenses/entity/license.entity.ts b/data_migration_tools/server/src/repositories/licenses/entity/license.entity.ts
new file mode 100644
index 0000000..90715ae
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/licenses/entity/license.entity.ts
@@ -0,0 +1,137 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+} from 'typeorm';
+import { bigintTransformer } from '../../../common/entity';
+
+
+@Entity({ name: 'licenses' })
+export class License {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column({ nullable: true, type: 'datetime' })
+ expiry_date: Date | null;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ type: string;
+
+ @Column()
+ status: string;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ allocated_user_id: number | null;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ order_id: number | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
+ delete_order_id: number | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ })
+ updated_at: Date;
+}
+
+@Entity({ name: 'license_allocation_history' })
+export class LicenseAllocationHistory {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ user_id: number;
+
+ @Column()
+ license_id: number;
+
+ @Column()
+ is_allocated: boolean;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ executed_at: Date;
+
+ @Column()
+ switch_from_type: string;
+
+ @Column({ nullable: true, type: 'datetime' })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ })
+ updated_at: Date;
+
+}
+
+
+@Entity({ name: "card_licenses" })
+export class CardLicense {
+ @PrimaryGeneratedColumn()
+ license_id: number;
+
+ @Column()
+ issue_id: number;
+
+ @Column()
+ card_license_key: string;
+
+ @Column({ nullable: true, type: "datetime" })
+ activated_at: Date | null;
+
+ @Column({ nullable: true, type: "datetime" })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ created_at: Date;
+
+ @Column({ nullable: true, type: "datetime" })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: "datetime",
+ })
+ updated_at: Date;
+}
\ No newline at end of file
diff --git a/data_migration_tools/server/src/repositories/licenses/errors/types.ts b/data_migration_tools/server/src/repositories/licenses/errors/types.ts
new file mode 100644
index 0000000..12b34d1
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/licenses/errors/types.ts
@@ -0,0 +1,108 @@
+// POナンバーがすでに存在するエラー
+export class PoNumberAlreadyExistError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'PoNumberAlreadyExistError';
+ }
+}
+
+// 取り込むカードライセンスが存在しないエラー
+export class LicenseNotExistError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicenseNotExistError';
+ }
+}
+
+// 取り込むライセンスが既に取り込み済みのエラー
+export class LicenseKeyAlreadyActivatedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicenseKeyAlreadyActivatedError';
+ }
+}
+
+// 注文不在エラー
+export class OrderNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'OrderNotFoundError';
+ }
+}
+// 注文発行済エラー
+export class AlreadyIssuedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AlreadyIssuedError';
+ }
+}
+// ライセンス不足エラー
+export class LicensesShortageError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicensesShortageError';
+ }
+}
+
+// ライセンス有効期限切れエラー
+export class LicenseExpiredError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicenseExpiredError';
+ }
+}
+// ライセンス割り当て不可エラー
+export class LicenseUnavailableError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicenseUnavailableError';
+ }
+}
+
+// ライセンス割り当て解除済みエラー
+export class LicenseAlreadyDeallocatedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicenseAlreadyDeallocatedError';
+ }
+}
+
+// 注文キャンセル失敗エラー
+export class CancelOrderFailedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'CancelOrderFailedError';
+ }
+}
+
+// ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
+export class AlreadyLicenseStatusChangedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AlreadyLicenseStatusChangedError';
+ }
+}
+
+// ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
+export class CancellationPeriodExpiredError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'CancellationPeriodExpiredError';
+ }
+}
+
+// ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
+export class AlreadyLicenseAllocatedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AlreadyLicenseAllocatedError';
+ }
+}
+
+// ライセンス未割当エラー
+export class LicenseNotAllocatedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LicenseNotAllocatedError';
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/licenses/licenses.repository.module.ts b/data_migration_tools/server/src/repositories/licenses/licenses.repository.module.ts
new file mode 100644
index 0000000..e3e3d0c
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/licenses/licenses.repository.module.ts
@@ -0,0 +1,17 @@
+import { Module } from "@nestjs/common";
+import { TypeOrmModule } from "@nestjs/typeorm";
+import {
+ CardLicense,
+ License,
+ LicenseAllocationHistory,
+} from "./entity/license.entity";
+import { LicensesRepositoryService } from "./licenses.repository.service";
+
+@Module({
+ imports: [
+ TypeOrmModule.forFeature([License, CardLicense, LicenseAllocationHistory]),
+ ],
+ providers: [LicensesRepositoryService],
+ exports: [LicensesRepositoryService],
+})
+export class LicensesRepositoryModule {}
diff --git a/data_migration_tools/server/src/repositories/licenses/licenses.repository.service.ts b/data_migration_tools/server/src/repositories/licenses/licenses.repository.service.ts
new file mode 100644
index 0000000..52e13b5
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/licenses/licenses.repository.service.ts
@@ -0,0 +1,130 @@
+import { Injectable, Logger } from "@nestjs/common";
+import { DataSource, In } from "typeorm";
+import {
+ License,
+ LicenseAllocationHistory,
+ CardLicense,
+} from "./entity/license.entity";
+import { insertEntities } from "../../common/repository";
+import { Context } from "../../common/log";
+import {
+ LicensesInputFile,
+ CardLicensesInputFile,
+} from "../../common/types/types";
+
+@Injectable()
+export class LicensesRepositoryService {
+ //クエリログにコメントを出力するかどうか
+ private readonly isCommentOut = process.env.STAGE !== "local";
+ constructor(private dataSource: DataSource) {}
+ private readonly logger = new Logger(LicensesRepositoryService.name);
+
+ /**
+ * ライセンスを登録する
+ * @context Context
+ * @param licensesInputFiles
+ */
+ async insertLicenses(
+ context: Context,
+ licensesInputFiles: LicensesInputFile[]
+ ): Promise<{}> {
+ const nowDate = new Date();
+ return await this.dataSource.transaction(async (entityManager) => {
+ const licenseRepo = entityManager.getRepository(License);
+
+ let newLicenses: License[] = [];
+ licensesInputFiles.forEach((licensesInputFile) => {
+ const license = new License();
+ license.account_id = licensesInputFile.account_id;
+ license.status = licensesInputFile.status;
+ license.type = licensesInputFile.type;
+ license.expiry_date = (licensesInputFile.expiry_date) ? new Date(licensesInputFile.expiry_date) : null;
+ if (licensesInputFile.allocated_user_id) {
+ license.allocated_user_id = licensesInputFile.allocated_user_id;
+ }
+ newLicenses.push(license);
+ });
+
+ // ライセンステーブルを登録
+ const insertedlicenses = await insertEntities(
+ License,
+ licenseRepo,
+ newLicenses,
+ this.isCommentOut,
+ context
+ );
+
+ const licenseAllocationHistoryRepo = entityManager.getRepository(
+ LicenseAllocationHistory
+ );
+ // ユーザに割り当てた場合はライセンス割り当て履歴にも登録
+ let newLicenseAllocationHistories: LicenseAllocationHistory[] = [];
+ insertedlicenses.forEach((insertedlicense) => {
+ if (insertedlicense.allocated_user_id) {
+ const licenseAllocationHistory = new LicenseAllocationHistory();
+ licenseAllocationHistory.user_id = insertedlicense.allocated_user_id;
+ licenseAllocationHistory.license_id = insertedlicense.id;
+ licenseAllocationHistory.is_allocated = true;
+ licenseAllocationHistory.account_id = insertedlicense.account_id;
+ licenseAllocationHistory.switch_from_type = "NONE";
+ licenseAllocationHistory.executed_at = insertedlicense.created_at;
+
+ newLicenseAllocationHistories.push(licenseAllocationHistory);
+ }
+ });
+
+ // ライセンス割り当てテーブルを登録
+ await insertEntities(
+ LicenseAllocationHistory,
+ licenseAllocationHistoryRepo,
+ newLicenseAllocationHistories,
+ this.isCommentOut,
+ context
+ );
+
+ return {};
+ });
+ }
+
+ /**
+ * カードライセンスを登録する
+ * @context Context
+ * @param cardLicensesInputFiles
+ */
+ async insertCardLicenses(
+ context: Context,
+ cardLicensesInputFiles: CardLicensesInputFile[]
+ ): Promise<{}> {
+ return await this.dataSource.transaction(async (entityManager) => {
+ const cardLicenseRepo = entityManager.getRepository(CardLicense);
+
+
+ let newCardLicenses: CardLicense[] = [];
+ cardLicensesInputFiles.forEach((cardLicensesInputFile) => {
+ const cardLicense = new CardLicense();
+ cardLicense.license_id = cardLicensesInputFile.license_id;
+ cardLicense.issue_id = cardLicensesInputFile.issue_id;
+ cardLicense.card_license_key = cardLicensesInputFile.card_license_key;
+ cardLicense.activated_at = (cardLicensesInputFile.activated_at) ? new Date(cardLicensesInputFile.activated_at) : null;
+ cardLicense.created_at = (cardLicensesInputFile.created_at) ? new Date(cardLicensesInputFile.created_at) : null;
+ cardLicense.created_by = cardLicensesInputFile.created_by;
+ cardLicense.updated_at = (cardLicensesInputFile.updated_at) ? new Date(cardLicensesInputFile.updated_at) : null;
+ cardLicense.updated_by = cardLicensesInputFile.updated_by;
+
+ newCardLicenses.push(cardLicense);
+ });
+
+ const query = cardLicenseRepo
+ .createQueryBuilder()
+ .insert()
+ .into(CardLicense);
+ if (this.isCommentOut) {
+ query.comment(`${context.getTrackingId()}_${new Date().toUTCString()}`);
+ }
+ query.values(newCardLicenses).execute();
+
+ return {};
+ });
+ }
+
+}
diff --git a/data_migration_tools/server/src/repositories/sort_criteria/entity/sort_criteria.entity.ts b/data_migration_tools/server/src/repositories/sort_criteria/entity/sort_criteria.entity.ts
new file mode 100644
index 0000000..260c9a9
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/sort_criteria/entity/sort_criteria.entity.ts
@@ -0,0 +1,16 @@
+import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
+
+@Entity({ name: 'sort_criteria' })
+export class SortCriteria {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ user_id: number;
+
+ @Column()
+ parameter: string;
+
+ @Column()
+ direction: string;
+}
diff --git a/data_migration_tools/server/src/repositories/sort_criteria/sort_criteria.repository.module.ts b/data_migration_tools/server/src/repositories/sort_criteria/sort_criteria.repository.module.ts
new file mode 100644
index 0000000..8a46b37
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/sort_criteria/sort_criteria.repository.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { SortCriteria } from './entity/sort_criteria.entity';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([SortCriteria])],
+})
+export class SortCriteriaRepositoryModule {}
diff --git a/data_migration_tools/server/src/repositories/users/entity/user.entity.ts b/data_migration_tools/server/src/repositories/users/entity/user.entity.ts
new file mode 100644
index 0000000..9ae5eb6
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/users/entity/user.entity.ts
@@ -0,0 +1,97 @@
+import { Account } from '../../../repositories/accounts/entity/account.entity';
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+ ManyToOne,
+ JoinColumn,
+} from 'typeorm';
+
+@Entity({ name: 'users' })
+export class User {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ external_id: string;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ role: string;
+
+ @Column({ nullable: true, type: 'varchar' })
+ author_id: string | null;
+
+ @Column({ nullable: true, type: 'varchar' })
+ accepted_eula_version: string | null;
+
+ @Column({ nullable: true, type: 'varchar' })
+ accepted_privacy_notice_version: string | null;
+
+ @Column({ nullable: true, type: 'varchar' })
+ accepted_dpa_version: string | null;
+
+ @Column({ default: false })
+ email_verified: boolean;
+
+ @Column({ default: true })
+ auto_renew: boolean;
+
+ @Column({ default: true })
+ notification: boolean;
+
+ @Column({ default: false })
+ encryption: boolean;
+
+ @Column({ nullable: true, type: 'varchar' })
+ encryption_password: string | null;
+
+ @Column({ default: false })
+ prompt: boolean;
+
+ @Column({ nullable: true, type: 'datetime' })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+ @ManyToOne(() => Account, (account) => account.user, {
+ createForeignKeyConstraints: false,
+ }) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
+ @JoinColumn({ name: 'account_id' })
+ account: Account | null;
+
+}
+
+
+export type newUser = Omit<
+ User,
+ | 'deleted_at'
+ | 'created_at'
+ | 'updated_at'
+ | 'updated_by'
+ | 'created_by'
+ | 'account'
+ | 'license'
+ | 'userGroupMembers'
+ | 'email_verified'
+>;
diff --git a/data_migration_tools/server/src/repositories/users/errors/types.ts b/data_migration_tools/server/src/repositories/users/errors/types.ts
new file mode 100644
index 0000000..6f90ede
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/users/errors/types.ts
@@ -0,0 +1,56 @@
+// Email検証済みエラー
+export class EmailAlreadyVerifiedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'EmailAlreadyVerifiedError';
+ }
+}
+// ユーザー未発見エラー
+export class UserNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'UserNotFoundError';
+ }
+}
+// AuthorID重複エラー
+export class AuthorIdAlreadyExistsError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'AuthorIdAlreadyExistsError';
+ }
+}
+// 不正なRole変更エラー
+export class InvalidRoleChangeError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'InvalidRoleChangeError';
+ }
+}
+// 暗号化パスワード不足エラー
+export class EncryptionPasswordNeedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'EncryptionPasswordNeedError';
+ }
+}
+// 利用規約バージョン情報不在エラー
+export class TermInfoNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'TermInfoNotFoundError';
+ }
+}
+// 利用規約バージョンパラメータ不在エラー
+export class UpdateTermsVersionNotSetError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'UpdateTermsVersionNotSetError';
+ }
+}
+// 代行操作不許可エラー
+export class DelegationNotAllowedError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'DelegationNotAllowedError';
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/users/users.repository.module.ts b/data_migration_tools/server/src/repositories/users/users.repository.module.ts
new file mode 100644
index 0000000..79be43a
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/users/users.repository.module.ts
@@ -0,0 +1,11 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { User } from './entity/user.entity';
+import { UsersRepositoryService } from './users.repository.service';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([User])],
+ providers: [UsersRepositoryService],
+ exports: [UsersRepositoryService],
+})
+export class UsersRepositoryModule {}
diff --git a/data_migration_tools/server/src/repositories/users/users.repository.service.ts b/data_migration_tools/server/src/repositories/users/users.repository.service.ts
new file mode 100644
index 0000000..ebb4dc3
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/users/users.repository.service.ts
@@ -0,0 +1,141 @@
+import { Injectable } from '@nestjs/common';
+import { User, newUser } from './entity/user.entity';
+import {
+ DataSource,
+} from 'typeorm';
+import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
+import {
+ getDirection,
+ getTaskListSortableAttribute,
+} from '../../common/types/sort/util';
+import { Context } from '../../common/log';
+import {
+ insertEntity,
+ deleteEntity,
+} from '../../common/repository';
+
+@Injectable()
+export class UsersRepositoryService {
+ // クエリログにコメントを出力するかどうか
+ private readonly isCommentOut = process.env.STAGE !== "local";
+
+ constructor(private dataSource: DataSource) {}
+
+ /**
+ * 一般ユーザーを作成する
+ * @param user
+ * @returns User
+ */
+ async createNormalUser(context: Context, user: newUser): Promise {
+ const {
+ id,
+ account_id: accountId,
+ external_id: externalUserId,
+ role,
+ auto_renew,
+ notification,
+ author_id,
+ accepted_eula_version,
+ accepted_dpa_version,
+ encryption,
+ encryption_password: encryptionPassword,
+ prompt,
+ } = user;
+ const userEntity = new User();
+
+ userEntity.id = id;
+ userEntity.role = role;
+ userEntity.account_id = accountId;
+ userEntity.external_id = externalUserId;
+ userEntity.auto_renew = auto_renew;
+ userEntity.notification = notification;
+ userEntity.author_id = author_id;
+ userEntity.accepted_eula_version = accepted_eula_version;
+ userEntity.accepted_dpa_version = accepted_dpa_version;
+ userEntity.encryption = encryption;
+ userEntity.encryption_password = encryptionPassword;
+ userEntity.prompt = prompt;
+ userEntity.email_verified = true;
+
+ const createdEntity = await this.dataSource.transaction(
+ async (entityManager) => {
+ const repo = entityManager.getRepository(User);
+ const newUser = repo.create(userEntity);
+ const persisted = await insertEntity(
+ User,
+ repo,
+ newUser,
+ this.isCommentOut,
+ context
+ );
+
+ // ユーザーのタスクソート条件を作成
+ const sortCriteria = new SortCriteria();
+ {
+ sortCriteria.parameter = getTaskListSortableAttribute("JOB_NUMBER");
+ sortCriteria.direction = getDirection("ASC");
+ sortCriteria.user_id = persisted.id;
+ }
+ const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
+ const newSortCriteria = sortCriteriaRepo.create(sortCriteria);
+ await insertEntity(
+ SortCriteria,
+ sortCriteriaRepo,
+ newSortCriteria,
+ this.isCommentOut,
+ context
+ );
+
+ return persisted;
+ }
+ );
+ return createdEntity;
+ }
+
+ /**
+ * AuthorIdが既に存在するか確認する
+ * @param user
+ * @returns 存在する:true 存在しない:false
+ */
+ async existsAuthorId(
+ context: Context,
+ accountId: number,
+ authorId: string
+ ): Promise {
+ const user = await this.dataSource.getRepository(User).findOne({
+ where: [
+ {
+ account_id: accountId,
+ author_id: authorId,
+ },
+ ],
+ comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ });
+
+ if (user) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * UserID指定のユーザーとソート条件を同時に削除する
+ * @param userId
+ * @returns delete
+ */
+ async deleteNormalUser(context: Context, userId: number): Promise {
+ await this.dataSource.transaction(async (entityManager) => {
+ const usersRepo = entityManager.getRepository(User);
+ const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
+ // ソート条件を削除
+ await deleteEntity(
+ sortCriteriaRepo,
+ { user_id: userId },
+ this.isCommentOut,
+ context
+ );
+ // プライマリ管理者を削除
+ await deleteEntity(usersRepo, { id: userId }, this.isCommentOut, context);
+ });
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/worktypes/entity/option_item.entity.ts b/data_migration_tools/server/src/repositories/worktypes/entity/option_item.entity.ts
new file mode 100644
index 0000000..f9e7ac4
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/worktypes/entity/option_item.entity.ts
@@ -0,0 +1,42 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ UpdateDateColumn,
+ CreateDateColumn,
+ ManyToOne,
+ JoinColumn,
+} from 'typeorm';
+import { Worktype } from './worktype.entity';
+
+@Entity({ name: 'option_items' })
+export class OptionItem {
+ @PrimaryGeneratedColumn()
+ id: number;
+ @Column()
+ worktype_id: number;
+ @Column()
+ item_label: string;
+ @Column()
+ default_value_type: string;
+ @Column()
+ initial_value: string;
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date | null;
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date | null;
+
+ @ManyToOne(() => Worktype, (worktype) => worktype.id)
+ @JoinColumn({ name: 'worktype_id' })
+ worktype: Worktype;
+}
diff --git a/data_migration_tools/server/src/repositories/worktypes/entity/worktype.entity.ts b/data_migration_tools/server/src/repositories/worktypes/entity/worktype.entity.ts
new file mode 100644
index 0000000..6419cbd
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/worktypes/entity/worktype.entity.ts
@@ -0,0 +1,44 @@
+import {
+ Entity,
+ Column,
+ PrimaryGeneratedColumn,
+ CreateDateColumn,
+ UpdateDateColumn,
+} from 'typeorm';
+
+@Entity({ name: 'worktypes' })
+export class Worktype {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ account_id: number;
+
+ @Column()
+ custom_worktype_id: string;
+
+ @Column({ nullable: true, type: 'varchar' })
+ description: string | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ deleted_at: Date | null;
+
+ @Column({ nullable: true, type: 'datetime' })
+ created_by: string | null;
+
+ @CreateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ created_at: Date;
+
+ @Column({ nullable: true, type: 'datetime' })
+ updated_by: string | null;
+
+ @UpdateDateColumn({
+ default: () => "datetime('now', 'localtime')",
+ type: 'datetime',
+ }) // defaultはSQLite用設定値.本番用は別途migrationで設定
+ updated_at: Date;
+
+}
diff --git a/data_migration_tools/server/src/repositories/worktypes/errors/types.ts b/data_migration_tools/server/src/repositories/worktypes/errors/types.ts
new file mode 100644
index 0000000..fb5db2d
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/worktypes/errors/types.ts
@@ -0,0 +1,28 @@
+// WorktypeID重複エラー
+export class WorktypeIdAlreadyExistsError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'WorktypeIdAlreadyExistsError';
+ }
+}
+// WorktypeID登録上限エラー
+export class WorktypeIdMaxCountError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'WorktypeIdMaxCountError';
+ }
+}
+// WorktypeID不在エラー
+export class WorktypeIdNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'WorktypeIdNotFoundError';
+ }
+}
+// WorktypeID使用中エラー
+export class WorktypeIdInUseError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'WorktypeIdInUseError';
+ }
+}
diff --git a/data_migration_tools/server/src/repositories/worktypes/worktypes.repository.module.ts b/data_migration_tools/server/src/repositories/worktypes/worktypes.repository.module.ts
new file mode 100644
index 0000000..1ae34d1
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/worktypes/worktypes.repository.module.ts
@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { Worktype } from './entity/worktype.entity';
+import { WorktypesRepositoryService } from './worktypes.repository.service';
+import { OptionItem } from "./entity/option_item.entity";
+
+@Module({
+ imports: [TypeOrmModule.forFeature([Worktype, OptionItem])],
+ providers: [WorktypesRepositoryService],
+ exports: [WorktypesRepositoryService],
+})
+export class WorktypesRepositoryModule {}
diff --git a/data_migration_tools/server/src/repositories/worktypes/worktypes.repository.service.ts b/data_migration_tools/server/src/repositories/worktypes/worktypes.repository.service.ts
new file mode 100644
index 0000000..9f7f2ff
--- /dev/null
+++ b/data_migration_tools/server/src/repositories/worktypes/worktypes.repository.service.ts
@@ -0,0 +1,104 @@
+import { Injectable } from "@nestjs/common";
+import { DataSource, Not } from "typeorm";
+import { Worktype } from "./entity/worktype.entity";
+import {
+ OPTION_ITEM_NUM,
+ OPTION_ITEM_VALUE_TYPE,
+ WORKTYPE_MAX_COUNT,
+} from "../../constants";
+import {
+ WorktypeIdAlreadyExistsError,
+ WorktypeIdMaxCountError,
+} from "./errors/types";
+import { OptionItem } from "./entity/option_item.entity";
+import { insertEntities, insertEntity } from "../../common/repository";
+import { Context } from "../../common/log";
+import { WorktypesInputFile } from "../../common/types/types";
+
+@Injectable()
+export class WorktypesRepositoryService {
+ // クエリログにコメントを出力するかどうか
+ private readonly isCommentOut = process.env.STAGE !== "local";
+
+ constructor(private dataSource: DataSource) {}
+
+ /**
+ * ワークタイプを作成する
+ * @param accountId
+ * @param worktypeId
+ * @param [description]
+ */
+ async createWorktype(
+ context: Context,
+ worktypesInputFiles: WorktypesInputFile[]
+ ): Promise {
+ await this.dataSource.transaction(async (entityManager) => {
+ const worktypeRepo = entityManager.getRepository(Worktype);
+ const optionItemRepo = entityManager.getRepository(OptionItem);
+
+ for (const worktypesInputFile of worktypesInputFiles) {
+ const accountId = worktypesInputFile.account_id;
+ const worktypeId = worktypesInputFile.custom_worktype_id;
+ const description = null;
+
+ const duplicatedWorktype = await worktypeRepo.findOne({
+ where: { account_id: accountId, custom_worktype_id: worktypeId },
+ comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ lock: { mode: "pessimistic_write" },
+ });
+
+ // ワークタイプIDが重複している場合はエラー
+ if (duplicatedWorktype) {
+ throw new WorktypeIdAlreadyExistsError(
+ `WorktypeID is already exists. WorktypeID: ${worktypeId}`
+ );
+ }
+
+ const worktypeCount = await worktypeRepo.count({
+ where: { account_id: accountId },
+ comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
+ lock: { mode: "pessimistic_write" },
+ });
+
+ // ワークタイプの登録数が上限に達している場合はエラー
+ if (worktypeCount >= WORKTYPE_MAX_COUNT) {
+ throw new WorktypeIdMaxCountError(
+ `Number of worktype is exceeded the limit. MAX_COUNT: ${WORKTYPE_MAX_COUNT}, currentCount: ${worktypeCount}`
+ );
+ }
+
+ // ワークタイプを作成
+ const worktype = await insertEntity(
+ Worktype,
+ worktypeRepo,
+ {
+ account_id: accountId,
+ custom_worktype_id: worktypeId,
+ description: description ?? null,
+ },
+ this.isCommentOut,
+ context
+ );
+
+ // ワークタイプに紐づくオプションアイテムを10件作成
+ const newOptionItems = Array.from({ length: OPTION_ITEM_NUM }, () => {
+ const optionItem = new OptionItem();
+ optionItem.worktype_id = worktype.id;
+ optionItem.item_label = "";
+ optionItem.default_value_type = OPTION_ITEM_VALUE_TYPE.DEFAULT;
+ optionItem.initial_value = "";
+
+ return optionItem;
+ });
+
+ await insertEntities(
+ OptionItem,
+ optionItemRepo,
+ newOptionItems,
+ this.isCommentOut,
+ context
+ );
+ }
+ });
+ }
+}
diff --git a/dictation_server/src/common/validators/authorId.validator.ts b/dictation_server/src/common/validators/authorId.validator.ts
index 02ad278..23afafb 100644
--- a/dictation_server/src/common/validators/authorId.validator.ts
+++ b/dictation_server/src/common/validators/authorId.validator.ts
@@ -5,11 +5,26 @@ import {
ValidationOptions,
registerDecorator,
} from 'class-validator';
+import {
+ PostUpdateUserRequest,
+ SignupRequest,
+} from '../../features/users/types/types';
+import { USER_ROLES } from '../../constants';
// 大文字英数字とアンダースコアのみを許可するバリデータ
@ValidatorConstraint({ name: 'IsAuthorId', async: false })
export class IsAuthorId implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
+ const request = args.object as SignupRequest | PostUpdateUserRequest;
+ // requestの存在チェック
+ if (!request) {
+ return false;
+ }
+ const { role } = request;
+ // roleがauthor以外の場合はスキップする
+ if (role !== USER_ROLES.AUTHOR) {
+ return true;
+ }
return /^[A-Z0-9_]*$/.test(value);
}
defaultMessage(args: ValidationArguments) {