ztechno_core 0.0.67 → 0.0.68
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/lib/typings/user_types.d.ts +20 -1
- package/lib/user_service.d.ts +57 -18
- package/lib/user_service.js +127 -39
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type ZUserCore = {
|
|
2
2
|
user_id: number;
|
|
3
3
|
email: string;
|
|
4
4
|
session: string;
|
|
@@ -7,12 +7,24 @@ export type ZUser = {
|
|
|
7
7
|
updated_at: any;
|
|
8
8
|
created_at: any;
|
|
9
9
|
};
|
|
10
|
+
export type ZUser = ZUserCore;
|
|
11
|
+
export interface ZUserExtended extends ZUserCore {
|
|
12
|
+
username?: string;
|
|
13
|
+
first_name?: string;
|
|
14
|
+
last_name?: string;
|
|
15
|
+
avatar_url?: string;
|
|
16
|
+
bio?: string;
|
|
17
|
+
is_active?: 0 | 1;
|
|
18
|
+
email_verified?: 0 | 1;
|
|
19
|
+
last_login?: any;
|
|
20
|
+
}
|
|
10
21
|
export type ZRequiredUserColumns = {
|
|
11
22
|
email: string;
|
|
12
23
|
role: string | null;
|
|
13
24
|
pass: string;
|
|
14
25
|
admin: 0 | 1;
|
|
15
26
|
};
|
|
27
|
+
export type ZRequiredUserColumnsExtended<T = {}> = ZRequiredUserColumns & Partial<T>;
|
|
16
28
|
export type ZUserCredentials = {
|
|
17
29
|
email: string;
|
|
18
30
|
pass: string;
|
|
@@ -20,3 +32,10 @@ export type ZUserCredentials = {
|
|
|
20
32
|
export type ZUserSession = {
|
|
21
33
|
session: string;
|
|
22
34
|
};
|
|
35
|
+
export type ZUserTableConfig = {
|
|
36
|
+
tableName?: string;
|
|
37
|
+
customColumns?: {
|
|
38
|
+
[columnName: string]: string;
|
|
39
|
+
};
|
|
40
|
+
customIndexes?: string[];
|
|
41
|
+
};
|
package/lib/user_service.d.ts
CHANGED
|
@@ -1,27 +1,65 @@
|
|
|
1
1
|
import { ZSQLService } from './sql_service';
|
|
2
|
-
import { ZRequiredUserColumns, ZUser, ZUserSession, ZUserCredentials } from './typings';
|
|
3
|
-
|
|
2
|
+
import { ZRequiredUserColumns, ZUser, ZUserCore, ZUserSession, ZUserCredentials, ZUserTableConfig } from './typings';
|
|
3
|
+
/**
|
|
4
|
+
* Generic User Service that can be extended with custom user fields
|
|
5
|
+
* @template TUser - The user type (defaults to ZUser for backward compatibility)
|
|
6
|
+
* @template TUserCreate - The user creation type (defaults to ZRequiredUserColumns)
|
|
7
|
+
*/
|
|
8
|
+
export declare class ZUserService<
|
|
9
|
+
TUser extends ZUserCore = ZUser,
|
|
10
|
+
TUserCreate extends ZRequiredUserColumns = ZRequiredUserColumns,
|
|
11
|
+
> {
|
|
4
12
|
private tableName;
|
|
5
13
|
private sqlService;
|
|
6
14
|
private salt;
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
protected tableConfig: ZUserTableConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new ZUserService instance
|
|
18
|
+
* @param options - Configuration options including SQL service and optional table configuration
|
|
19
|
+
*/
|
|
20
|
+
constructor({ sqlService, tableConfig }: { sqlService: ZSQLService; tableConfig?: ZUserTableConfig });
|
|
21
|
+
/**
|
|
22
|
+
* Gets the base table columns definition
|
|
23
|
+
* @returns SQL column definitions for core user fields
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
protected getBaseTableColumns(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Gets custom table columns if defined
|
|
29
|
+
* @returns SQL column definitions for custom fields
|
|
30
|
+
* @protected
|
|
31
|
+
*/
|
|
32
|
+
protected getCustomTableColumns(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Gets base table indexes
|
|
35
|
+
* @returns SQL index definitions for core fields
|
|
36
|
+
* @protected
|
|
37
|
+
*/
|
|
38
|
+
protected getBaseTableIndexes(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Gets custom table indexes if defined
|
|
41
|
+
* @returns SQL index definitions for custom fields
|
|
42
|
+
* @protected
|
|
43
|
+
*/
|
|
44
|
+
protected getCustomTableIndexes(): string;
|
|
45
|
+
protected checkTableExists(): Promise<boolean>;
|
|
9
46
|
checkTableHasAdmin(): Promise<boolean>;
|
|
10
|
-
|
|
47
|
+
protected createTable(): Promise<void>;
|
|
11
48
|
ensureTableExists(): Promise<void>;
|
|
12
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Registers a new user with extensible data
|
|
51
|
+
* @param userData - User data including core fields and any custom fields
|
|
52
|
+
* @returns Promise resolving to session information
|
|
53
|
+
*/
|
|
54
|
+
register(userData: TUserCreate): Promise<{
|
|
13
55
|
session: string;
|
|
14
56
|
}>;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
| {
|
|
22
|
-
user_id: number;
|
|
23
|
-
},
|
|
24
|
-
): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Gets all available columns for SELECT queries
|
|
59
|
+
* @returns Comma-separated list of column names
|
|
60
|
+
* @protected
|
|
61
|
+
*/
|
|
62
|
+
protected getSelectColumns(): string;
|
|
25
63
|
find(
|
|
26
64
|
opt:
|
|
27
65
|
| {
|
|
@@ -30,12 +68,13 @@ export declare class ZUserService {
|
|
|
30
68
|
| {
|
|
31
69
|
user_id: number;
|
|
32
70
|
},
|
|
33
|
-
): Promise<
|
|
71
|
+
): Promise<TUser | undefined>;
|
|
34
72
|
auth(opt: ZUserSession | ZUserCredentials): Promise<{
|
|
35
|
-
user?:
|
|
73
|
+
user?: TUser;
|
|
36
74
|
session?: string;
|
|
37
75
|
authenticated: boolean;
|
|
38
76
|
}>;
|
|
77
|
+
fetch(opt?: { limit?: number }): Promise<TUser[]>;
|
|
39
78
|
private genSession;
|
|
40
79
|
private hashPass;
|
|
41
80
|
}
|
package/lib/user_service.js
CHANGED
|
@@ -2,12 +2,79 @@
|
|
|
2
2
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
3
|
exports.ZUserService = void 0;
|
|
4
4
|
const crypto_service_1 = require('./crypto_service');
|
|
5
|
+
/**
|
|
6
|
+
* Generic User Service that can be extended with custom user fields
|
|
7
|
+
* @template TUser - The user type (defaults to ZUser for backward compatibility)
|
|
8
|
+
* @template TUserCreate - The user creation type (defaults to ZRequiredUserColumns)
|
|
9
|
+
*/
|
|
5
10
|
class ZUserService {
|
|
6
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new ZUserService instance
|
|
13
|
+
* @param options - Configuration options including SQL service and optional table configuration
|
|
14
|
+
*/
|
|
15
|
+
constructor({ sqlService, tableConfig }) {
|
|
7
16
|
this.sqlService = sqlService;
|
|
8
|
-
this.
|
|
17
|
+
this.tableConfig = tableConfig || {};
|
|
18
|
+
this.tableName = this.tableConfig.tableName || 'users';
|
|
9
19
|
this.salt = sqlService.database;
|
|
10
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Gets the base table columns definition
|
|
23
|
+
* @returns SQL column definitions for core user fields
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
getBaseTableColumns() {
|
|
27
|
+
return `
|
|
28
|
+
\`user_id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
|
29
|
+
\`email\` varchar(255) NOT NULL,
|
|
30
|
+
\`role\` varchar(64) DEFAULT NULL,
|
|
31
|
+
\`pass\` varchar(512) NOT NULL,
|
|
32
|
+
\`session\` varchar(512) NOT NULL,
|
|
33
|
+
\`admin\` tinyint(1) NOT NULL DEFAULT 0,
|
|
34
|
+
\`updated_at\` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
|
35
|
+
\`created_at\` datetime NOT NULL DEFAULT current_timestamp()
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets custom table columns if defined
|
|
40
|
+
* @returns SQL column definitions for custom fields
|
|
41
|
+
* @protected
|
|
42
|
+
*/
|
|
43
|
+
getCustomTableColumns() {
|
|
44
|
+
if (!this.tableConfig.customColumns) return '';
|
|
45
|
+
return Object.entries(this.tableConfig.customColumns)
|
|
46
|
+
.map(([columnName, definition]) => `\`${columnName}\` ${definition}`)
|
|
47
|
+
.join(',\n ');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Gets base table indexes
|
|
51
|
+
* @returns SQL index definitions for core fields
|
|
52
|
+
* @protected
|
|
53
|
+
*/
|
|
54
|
+
getBaseTableIndexes() {
|
|
55
|
+
return `
|
|
56
|
+
PRIMARY KEY (\`user_id\`),
|
|
57
|
+
UNIQUE KEY \`email_UNIQUE\` (\`email\`),
|
|
58
|
+
KEY \`email\` (\`email\`),
|
|
59
|
+
KEY \`role\` (\`role\`),
|
|
60
|
+
KEY \`admin\` (\`admin\`),
|
|
61
|
+
KEY \`created_at\` (\`created_at\`),
|
|
62
|
+
KEY \`updated_at\` (\`updated_at\`),
|
|
63
|
+
KEY \`session\` (\`session\`)
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets custom table indexes if defined
|
|
68
|
+
* @returns SQL index definitions for custom fields
|
|
69
|
+
* @protected
|
|
70
|
+
*/
|
|
71
|
+
getCustomTableIndexes() {
|
|
72
|
+
if (!this.tableConfig.customIndexes) return '';
|
|
73
|
+
return this.tableConfig.customIndexes
|
|
74
|
+
.map((index) => index.trim())
|
|
75
|
+
.filter((index) => index.length > 0)
|
|
76
|
+
.join(',\n ');
|
|
77
|
+
}
|
|
11
78
|
async checkTableExists() {
|
|
12
79
|
const res = await this.sqlService.query(`
|
|
13
80
|
SELECT ENGINE, VERSION, CREATE_TIME FROM information_schema.tables
|
|
@@ -23,23 +90,17 @@ class ZUserService {
|
|
|
23
90
|
return res.length > 0;
|
|
24
91
|
}
|
|
25
92
|
async createTable() {
|
|
93
|
+
const baseColumns = this.getBaseTableColumns();
|
|
94
|
+
const customColumns = this.getCustomTableColumns();
|
|
95
|
+
const baseIndexes = this.getBaseTableIndexes();
|
|
96
|
+
const customIndexes = this.getCustomTableIndexes();
|
|
97
|
+
const allColumns = customColumns ? `${baseColumns},\n ${customColumns}` : baseColumns;
|
|
98
|
+
const allIndexes = customIndexes ? `${baseIndexes},\n ${customIndexes}` : baseIndexes;
|
|
26
99
|
await this.sqlService.query(`
|
|
27
100
|
CREATE TABLE \`${this.tableName}\` (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
\`pass\` varchar(512) NOT NULL,
|
|
32
|
-
\`session\` varchar(512) NOT NULL,
|
|
33
|
-
\`admin\` tinyint(1) NOT NULL,
|
|
34
|
-
\`updated_at\` datetime NOT NULL DEFAULT current_timestamp(),
|
|
35
|
-
\`created_at\` datetime NOT NULL DEFAULT current_timestamp(),
|
|
36
|
-
PRIMARY KEY (\`user_id\`),
|
|
37
|
-
UNIQUE KEY \`email_UNIQUE\` (\`email\`),
|
|
38
|
-
KEY \`email\` (\`email\`),
|
|
39
|
-
KEY \`createdat\` (\`created_at\`),
|
|
40
|
-
KEY \`updatedat\` (\`updated_at\`),
|
|
41
|
-
KEY \`session\` (\`session\`)
|
|
42
|
-
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
|
|
101
|
+
${allColumns},
|
|
102
|
+
${allIndexes}
|
|
103
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
43
104
|
`);
|
|
44
105
|
}
|
|
45
106
|
async ensureTableExists() {
|
|
@@ -48,36 +109,49 @@ class ZUserService {
|
|
|
48
109
|
await this.createTable();
|
|
49
110
|
}
|
|
50
111
|
}
|
|
51
|
-
|
|
52
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Registers a new user with extensible data
|
|
114
|
+
* @param userData - User data including core fields and any custom fields
|
|
115
|
+
* @returns Promise resolving to session information
|
|
116
|
+
*/
|
|
117
|
+
async register(userData) {
|
|
118
|
+
const session = this.genSession({ email: userData.email, pass: userData.pass });
|
|
119
|
+
// Build dynamic SQL for insertion
|
|
120
|
+
const coreFields = ['email', 'pass', 'session', 'role', 'admin'];
|
|
121
|
+
const customFields = Object.keys(userData).filter((key) => !coreFields.includes(key) && key !== 'pass');
|
|
122
|
+
const allFields = [...coreFields, ...customFields];
|
|
123
|
+
const placeholders = allFields.map(() => '?').join(', ');
|
|
124
|
+
const fieldNames = allFields.map((field) => `\`${field}\``).join(', ');
|
|
125
|
+
const values = allFields.map((field) => {
|
|
126
|
+
if (field === 'pass') return this.hashPass({ email: userData.email, pass: userData.pass });
|
|
127
|
+
if (field === 'session') return session;
|
|
128
|
+
return userData[field];
|
|
129
|
+
});
|
|
53
130
|
await this.sqlService.query(
|
|
54
131
|
`
|
|
55
|
-
INSERT INTO \`${this.tableName}\` (
|
|
56
|
-
VALUES (
|
|
132
|
+
INSERT INTO \`${this.tableName}\` (${fieldNames})
|
|
133
|
+
VALUES (${placeholders})
|
|
57
134
|
`,
|
|
58
|
-
|
|
135
|
+
values,
|
|
59
136
|
);
|
|
60
137
|
return { session };
|
|
61
138
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
async exists(opt) {
|
|
73
|
-
const user = await this.find(opt);
|
|
74
|
-
return user !== undefined;
|
|
139
|
+
/**
|
|
140
|
+
* Gets all available columns for SELECT queries
|
|
141
|
+
* @returns Comma-separated list of column names
|
|
142
|
+
* @protected
|
|
143
|
+
*/
|
|
144
|
+
getSelectColumns() {
|
|
145
|
+
const baseColumns = ['user_id', 'email', 'session', 'role', 'admin', 'updated_at', 'created_at'];
|
|
146
|
+
const customColumns = this.tableConfig.customColumns ? Object.keys(this.tableConfig.customColumns) : [];
|
|
147
|
+
return [...baseColumns, ...customColumns].map((col) => `\`${col}\``).join(', ');
|
|
75
148
|
}
|
|
76
149
|
async find(opt) {
|
|
150
|
+
const selectColumns = this.getSelectColumns();
|
|
77
151
|
if (opt.email !== undefined) {
|
|
78
152
|
const rows = await this.sqlService.query(
|
|
79
153
|
`
|
|
80
|
-
SELECT
|
|
154
|
+
SELECT ${selectColumns} FROM \`${this.tableName}\`
|
|
81
155
|
WHERE email=?`,
|
|
82
156
|
[opt.email],
|
|
83
157
|
);
|
|
@@ -85,7 +159,7 @@ class ZUserService {
|
|
|
85
159
|
} else if (opt.user_id !== undefined) {
|
|
86
160
|
const rows = await this.sqlService.query(
|
|
87
161
|
`
|
|
88
|
-
SELECT
|
|
162
|
+
SELECT ${selectColumns} FROM \`${this.tableName}\`
|
|
89
163
|
WHERE user_id=?`,
|
|
90
164
|
[opt.user_id],
|
|
91
165
|
);
|
|
@@ -98,21 +172,35 @@ class ZUserService {
|
|
|
98
172
|
if (!opt.session && !opt.email && !opt.pass) {
|
|
99
173
|
return { authenticated: false };
|
|
100
174
|
}
|
|
175
|
+
const selectColumns = this.getSelectColumns();
|
|
101
176
|
const res = await (opt.session
|
|
102
177
|
? this.sqlService.query(
|
|
103
178
|
`
|
|
104
|
-
SELECT
|
|
179
|
+
SELECT ${selectColumns} FROM \`${this.tableName}\`
|
|
105
180
|
WHERE session=?`,
|
|
106
181
|
[opt.session],
|
|
107
182
|
)
|
|
108
183
|
: this.sqlService.query(
|
|
109
184
|
`
|
|
110
|
-
SELECT
|
|
185
|
+
SELECT ${selectColumns} FROM \`${this.tableName}\`
|
|
111
186
|
WHERE email=? AND pass=?`,
|
|
112
187
|
[opt.email, this.hashPass(opt)],
|
|
113
188
|
));
|
|
114
189
|
return res.length === 0 ? { authenticated: false } : { user: res[0], session: res[0].session, authenticated: true };
|
|
115
190
|
}
|
|
191
|
+
async fetch(opt) {
|
|
192
|
+
const selectColumns = this.getSelectColumns();
|
|
193
|
+
const limit = opt?.limit || 100;
|
|
194
|
+
const rows = await this.sqlService.query(
|
|
195
|
+
`
|
|
196
|
+
SELECT ${selectColumns} FROM \`${this.tableName}\`
|
|
197
|
+
ORDER BY created_at DESC
|
|
198
|
+
LIMIT ?
|
|
199
|
+
`,
|
|
200
|
+
[limit],
|
|
201
|
+
);
|
|
202
|
+
return rows;
|
|
203
|
+
}
|
|
116
204
|
genSession({ email }) {
|
|
117
205
|
const salt = this.salt;
|
|
118
206
|
const data = email + Date.now() * Math.random();
|