pg-mvc-service 2.0.42 → 2.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/favicon.ico +0 -0
- package/dist/models/TableDoc.js +4 -1
- package/package.json +8 -2
- package/index.d.ts +0 -189
- package/src/PoolManager.ts +0 -48
- package/src/Service.ts +0 -276
- package/src/Utils/DateTimeUtil.ts +0 -146
- package/src/Utils/NumberUtil.ts +0 -23
- package/src/Utils/StringUtil.ts +0 -33
- package/src/clients/AwsS3Client.ts +0 -310
- package/src/clients/Base64Client.ts +0 -305
- package/src/clients/EncryptClient.ts +0 -100
- package/src/clients/StringClient.ts +0 -19
- package/src/cron/BaseCron.ts +0 -122
- package/src/cron/CronExecuter.ts +0 -34
- package/src/cron/CronType.ts +0 -25
- package/src/documents/Swagger.ts +0 -105
- package/src/exceptions/Exception.ts +0 -72
- package/src/index.ts +0 -23
- package/src/models/ExpressionClient.ts +0 -72
- package/src/models/MigrateDatabase.ts +0 -135
- package/src/models/MigrateRollback.ts +0 -151
- package/src/models/MigrateTable.ts +0 -56
- package/src/models/SqlUtils/SelectExpression.ts +0 -97
- package/src/models/SqlUtils/UpdateExpression.ts +0 -29
- package/src/models/SqlUtils/ValidateValueUtil.ts +0 -354
- package/src/models/SqlUtils/WhereExpression.ts +0 -421
- package/src/models/TableDoc.ts +0 -366
- package/src/models/TableModel.ts +0 -701
- package/src/models/Type.ts +0 -62
- package/src/models/Utils/MessageUtil.ts +0 -60
- package/src/models/ValidateClient.ts +0 -182
- package/src/reqestResponse/ReqResType.ts +0 -170
- package/src/reqestResponse/RequestType.ts +0 -918
- package/src/reqestResponse/ResponseType.ts +0 -420
- package/tsconfig.json +0 -14
package/src/documents/Swagger.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Service } from '../Service';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export interface IParams {
|
|
5
|
-
in: 'header' | 'path',
|
|
6
|
-
name: string,
|
|
7
|
-
require?: boolean,
|
|
8
|
-
description?: string,
|
|
9
|
-
example?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const createSwagger = (services: Service[], name: string, url: string, params: Array<IParams> = []): string => {
|
|
13
|
-
// *****************************************
|
|
14
|
-
// Internal method definitions
|
|
15
|
-
// *****************************************
|
|
16
|
-
function setYmlByMethod(method: string, swaggerYmlObj: {[key: string]: string}) {
|
|
17
|
-
if (method in swaggerYmlObj) {
|
|
18
|
-
swaggerInfo += ` ${method.toLowerCase()}:\n`;
|
|
19
|
-
swaggerInfo += swaggerYmlObj[method];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
// *****************************************
|
|
23
|
-
// Execution part
|
|
24
|
-
// *****************************************
|
|
25
|
-
const endpontSwaggerYml: {[keyEndpoint: string]: {[keyMethod: string]: string}} = {};
|
|
26
|
-
let tags: Array<string> = [];
|
|
27
|
-
|
|
28
|
-
for (const service of services) {
|
|
29
|
-
if (service.Endpoint in endpontSwaggerYml === false) {
|
|
30
|
-
endpontSwaggerYml[service.Endpoint] = {};
|
|
31
|
-
}
|
|
32
|
-
let yml = "";
|
|
33
|
-
|
|
34
|
-
const apiTags = service.Tags;
|
|
35
|
-
if (apiTags.length > 0) {
|
|
36
|
-
tags = [ ...tags, ...apiTags];
|
|
37
|
-
yml += ` tags:\n`;
|
|
38
|
-
for (const tag of apiTags) {
|
|
39
|
-
yml += ` - ${tag}\n`;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
yml += ` summary: ${service.Summary}\n`;
|
|
43
|
-
|
|
44
|
-
const croneParams = [...params];
|
|
45
|
-
for (const path of service.Endpoint.split('/')) {
|
|
46
|
-
if (path.startsWith('{') && path.endsWith('}')) {
|
|
47
|
-
const key = path.replace('{', '').replace('}', '');
|
|
48
|
-
croneParams.push({
|
|
49
|
-
in: 'path',
|
|
50
|
-
name: key,
|
|
51
|
-
require: true,
|
|
52
|
-
description: key,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (croneParams.length > 0) {
|
|
58
|
-
yml += ` parameters:\n`;
|
|
59
|
-
for (const param of croneParams) {
|
|
60
|
-
yml += ` - in: ${param.in}\n`;
|
|
61
|
-
yml += ` name: ${param.name}\n`;
|
|
62
|
-
yml += ` required: ${param.require === true ? 'true' : 'false'}\n`;
|
|
63
|
-
if (param.description !== undefined) {
|
|
64
|
-
yml += ` description: |\n ${param.description}\n`;
|
|
65
|
-
}
|
|
66
|
-
if (param.example !== undefined) {
|
|
67
|
-
yml += ` example: ${param.example}\n`;
|
|
68
|
-
}
|
|
69
|
-
yml += ` schema:\n`;
|
|
70
|
-
yml += ` type: string\n`;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
yml += service.Request.createSwagger(service.Method);
|
|
75
|
-
yml += service.Response.createSwagger();
|
|
76
|
-
|
|
77
|
-
endpontSwaggerYml[service.Endpoint][service.Method] = yml;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let swaggerInfo = `openapi: 3.0.0
|
|
81
|
-
info:
|
|
82
|
-
title: Your API Documentation
|
|
83
|
-
version: 1.0.0
|
|
84
|
-
description: API documentation for your service
|
|
85
|
-
servers:
|
|
86
|
-
- url: ${url}
|
|
87
|
-
description: ${name} API IF定義書
|
|
88
|
-
tags:\n`;
|
|
89
|
-
for (const tag of tags) {
|
|
90
|
-
swaggerInfo += ` - name: ${tag}\n`;
|
|
91
|
-
}
|
|
92
|
-
swaggerInfo += 'paths:\n'
|
|
93
|
-
|
|
94
|
-
for (const keyEndpoint in endpontSwaggerYml) {
|
|
95
|
-
swaggerInfo += ` ${keyEndpoint}:\n`;
|
|
96
|
-
|
|
97
|
-
setYmlByMethod('GET', endpontSwaggerYml[keyEndpoint]);
|
|
98
|
-
setYmlByMethod('POST', endpontSwaggerYml[keyEndpoint]);
|
|
99
|
-
setYmlByMethod('PUT', endpontSwaggerYml[keyEndpoint]);
|
|
100
|
-
setYmlByMethod('PATCH', endpontSwaggerYml[keyEndpoint]);
|
|
101
|
-
setYmlByMethod('DELETE', endpontSwaggerYml[keyEndpoint]);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return swaggerInfo;
|
|
105
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
export class AuthException extends Error {
|
|
2
|
-
private id: string = "";
|
|
3
|
-
get Id(): string {
|
|
4
|
-
return this.id;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
constructor(id: string, message: string = "") {
|
|
8
|
-
super(message);
|
|
9
|
-
this.id = id;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class ForbiddenException extends Error {
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class InputErrorException extends Error {
|
|
17
|
-
private errorId: string = "";
|
|
18
|
-
get ErrorId(): string {
|
|
19
|
-
return this.errorId;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
constructor(errorId: string, message: string = "") {
|
|
23
|
-
super(message);
|
|
24
|
-
this.errorId = errorId;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class MaintenanceException extends Error {
|
|
29
|
-
|
|
30
|
-
constructor(message: string = "") {
|
|
31
|
-
super(message);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class NotFoundException extends Error {
|
|
36
|
-
// for 404 Not Found
|
|
37
|
-
private errorId: string = "";
|
|
38
|
-
get ErrorId(): string {
|
|
39
|
-
return this.errorId;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
constructor(errorId: string, message: string = "") {
|
|
43
|
-
super(message);
|
|
44
|
-
this.errorId = errorId;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class DbConflictException extends Error {
|
|
49
|
-
// for 409 Conflict
|
|
50
|
-
private errorId: string = "";
|
|
51
|
-
get ErrorId(): string {
|
|
52
|
-
return this.errorId;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
constructor(errorId: string, message: string = "") {
|
|
56
|
-
super(message);
|
|
57
|
-
this.errorId = errorId;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export class UnprocessableException extends Error {
|
|
62
|
-
// for 422 Unprocessable Entity
|
|
63
|
-
private errorId: string = "";
|
|
64
|
-
get ErrorId(): string {
|
|
65
|
-
return this.errorId;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
constructor(errorId: string, message: string = "") {
|
|
69
|
-
super(message);
|
|
70
|
-
this.errorId = errorId;
|
|
71
|
-
}
|
|
72
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export { Service } from './Service';
|
|
2
|
-
export { MaintenanceException, AuthException, InputErrorException, ForbiddenException, DbConflictException, UnprocessableException, NotFoundException } from './exceptions/Exception';
|
|
3
|
-
export { createSwagger } from './documents/Swagger';
|
|
4
|
-
export { AwsS3Client } from './clients/AwsS3Client';
|
|
5
|
-
export { Base64Client } from './clients/Base64Client';
|
|
6
|
-
export { StringClient } from './clients/StringClient';
|
|
7
|
-
export { EncryptClient } from './clients/EncryptClient';
|
|
8
|
-
|
|
9
|
-
export { RequestType } from './reqestResponse/RequestType';
|
|
10
|
-
export { ResponseType } from './reqestResponse/ResponseType';
|
|
11
|
-
export { PropertyType } from './reqestResponse/ReqResType';
|
|
12
|
-
|
|
13
|
-
// models
|
|
14
|
-
export { TableModel } from './models/TableModel';
|
|
15
|
-
export { createTableDoc } from './models/TableDoc';
|
|
16
|
-
export { MigrateTable } from './models/MigrateTable';
|
|
17
|
-
export { MigrateDatabase } from './models/MigrateDatabase';
|
|
18
|
-
export { migrate, rollback } from './models/MigrateRollback';
|
|
19
|
-
|
|
20
|
-
// cron
|
|
21
|
-
export { DayType, MonthType, DateType, HourType, MinuteSecondType } from './cron/CronType';
|
|
22
|
-
export { BaseCron } from './cron/BaseCron';
|
|
23
|
-
export { runCron } from './cron/CronExecuter';
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { TableModel } from "./TableModel";
|
|
2
|
-
import { TColumnInfo } from "./Type";
|
|
3
|
-
|
|
4
|
-
export default class ExpressionClient {
|
|
5
|
-
private model: TableModel;
|
|
6
|
-
constructor(model: TableModel) {
|
|
7
|
-
this.model = model;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public toSqlStringValue(value: any): string {
|
|
11
|
-
if (typeof value === 'string' || typeof value === 'number') {
|
|
12
|
-
return `'${value}'`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
throw new Error('Please enter a value of type string or number.');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public toSqlNumberValue(value: any): string {
|
|
19
|
-
if (typeof value === 'number') {
|
|
20
|
-
return value.toString();
|
|
21
|
-
} else if (typeof value === 'string') {
|
|
22
|
-
if (value.trim() !== "" || isNaN(Number(value)) === false) {
|
|
23
|
-
return value;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
throw new Error('Please enter a value of type number or a string of half-width digits.');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public createCaseFromObject(column: string | TColumnInfo, obj: {[key: string | number]: string | number | boolean | null}, elseValue: string | number | boolean | null): string {
|
|
31
|
-
const columnInfo = typeof column === 'string' ? this.model.getColumn(column) : column.model.getColumn(column.name);
|
|
32
|
-
if (columnInfo.type !== 'string' && columnInfo.type !== 'integer') {
|
|
33
|
-
throw new Error('This method cannot be used for columns other than integer, real, or string.');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const whenExpression = Object.entries(obj).map(([key, value]) => {
|
|
37
|
-
let expression = `WHEN ${columnInfo.expression} = `;
|
|
38
|
-
// 今は文字列と数値だけだが、他のも対応しないといけないかも
|
|
39
|
-
if (columnInfo.type === 'string') {
|
|
40
|
-
expression += this.toSqlStringValue(key);
|
|
41
|
-
} else {
|
|
42
|
-
expression += this.toSqlNumberValue(key);
|
|
43
|
-
}
|
|
44
|
-
expression += ` THEN `;
|
|
45
|
-
|
|
46
|
-
if (value === null) {
|
|
47
|
-
expression += 'null';
|
|
48
|
-
} else if (typeof value === 'string') {
|
|
49
|
-
expression += this.toSqlStringValue(value);
|
|
50
|
-
} else if (typeof value === 'boolean') {
|
|
51
|
-
expression += `${value}`;
|
|
52
|
-
} else {
|
|
53
|
-
expression += `${value}`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return expression;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
let elseExpression = `ELSE `;
|
|
60
|
-
if (elseValue === null) {
|
|
61
|
-
elseExpression += 'null';
|
|
62
|
-
} else if (typeof elseValue === 'string') {
|
|
63
|
-
elseExpression += this.toSqlStringValue(elseValue);
|
|
64
|
-
} else if (typeof elseValue === 'boolean') {
|
|
65
|
-
elseExpression += `${elseValue}`;
|
|
66
|
-
} else {
|
|
67
|
-
elseExpression += `${elseValue}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return `CASE ${whenExpression.join(' ')} ${elseExpression} END`;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { Pool } from "pg";
|
|
2
|
-
|
|
3
|
-
export class MigrateDatabase {
|
|
4
|
-
|
|
5
|
-
private dbName: string;
|
|
6
|
-
get DbName(): string { return this.dbName; }
|
|
7
|
-
private userName: string;
|
|
8
|
-
get UserName(): string { return this.userName; }
|
|
9
|
-
private password: string | null = null;
|
|
10
|
-
get Password(): string | null {
|
|
11
|
-
return this.password;
|
|
12
|
-
}
|
|
13
|
-
private pool: Pool;
|
|
14
|
-
|
|
15
|
-
constructor (dbName: string, userName: string, pool: Pool) {
|
|
16
|
-
this.dbName = dbName;
|
|
17
|
-
this.userName = userName;
|
|
18
|
-
this.pool = pool;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public async IsExistUser(): Promise<boolean> {
|
|
22
|
-
const sql = `
|
|
23
|
-
SELECT count(*) > 0 as is_exist
|
|
24
|
-
FROM pg_roles
|
|
25
|
-
WHERE rolname = '${this.UserName}';
|
|
26
|
-
`;
|
|
27
|
-
const datas = await this.pool.query(sql);
|
|
28
|
-
return datas.rows[0].is_exist;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
public async CreateUser(password: string = ''): Promise<void> {
|
|
32
|
-
if (password.trim() === '') {
|
|
33
|
-
password = '';
|
|
34
|
-
|
|
35
|
-
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@$%^&*_+|;:.<>?';
|
|
36
|
-
for (let i = 0; i < 36; i++) {
|
|
37
|
-
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
38
|
-
password += characters[randomIndex];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
this.password = password;
|
|
42
|
-
|
|
43
|
-
const sql = `
|
|
44
|
-
DO $$
|
|
45
|
-
BEGIN
|
|
46
|
-
IF NOT EXISTS (
|
|
47
|
-
SELECT FROM pg_catalog.pg_roles WHERE rolname = '${this.UserName}'
|
|
48
|
-
) THEN
|
|
49
|
-
CREATE USER ${this.UserName} WITH PASSWORD '${password}';
|
|
50
|
-
END IF;
|
|
51
|
-
END
|
|
52
|
-
$$;
|
|
53
|
-
|
|
54
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO ${this.UserName};
|
|
55
|
-
|
|
56
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
57
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${this.UserName};
|
|
58
|
-
`;
|
|
59
|
-
await this.pool.query(sql);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public async IsExistDb(): Promise<boolean> {
|
|
63
|
-
const sql = `
|
|
64
|
-
SELECT count(*) > 0 as is_exist
|
|
65
|
-
FROM pg_database
|
|
66
|
-
WHERE datname = '${this.DbName}';
|
|
67
|
-
`;
|
|
68
|
-
const datas = await this.pool.query(sql);
|
|
69
|
-
return datas.rows[0].is_exist;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public async CreateDb(collateType: string = 'C'): Promise<void> {
|
|
73
|
-
const sql = `
|
|
74
|
-
CREATE DATABASE ${this.DbName}
|
|
75
|
-
WITH OWNER = ${this.UserName}
|
|
76
|
-
ENCODING = 'UTF8'
|
|
77
|
-
LC_COLLATE = '${collateType}'
|
|
78
|
-
LC_CTYPE = '${collateType}'
|
|
79
|
-
CONNECTION LIMIT = -1;
|
|
80
|
-
`;
|
|
81
|
-
await this.pool.query(sql);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
public RollbackDbSql(): string {
|
|
85
|
-
const sql = `
|
|
86
|
-
-- ${this.DbName}データベースに接続しているすべてのセッションを強制終了
|
|
87
|
-
SELECT pg_terminate_backend(pid)
|
|
88
|
-
FROM pg_stat_activity
|
|
89
|
-
WHERE datname = '${this.DbName}';
|
|
90
|
-
|
|
91
|
-
-- DB削除
|
|
92
|
-
DROP DATABASE IF EXISTS ${this.DbName};`;
|
|
93
|
-
return this.trimSpaceLineSql(sql);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
public RollbackUserSql(otherUserName: string): string {
|
|
97
|
-
const sql = `
|
|
98
|
-
-- 1. すべてのセッションを強制終了
|
|
99
|
-
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
100
|
-
FROM pg_stat_activity
|
|
101
|
-
WHERE usename = '${this.UserName}';
|
|
102
|
-
|
|
103
|
-
-- 2. 所有オブジェクトを ${otherUserName} に移行
|
|
104
|
-
REASSIGN OWNED BY ${this.UserName} TO ${otherUserName};
|
|
105
|
-
|
|
106
|
-
-- 2. すべての権限を削除
|
|
107
|
-
DROP OWNED BY ${this.UserName} CASCADE;
|
|
108
|
-
|
|
109
|
-
-- 3. ロールを削除
|
|
110
|
-
DROP ROLE IF EXISTS ${this.UserName};`;
|
|
111
|
-
return this.trimSpaceLineSql(sql);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
private trimSpaceLineSql(str: string) {
|
|
115
|
-
const splitLines = str.split('\n');
|
|
116
|
-
let sql = '';
|
|
117
|
-
for (let line of splitLines) {
|
|
118
|
-
line = line.replace(/\s+/g, ' ').trim(); // 複数のスペースを一つに置き換え
|
|
119
|
-
|
|
120
|
-
if (line.startsWith('--') && sql[sql.length - 1] != '\n') {
|
|
121
|
-
line = '\n' + line;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (line.length > 0) {
|
|
125
|
-
if (line.includes('--') === false) {
|
|
126
|
-
sql += line + ' ';
|
|
127
|
-
} else {
|
|
128
|
-
sql += line + '\n';
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return sql;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { Pool } from "pg";
|
|
2
|
-
import { MigrateTable } from "./MigrateTable";
|
|
3
|
-
|
|
4
|
-
export const migrate = async (migrates: Array<MigrateTable>, poolParam: {
|
|
5
|
-
host: string, user: string, dbName: string, password: string, port?: number, isSsl?: boolean
|
|
6
|
-
}): Promise<void> => {
|
|
7
|
-
|
|
8
|
-
const pool = new Pool({
|
|
9
|
-
user: poolParam.user,
|
|
10
|
-
password: poolParam.password,
|
|
11
|
-
host: poolParam.host,
|
|
12
|
-
database: poolParam.dbName,
|
|
13
|
-
port: poolParam.port ?? 5432,
|
|
14
|
-
ssl: poolParam.isSsl === true ? {
|
|
15
|
-
rejectUnauthorized: false
|
|
16
|
-
} : false
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
// create migration table
|
|
20
|
-
try {
|
|
21
|
-
if (await isExistMigrationTable(pool) == false) {
|
|
22
|
-
const sql = `
|
|
23
|
-
CREATE TABLE migrations (
|
|
24
|
-
migration_number int,
|
|
25
|
-
script_file VARCHAR(50),
|
|
26
|
-
rollback_script VARCHAR(5000),
|
|
27
|
-
create_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
28
|
-
);`;
|
|
29
|
-
await pool.query(sql);
|
|
30
|
-
}
|
|
31
|
-
} catch (ex) {
|
|
32
|
-
console.error('An error occurred related to the Migrate table:', ex);
|
|
33
|
-
await pool.end();
|
|
34
|
-
throw ex;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const client = await pool.connect();
|
|
38
|
-
try {
|
|
39
|
-
client.query('BEGIN');
|
|
40
|
-
|
|
41
|
-
const datas = await getMigrations(pool);
|
|
42
|
-
let maxNumber = datas.maxNumber;
|
|
43
|
-
for (const migrate of migrates) {
|
|
44
|
-
const className = migrate.constructor.name;
|
|
45
|
-
if (datas.datas.filter(data => data.script_file === className).length > 0) {
|
|
46
|
-
console.log(`Already executed: ${className}`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
await client.query(migrate.MigrateSql);
|
|
51
|
-
|
|
52
|
-
const grantSql = migrate.AddGrantSql;
|
|
53
|
-
if (grantSql !== null) {
|
|
54
|
-
await client.query(grantSql);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const migrateInsertSql = `
|
|
58
|
-
INSERT INTO migrations
|
|
59
|
-
(migration_number, script_file, rollback_script)
|
|
60
|
-
VALUES (${maxNumber + 1}, '${className}', '${migrate.RollbackSql}');
|
|
61
|
-
`;
|
|
62
|
-
maxNumber++;
|
|
63
|
-
|
|
64
|
-
await client.query(migrateInsertSql);
|
|
65
|
-
|
|
66
|
-
console.log(`Execution completed: ${className}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
await client.query('COMMIT');
|
|
70
|
-
|
|
71
|
-
console.log('Migration completed');
|
|
72
|
-
} catch (ex) {
|
|
73
|
-
await client.query('ROLLBACK');
|
|
74
|
-
console.log('Migration failed.', ex);
|
|
75
|
-
} finally {
|
|
76
|
-
client.release();
|
|
77
|
-
await pool.end();
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export const rollback = async (toNumber: number, poolParam: {
|
|
82
|
-
host: string, user: string, dbName: string, password: string, port?: number, isSsl?: boolean
|
|
83
|
-
}): Promise<void> => {
|
|
84
|
-
|
|
85
|
-
const pool = new Pool({
|
|
86
|
-
user: poolParam.user,
|
|
87
|
-
password: poolParam.password,
|
|
88
|
-
host: poolParam.host,
|
|
89
|
-
database: poolParam.dbName,
|
|
90
|
-
port: poolParam.port ?? 5432,
|
|
91
|
-
ssl: poolParam.isSsl === true ? {
|
|
92
|
-
rejectUnauthorized: false
|
|
93
|
-
} : false
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
// If the migration table does not exist, there is no target for rollback, so do not perform it
|
|
98
|
-
if (await isExistMigrationTable(pool) == false) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
} catch (ex) {
|
|
102
|
-
console.error('An error occurred related to the Migrate table:', ex);
|
|
103
|
-
await pool.end();
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const client = await pool.connect();
|
|
108
|
-
try {
|
|
109
|
-
await client.query('BEGIN');
|
|
110
|
-
|
|
111
|
-
const datas = await getMigrations(pool);
|
|
112
|
-
for (const data of datas.datas) {
|
|
113
|
-
if (data.migration_number < toNumber) {
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
await client.query(data.rollback_script);
|
|
117
|
-
await client.query(`DELETE FROM migrations WHERE migration_number = ${data.migration_number}`);
|
|
118
|
-
|
|
119
|
-
console.log(`Execution completed: ${data.script_file}`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
await client.query('COMMIT');
|
|
123
|
-
|
|
124
|
-
console.log('Rollback completed');
|
|
125
|
-
} catch (ex) {
|
|
126
|
-
await client.query('ROLLBACK');
|
|
127
|
-
console.error('Rollback failed', ex);
|
|
128
|
-
} finally {
|
|
129
|
-
client.release();
|
|
130
|
-
await pool.end();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const isExistMigrationTable = async (pool: Pool) => {
|
|
135
|
-
const existMigrationTableSql = `
|
|
136
|
-
SELECT EXISTS (
|
|
137
|
-
SELECT FROM information_schema.tables
|
|
138
|
-
WHERE table_name = 'migrations'
|
|
139
|
-
);
|
|
140
|
-
`;
|
|
141
|
-
const res = await pool.query(existMigrationTableSql);
|
|
142
|
-
return res.rows[0].exists;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const getMigrations = async (pool: Pool): Promise<{datas: Array<{migration_number: number, script_file: string, rollback_script: string}>, maxNumber: number}> => {
|
|
146
|
-
const datas = await pool.query("SELECT * FROM migrations ORDER BY migration_number DESC;");
|
|
147
|
-
return {
|
|
148
|
-
maxNumber: datas.rows.reduce((max, data) => data.migration_number > max ? data.migration_number : max, 0),
|
|
149
|
-
datas: datas.rows
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
export class MigrateTable {
|
|
2
|
-
|
|
3
|
-
protected readonly migrateSql: string = '';
|
|
4
|
-
protected readonly rollbackSql: string = '';
|
|
5
|
-
protected readonly addGrantTables: Array<string> = [];
|
|
6
|
-
protected readonly user: string = '';
|
|
7
|
-
|
|
8
|
-
get MigrateSql(): string { return this.trimSpaceLineSql(this.migrateSql); }
|
|
9
|
-
get RollbackSql(): string { return this.trimSpaceLineSql(this.rollbackSql); }
|
|
10
|
-
get AddGrantSql(): string | null {
|
|
11
|
-
if ((this.user ?? "").trim() === "") {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const tables = this.addGrantTables.filter(table => table.trim() !== '');
|
|
16
|
-
if (tables.length === 0) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let sql = "";
|
|
21
|
-
for (const table of tables) {
|
|
22
|
-
sql += `GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.${table} TO ${this.user};`;
|
|
23
|
-
}
|
|
24
|
-
return sql;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
constructor();
|
|
28
|
-
constructor(user: string);
|
|
29
|
-
constructor(user?: string) {
|
|
30
|
-
if (user !== undefined) {
|
|
31
|
-
this.user = user;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private trimSpaceLineSql(str: string) {
|
|
36
|
-
const splitLines = str.split('\n');
|
|
37
|
-
let sql = '';
|
|
38
|
-
for (let line of splitLines) {
|
|
39
|
-
line = line.replace(/\s+/g, ' ').trim(); // 複数のスペースを一つに置き換え
|
|
40
|
-
|
|
41
|
-
if (line.startsWith('--') && sql[sql.length - 1] != '\n') {
|
|
42
|
-
line = '\n' + line;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (line.length > 0) {
|
|
46
|
-
if (line.includes('--') === false) {
|
|
47
|
-
sql += line + ' ';
|
|
48
|
-
} else {
|
|
49
|
-
sql += line + '\n';
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return sql;
|
|
55
|
-
}
|
|
56
|
-
}
|