@l-v-yonsama/multi-platform-database-drivers 0.1.1
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/.editorconfig +15 -0
- package/.eslintignore +1 -0
- package/.eslintrc.json +30 -0
- package/.github/workflows/npm-publish-github-packages.yml +50 -0
- package/.prettierrc +12 -0
- package/README.md +7 -0
- package/__tests__/db/drivers/AwsS3Driver.test.ts +97 -0
- package/__tests__/db/drivers/MySQLDriver.test.ts +140 -0
- package/__tests__/db/drivers/PostgresDriver.test.ts +139 -0
- package/__tests__/db/drivers/RedisDriver.test.ts +156 -0
- package/jest.config.js +18 -0
- package/package.json +76 -0
- package/src/db/DBError.ts +26 -0
- package/src/db/drivers/AwsS3Driver.ts +539 -0
- package/src/db/drivers/BaseDriver.ts +256 -0
- package/src/db/drivers/MongoDriver.js +110 -0
- package/src/db/drivers/MySQLDriver.ts +318 -0
- package/src/db/drivers/PostgresDriver.ts +363 -0
- package/src/db/drivers/RedisDriver.ts +277 -0
- package/src/db/manager.ts +95 -0
- package/src/db/resource/DbResource.ts +579 -0
- package/src/db/resource/MultipleResultSetDataHolder.ts +119 -0
- package/src/db/resource/ResourceUtil.ts +366 -0
- package/src/db/resource/ResultSetDataHolder.ts +806 -0
- package/src/db/resource/types/DBType.ts +43 -0
- package/src/db/resource/types/GeneralColumnType.ts +321 -0
- package/src/db/resource/types/MySQLColumnType.ts +134 -0
- package/src/db/resource/types/ODBCVendorType.ts +3 -0
- package/src/db/resource/types/PostgresColumnType.ts +94 -0
- package/src/db/resource/types/RedisKeyType.ts +48 -0
- package/src/db/resource/types/ResourceType.ts +10 -0
- package/src/main.ts +1 -0
- package/src/service/request/general_db_request.ts +20 -0
- package/src/service/request/redis_request.ts +21 -0
- package/src/service/request/s3_request.ts +15 -0
- package/src/service/response/GeneralReponse.ts +10 -0
- package/src/types/DateModifiedType.ts +75 -0
- package/src/types/FileKindType.ts +80 -0
- package/src/util/file_util.ts +178 -0
- package/tsconfig.json +23 -0
- package/tsconfig.release.json +23 -0
- package/unit-test.yml +41 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface S3Request {
|
|
2
|
+
connection_id: string;
|
|
3
|
+
command: S3CommandType;
|
|
4
|
+
Bucket: string;
|
|
5
|
+
key?: string;
|
|
6
|
+
options?: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export enum S3CommandType {
|
|
10
|
+
PutObject = "PutObject",
|
|
11
|
+
Search = "Search",
|
|
12
|
+
AbortSearch = "AbortSearch",
|
|
13
|
+
listObject = "listObject",
|
|
14
|
+
getObject = "getObject"
|
|
15
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export enum DateModifiedType {
|
|
2
|
+
None = '-',
|
|
3
|
+
Today = 'Today',
|
|
4
|
+
Yesterday = 'Yesterday',
|
|
5
|
+
ThisWeek = 'This week',
|
|
6
|
+
LastWeek = 'Last week',
|
|
7
|
+
ThisMonth = 'This month',
|
|
8
|
+
LastMonth = 'Last month',
|
|
9
|
+
ThisYear = 'This year',
|
|
10
|
+
LastYear = 'Last year',
|
|
11
|
+
}
|
|
12
|
+
const DAY1 = 1000 * 60 * 60 * 24;
|
|
13
|
+
const cutoffHours = (dt: Date): Date => {
|
|
14
|
+
const r = new Date();
|
|
15
|
+
r.setTime(dt.getTime());
|
|
16
|
+
r.setHours(0);
|
|
17
|
+
r.setMinutes(0);
|
|
18
|
+
r.setSeconds(0);
|
|
19
|
+
r.setMilliseconds(0);
|
|
20
|
+
return r;
|
|
21
|
+
};
|
|
22
|
+
export namespace DateModifiedType {
|
|
23
|
+
export function isValid(
|
|
24
|
+
type: DateModifiedType,
|
|
25
|
+
inDate: number | Date,
|
|
26
|
+
): boolean {
|
|
27
|
+
const now = new Date();
|
|
28
|
+
const today = cutoffHours(now).getTime();
|
|
29
|
+
if (typeof inDate === 'number') {
|
|
30
|
+
inDate = new Date(inDate);
|
|
31
|
+
}
|
|
32
|
+
switch (type) {
|
|
33
|
+
case DateModifiedType.Today:
|
|
34
|
+
return today <= inDate.getTime() && inDate.getTime() <= today + DAY1;
|
|
35
|
+
case DateModifiedType.Yesterday:
|
|
36
|
+
return today - DAY1 <= inDate.getTime() && inDate.getTime() <= today;
|
|
37
|
+
case DateModifiedType.ThisWeek: {
|
|
38
|
+
const d = new Date();
|
|
39
|
+
d.setTime(
|
|
40
|
+
d.getTime() - (d.getDay() ? d.getDay() : 7) * 24 * 60 * 60 * 1000,
|
|
41
|
+
);
|
|
42
|
+
const endOfWeek = cutoffHours(d).getTime();
|
|
43
|
+
d.setTime(d.getTime() - 6 * 24 * 60 * 60 * 1000);
|
|
44
|
+
const startOfWeek = cutoffHours(d).getTime();
|
|
45
|
+
return (
|
|
46
|
+
endOfWeek <= inDate.getTime() &&
|
|
47
|
+
inDate.getTime() <= startOfWeek + DAY1
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
case DateModifiedType.LastWeek: {
|
|
51
|
+
const d = new Date();
|
|
52
|
+
d.setTime(
|
|
53
|
+
d.getTime() -
|
|
54
|
+
(d.getDay() ? d.getDay() : 7) * 24 * 60 * 60 * 1000 -
|
|
55
|
+
DAY1 * 7,
|
|
56
|
+
);
|
|
57
|
+
const endOfWeek = cutoffHours(d).getTime();
|
|
58
|
+
d.setTime(d.getTime() - 6 * 24 * 60 * 60 * 1000);
|
|
59
|
+
const startOfWeek = cutoffHours(d).getTime();
|
|
60
|
+
return (
|
|
61
|
+
endOfWeek <= inDate.getTime() &&
|
|
62
|
+
inDate.getTime() <= startOfWeek + DAY1
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
case DateModifiedType.ThisMonth:
|
|
66
|
+
return (
|
|
67
|
+
now.getFullYear() === inDate.getFullYear() &&
|
|
68
|
+
now.getMonth() === inDate.getMonth()
|
|
69
|
+
);
|
|
70
|
+
case DateModifiedType.ThisYear:
|
|
71
|
+
return now.getFullYear() === inDate.getFullYear();
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export enum FileKindType {
|
|
2
|
+
All = 'All',
|
|
3
|
+
Image = 'Image',
|
|
4
|
+
Video = 'Video',
|
|
5
|
+
Music = 'Music',
|
|
6
|
+
Document = 'Document',
|
|
7
|
+
Program = 'Program',
|
|
8
|
+
Text = 'Text',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export namespace FileKindType {
|
|
12
|
+
export function isValid(type: FileKindType, file_name: string): boolean {
|
|
13
|
+
if (FileKindType.All === type) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
file_name = file_name.toLocaleLowerCase().trim();
|
|
17
|
+
const idx = file_name.lastIndexOf('.');
|
|
18
|
+
if (file_name === '' || idx <= 0) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const ext = file_name.substring(idx + 1);
|
|
22
|
+
switch (type) {
|
|
23
|
+
case FileKindType.Image:
|
|
24
|
+
return (
|
|
25
|
+
ext === 'jpg' ||
|
|
26
|
+
ext === 'jpeg' ||
|
|
27
|
+
ext === 'png' ||
|
|
28
|
+
ext === 'gif' ||
|
|
29
|
+
ext === 'tiff' ||
|
|
30
|
+
ext === 'tif' ||
|
|
31
|
+
ext === 'bmp' ||
|
|
32
|
+
ext === 'webp'
|
|
33
|
+
);
|
|
34
|
+
case FileKindType.Video:
|
|
35
|
+
return (
|
|
36
|
+
ext === 'mov' || ext === 'mp4' || ext === 'webm' || ext === 'avi'
|
|
37
|
+
);
|
|
38
|
+
case FileKindType.Music:
|
|
39
|
+
return ext === 'wav' || ext === 'mp3';
|
|
40
|
+
case FileKindType.Document:
|
|
41
|
+
return (
|
|
42
|
+
ext === 'xls' ||
|
|
43
|
+
ext === 'doc' ||
|
|
44
|
+
ext === 'ppt' ||
|
|
45
|
+
ext === 'pdf' ||
|
|
46
|
+
ext === 'xlsx' ||
|
|
47
|
+
ext === 'docx' ||
|
|
48
|
+
ext === 'pptx'
|
|
49
|
+
);
|
|
50
|
+
case FileKindType.Text:
|
|
51
|
+
if (ext === 'txt' || ext === 'json') {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return (
|
|
55
|
+
ext === 'js' ||
|
|
56
|
+
ext === 'ts' ||
|
|
57
|
+
ext === 'py' ||
|
|
58
|
+
ext === 'pyx' ||
|
|
59
|
+
ext === 'java' ||
|
|
60
|
+
ext === 'html' ||
|
|
61
|
+
ext === 'xml' ||
|
|
62
|
+
ext === 'vue' ||
|
|
63
|
+
ext === 'c'
|
|
64
|
+
);
|
|
65
|
+
case FileKindType.Program:
|
|
66
|
+
return (
|
|
67
|
+
ext === 'js' ||
|
|
68
|
+
ext === 'ts' ||
|
|
69
|
+
ext === 'py' ||
|
|
70
|
+
ext === 'pyx' ||
|
|
71
|
+
ext === 'java' ||
|
|
72
|
+
ext === 'html' ||
|
|
73
|
+
ext === 'xml' ||
|
|
74
|
+
ext === 'vue' ||
|
|
75
|
+
ext === 'c'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import prettyBytes from 'pretty-bytes';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import * as jconv from 'jconv';
|
|
7
|
+
import GeneralResponse from '../service/response/GeneralReponse';
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
replaceSafeFileName,
|
|
11
|
+
createKeyFolder,
|
|
12
|
+
getAppPath,
|
|
13
|
+
createAbsoluteFilePath,
|
|
14
|
+
getAbsTmpDirPath,
|
|
15
|
+
readFile,
|
|
16
|
+
syncReadFile,
|
|
17
|
+
isAsyncFileExists,
|
|
18
|
+
sizeToString,
|
|
19
|
+
stats,
|
|
20
|
+
getKeyFile,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const AWS = 'aws';
|
|
24
|
+
const TMP = 'tmp';
|
|
25
|
+
|
|
26
|
+
function sizeToString(len?: number): string {
|
|
27
|
+
if (len === undefined) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
return prettyBytes(len);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function stats(file_path: string): StatsExtra {
|
|
34
|
+
const stat = fs.statSync(file_path);
|
|
35
|
+
const r = <StatsExtra>Object.assign({}, stat);
|
|
36
|
+
r.size_string = sizeToString(stat.size);
|
|
37
|
+
r.type = '';
|
|
38
|
+
r.extension = '';
|
|
39
|
+
r.basename = path.basename(file_path);
|
|
40
|
+
const dot = r.basename.lastIndexOf('.');
|
|
41
|
+
if (dot >= 0) {
|
|
42
|
+
r.extension = r.basename.substring(dot + 1);
|
|
43
|
+
if (r.extension.match(/(jpg|jpeg|png|gif|bmp|tiff|tif)/i)) {
|
|
44
|
+
r.type = 'image/' + r.extension;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return r;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface StatsExtra extends fs.Stats {
|
|
51
|
+
size_string: string;
|
|
52
|
+
type: string;
|
|
53
|
+
basename: string;
|
|
54
|
+
extension: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getAppPath(): string {
|
|
58
|
+
return './';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function createAbsoluteFilePath(
|
|
62
|
+
category: string,
|
|
63
|
+
relative_file_path: string,
|
|
64
|
+
): string {
|
|
65
|
+
const userDataDir = getAppPath();
|
|
66
|
+
return path.join(userDataDir, category, relative_file_path);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const lpad = (n: number): string => (n < 10 ? '0' + n : String(n));
|
|
70
|
+
|
|
71
|
+
function replaceSafeFileName(name = ''): string {
|
|
72
|
+
return name.replace(/[<>#%{}|\\^~[]`;\?:@=& ]/gi, '_').trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function createKeyFolder(
|
|
76
|
+
bucket_name: string,
|
|
77
|
+
key: string,
|
|
78
|
+
): Promise<string> {
|
|
79
|
+
if (key.startsWith('/')) {
|
|
80
|
+
// remove.
|
|
81
|
+
key = key.substring(1);
|
|
82
|
+
}
|
|
83
|
+
let folders = [];
|
|
84
|
+
if (key.endsWith('/')) {
|
|
85
|
+
// this is folder
|
|
86
|
+
// remove.
|
|
87
|
+
key = key.substring(0, key.length - 1);
|
|
88
|
+
folders = key.split('/');
|
|
89
|
+
} else {
|
|
90
|
+
// this is file
|
|
91
|
+
folders = key.split('/');
|
|
92
|
+
folders.pop(); // remove filename
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const userDataDir = getAppPath();
|
|
96
|
+
const output_dir_path = path.join(userDataDir, AWS, bucket_name, ...folders);
|
|
97
|
+
fs.promises.mkdir(output_dir_path, { recursive: true });
|
|
98
|
+
return output_dir_path;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getAbsTmpDirPath(): string {
|
|
102
|
+
const userDataDir = getAppPath();
|
|
103
|
+
return path.join(userDataDir, TMP);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function isAsyncFileExists(full_path: string): Promise<boolean> {
|
|
107
|
+
const stat = promisify(fs.stat);
|
|
108
|
+
try {
|
|
109
|
+
await stat(full_path);
|
|
110
|
+
return true;
|
|
111
|
+
} catch (e) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function readFile(
|
|
117
|
+
full_path: string,
|
|
118
|
+
encoding: string | null = null,
|
|
119
|
+
): Promise<string | Buffer> {
|
|
120
|
+
return new Promise<string | Buffer>((resolve, reject) => {
|
|
121
|
+
if (encoding && encoding.match(/sjis|jis|eucjp|ucs2|utf16le/i)) {
|
|
122
|
+
fs.readFile(full_path, (err, buf: Buffer) => {
|
|
123
|
+
if (err) {
|
|
124
|
+
reject(err);
|
|
125
|
+
} else {
|
|
126
|
+
// var buf = new Buffer(data, 'binary');
|
|
127
|
+
const retStr = jconv.decode(buf, encoding);
|
|
128
|
+
resolve(retStr);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
// log.info(LOG_PREFIX, '#readFile', full_path)
|
|
133
|
+
fs.readFile(full_path, encoding as BufferEncoding, (err, data) => {
|
|
134
|
+
if (err) {
|
|
135
|
+
reject(err);
|
|
136
|
+
} else {
|
|
137
|
+
resolve(data);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function syncReadFile(
|
|
145
|
+
full_path: string,
|
|
146
|
+
encoding: BufferEncoding | null = null,
|
|
147
|
+
): string | Buffer {
|
|
148
|
+
if (encoding) {
|
|
149
|
+
return fs.readFileSync(full_path, encoding);
|
|
150
|
+
}
|
|
151
|
+
return fs.readFileSync(full_path);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function getKeyFile(
|
|
155
|
+
bucket_name: string,
|
|
156
|
+
key: string,
|
|
157
|
+
): Promise<GeneralResponse> {
|
|
158
|
+
if (key.startsWith('/')) {
|
|
159
|
+
key = key.substring(1);
|
|
160
|
+
}
|
|
161
|
+
if (key.endsWith('/')) {
|
|
162
|
+
// this is folder
|
|
163
|
+
// remove.
|
|
164
|
+
key = key.substring(0, key.length - 1);
|
|
165
|
+
}
|
|
166
|
+
const userDataDir = getAppPath();
|
|
167
|
+
const key_file_path = path.join(
|
|
168
|
+
userDataDir,
|
|
169
|
+
AWS,
|
|
170
|
+
bucket_name,
|
|
171
|
+
...key.split('/'),
|
|
172
|
+
);
|
|
173
|
+
const exists = await isAsyncFileExists(key_file_path);
|
|
174
|
+
const res = new GeneralResponse(exists, '');
|
|
175
|
+
res.result = key_file_path;
|
|
176
|
+
// log.info(LOG_PREFIX, "key_file_path:res=", res);
|
|
177
|
+
return res;
|
|
178
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2017",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": false,
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"rootDir": ".",
|
|
8
|
+
"outDir": "build",
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"importHelpers": true,
|
|
11
|
+
"alwaysStrict": true,
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"noImplicitReturns": true,
|
|
16
|
+
"noUnusedLocals": false,
|
|
17
|
+
"noUnusedParameters": false,
|
|
18
|
+
"noImplicitAny": false,
|
|
19
|
+
"noImplicitThis": false,
|
|
20
|
+
"strictNullChecks": false,
|
|
21
|
+
"skipLibCheck": true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2017",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"rootDir": ".",
|
|
8
|
+
"outDir": "build",
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"importHelpers": true,
|
|
11
|
+
"alwaysStrict": true,
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"noImplicitReturns": true,
|
|
16
|
+
"noUnusedLocals": false,
|
|
17
|
+
"noUnusedParameters": false,
|
|
18
|
+
"noImplicitAny": false,
|
|
19
|
+
"noImplicitThis": false,
|
|
20
|
+
"strictNullChecks": false,
|
|
21
|
+
"skipLibCheck": true
|
|
22
|
+
}
|
|
23
|
+
}
|
package/unit-test.yml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
version: '3'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
redis:
|
|
5
|
+
image: redis:5.0
|
|
6
|
+
container_name: redis_container
|
|
7
|
+
command: redis-server --requirepass testpass
|
|
8
|
+
ports:
|
|
9
|
+
- '6000:6379'
|
|
10
|
+
|
|
11
|
+
mysql:
|
|
12
|
+
image: mysql:8.0
|
|
13
|
+
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
|
14
|
+
container_name: mysql_container
|
|
15
|
+
ports:
|
|
16
|
+
- 6001:3306
|
|
17
|
+
environment:
|
|
18
|
+
TZ: 'Asia/Tokyo'
|
|
19
|
+
MYSQL_ROOT_PASSWORD: p@ssw0rd
|
|
20
|
+
MYSQL_DATABASE: testdb
|
|
21
|
+
MYSQL_USER: testuser
|
|
22
|
+
MYSQL_PASSWORD: testpass
|
|
23
|
+
postgres:
|
|
24
|
+
image: postgres:14
|
|
25
|
+
container_name: postgres_container
|
|
26
|
+
ports:
|
|
27
|
+
- 6002:5432
|
|
28
|
+
environment:
|
|
29
|
+
- POSTGRES_USER=testuser
|
|
30
|
+
- POSTGRES_PASSWORD=testpass
|
|
31
|
+
- POSTGRES_DB=testdb
|
|
32
|
+
minio:
|
|
33
|
+
image: quay.io/minio/minio:latest
|
|
34
|
+
container_name: minio_container
|
|
35
|
+
environment:
|
|
36
|
+
MINIO_ROOT_USER: testuser
|
|
37
|
+
MINIO_ROOT_PASSWORD: testpass
|
|
38
|
+
command: server --console-address ":9001" /data
|
|
39
|
+
ports:
|
|
40
|
+
- 6003:9000
|
|
41
|
+
- 6004:9001
|