@devopsplaybook.io/common-utils 1.10.1-beta.23.b1906d7 → 1.11.0-beta.24.d8f9aef
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/README.md +73 -27
- package/dist/src/ConfigBase.d.ts +31 -0
- package/dist/src/ConfigBase.js +58 -16
- package/dist/src/DbUtils.d.ts +20 -3
- package/dist/src/DbUtils.js +93 -2
- package/dist/src/DbUtilsNoTelemetry.d.ts +4 -1
- package/dist/src/DbUtilsNoTelemetry.js +62 -6
- package/dist/src/PostgresDbUtils.d.ts +41 -10
- package/dist/src/PostgresDbUtils.js +357 -304
- package/dist/src/SqlDbUtils.d.ts +12 -4
- package/dist/src/SqlDbUtils.js +76 -30
- package/dist/src/users/Auth.d.ts +11 -1
- package/dist/src/users/Auth.js +160 -44
- package/dist/src/users/User.d.ts +10 -0
- package/dist/src/users/User.js +30 -10
- package/dist/src/users/UserApiToken.d.ts +4 -0
- package/dist/src/users/UserApiToken.js +11 -0
- package/dist/src/users/UsersApiTokensData.d.ts +12 -0
- package/dist/src/users/UsersApiTokensData.js +130 -33
- package/dist/src/users/UsersData.d.ts +20 -1
- package/dist/src/users/UsersData.js +150 -52
- package/dist/src/users/UsersRoutes.js +178 -61
- package/dist/src/users/index.d.ts +8 -0
- package/dist/src/users/index.js +24 -0
- package/package.json +58 -1
- package/.github/workflows/main-build.yml +0 -18
- package/.github/workflows/pr-check.yml +0 -27
- package/.github/workflows/reusable-merge-build.yml +0 -197
- package/.github/workflows/reusable-npm-merge.yml +0 -135
- package/.github/workflows/reusable-npm-pr.yml +0 -183
- package/.github/workflows/reusable-npm-upgrade.yml +0 -92
- package/.github/workflows/reusable-pr-verify.yml +0 -181
- package/AGENTS.md +0 -105
- package/index.ts +0 -18
- package/jest.config.js +0 -17
- package/prettierrc.json +0 -5
- package/src/ConfigBase.spec.ts +0 -108
- package/src/ConfigBase.ts +0 -297
- package/src/DbUtils.spec.ts +0 -23
- package/src/DbUtils.ts +0 -116
- package/src/DbUtilsNoTelemetry.spec.ts +0 -168
- package/src/DbUtilsNoTelemetry.ts +0 -117
- package/src/LLM.spec.ts +0 -303
- package/src/LLM.ts +0 -204
- package/src/Notifications.spec.ts +0 -265
- package/src/Notifications.ts +0 -201
- package/src/OTelContext.spec.ts +0 -58
- package/src/OTelContext.ts +0 -63
- package/src/PostgresDbUtils.spec.ts +0 -153
- package/src/PostgresDbUtils.ts +0 -666
- package/src/SqlDbUtils.spec.ts +0 -108
- package/src/SqlDbUtils.ts +0 -152
- package/src/SystemCommand.spec.ts +0 -18
- package/src/SystemCommand.ts +0 -23
- package/src/Timeout.spec.ts +0 -18
- package/src/Timeout.ts +0 -12
- package/src/users/Auth.spec.ts +0 -268
- package/src/users/Auth.ts +0 -202
- package/src/users/User.ts +0 -75
- package/src/users/UserApiToken.ts +0 -55
- package/src/users/UserPassword.spec.ts +0 -28
- package/src/users/UserPassword.ts +0 -20
- package/src/users/UserSession.ts +0 -9
- package/src/users/UsersApiTokensData.spec.ts +0 -158
- package/src/users/UsersApiTokensData.ts +0 -125
- package/src/users/UsersData.ts +0 -141
- package/src/users/UsersRoutes.ts +0 -374
- package/tsconfig.json +0 -15
- package/tsconfig.spec.json +0 -8
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UsersRoutes = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
|
-
const uuid_1 = require("uuid");
|
|
6
5
|
const Auth_1 = require("./Auth");
|
|
6
|
+
const DbUtils_1 = require("../DbUtils");
|
|
7
7
|
const User_1 = require("./User");
|
|
8
8
|
const UserApiToken_1 = require("./UserApiToken");
|
|
9
9
|
const UserPassword_1 = require("./UserPassword");
|
|
10
10
|
const UsersData_1 = require("./UsersData");
|
|
11
11
|
const UsersApiTokensData_1 = require("./UsersApiTokensData");
|
|
12
|
+
/** Maximum accepted API token name length. */
|
|
13
|
+
const API_TOKEN_NAME_MAX_LENGTH = 255;
|
|
12
14
|
/**
|
|
13
15
|
* Retrieves the OTel span attached to the request by the
|
|
14
16
|
* `@devopsplaybook.io/otel-utils-fastify` hooks.
|
|
@@ -16,6 +18,17 @@ const UsersApiTokensData_1 = require("./UsersApiTokensData");
|
|
|
16
18
|
function requestSpan(req) {
|
|
17
19
|
return req === null || req === void 0 ? void 0 : req.tracerSpanApi;
|
|
18
20
|
}
|
|
21
|
+
/** Parse an optional non-negative integer query parameter. */
|
|
22
|
+
function parseNonNegativeInt(value) {
|
|
23
|
+
if (value === undefined || value === null || value === "") {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const parsed = Number(value);
|
|
27
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
19
32
|
/**
|
|
20
33
|
* Standard user management routes shared across applications:
|
|
21
34
|
* initialization status, login (session), user CRUD and password changes.
|
|
@@ -30,14 +43,12 @@ class UsersRoutes {
|
|
|
30
43
|
async getRoutes(fastify) {
|
|
31
44
|
//
|
|
32
45
|
fastify.get("/status/initialization", async (req, res) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
res.status(201).send({ initialized: true });
|
|
38
|
-
}
|
|
46
|
+
const count = await (0, UsersData_1.UsersDataCount)(requestSpan(req));
|
|
47
|
+
return res.status(200).send({ initialized: count > 0 });
|
|
39
48
|
});
|
|
40
49
|
fastify.post("/session", async (req, res) => {
|
|
50
|
+
var _a;
|
|
51
|
+
const body = (_a = req.body) !== null && _a !== void 0 ? _a : {};
|
|
41
52
|
let user;
|
|
42
53
|
// From token
|
|
43
54
|
const userSession = await (0, Auth_1.AuthGetUserSession)(req);
|
|
@@ -54,17 +65,17 @@ class UsersRoutes {
|
|
|
54
65
|
});
|
|
55
66
|
}
|
|
56
67
|
// From User/Pass
|
|
57
|
-
if (!
|
|
68
|
+
if (!body.name) {
|
|
58
69
|
return res.status(400).send({ error: "Missing: Name" });
|
|
59
70
|
}
|
|
60
|
-
if (!
|
|
71
|
+
if (!body.password) {
|
|
61
72
|
return res.status(400).send({ error: "Missing: Password" });
|
|
62
73
|
}
|
|
63
|
-
user = await (0, UsersData_1.UsersDataGetByName)(requestSpan(req),
|
|
74
|
+
user = await (0, UsersData_1.UsersDataGetByName)(requestSpan(req), body.name);
|
|
64
75
|
if (!user) {
|
|
65
76
|
return res.status(403).send({ error: "Authentication Failed" });
|
|
66
77
|
}
|
|
67
|
-
else if (await (0, UserPassword_1.UserPasswordCheckPassword)(requestSpan(req), user,
|
|
78
|
+
else if (await (0, UserPassword_1.UserPasswordCheckPassword)(requestSpan(req), user, body.password)) {
|
|
68
79
|
return res.status(201).send({
|
|
69
80
|
success: true,
|
|
70
81
|
token: await (0, Auth_1.AuthGenerateJWT)(user),
|
|
@@ -77,58 +88,96 @@ class UsersRoutes {
|
|
|
77
88
|
});
|
|
78
89
|
// ==================== LIST USERS (Admin only) ====================
|
|
79
90
|
fastify.get("/", async (req, res) => {
|
|
91
|
+
var _a;
|
|
80
92
|
try {
|
|
81
93
|
await (0, Auth_1.AuthMustBeAdmin)(req, res);
|
|
82
94
|
}
|
|
83
95
|
catch {
|
|
84
96
|
return;
|
|
85
97
|
}
|
|
86
|
-
const
|
|
98
|
+
const query = ((_a = req.query) !== null && _a !== void 0 ? _a : {});
|
|
99
|
+
const limit = parseNonNegativeInt(query.limit);
|
|
100
|
+
const offset = parseNonNegativeInt(query.offset);
|
|
101
|
+
if (query.limit !== undefined && limit === undefined) {
|
|
102
|
+
return res.status(400).send({ error: "Invalid: limit" });
|
|
103
|
+
}
|
|
104
|
+
if (query.offset !== undefined && offset === undefined) {
|
|
105
|
+
return res.status(400).send({ error: "Invalid: offset" });
|
|
106
|
+
}
|
|
107
|
+
const users = await (0, UsersData_1.UsersDataList)(requestSpan(req), limit, offset);
|
|
87
108
|
return res.status(200).send(users.map((u) => u.toTransportJson()));
|
|
88
109
|
});
|
|
89
110
|
fastify.post("/", async (req, res) => {
|
|
111
|
+
var _a;
|
|
90
112
|
const context = requestSpan(req);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
const body = (_a = req.body) !== null && _a !== void 0 ? _a : {};
|
|
114
|
+
const createUser = async (isFirstUser) => {
|
|
115
|
+
if (!body.name) {
|
|
116
|
+
return res.status(400).send({ error: "Missing: Name" });
|
|
117
|
+
}
|
|
118
|
+
if (!body.password) {
|
|
119
|
+
return res.status(400).send({ error: "Missing: Password" });
|
|
120
|
+
}
|
|
121
|
+
if (await (0, UsersData_1.UsersDataGetByName)(context, body.name)) {
|
|
122
|
+
return res.status(400).send({ error: "Username Already Exists" });
|
|
123
|
+
}
|
|
124
|
+
const newUser = new User_1.User();
|
|
125
|
+
newUser.name = body.name;
|
|
126
|
+
// First user is always admin
|
|
127
|
+
if (isFirstUser) {
|
|
128
|
+
newUser.role = "admin";
|
|
129
|
+
newUser.scopes = [...User_1.User.ALL_SCOPES];
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
newUser.role = body.role === "admin" ? "admin" : "user";
|
|
133
|
+
if (body.scopes && Array.isArray(body.scopes)) {
|
|
134
|
+
newUser.scopes = body.scopes.filter((s) => User_1.User.ALL_SCOPES.includes(s));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
await (0, UserPassword_1.UserPasswordSetPassword)(context, newUser, body.password);
|
|
138
|
+
try {
|
|
139
|
+
await (0, UsersData_1.UsersDataAdd)(context, newUser);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Uniqueness also holds when the pre-check races another request
|
|
143
|
+
// (unique index on LOWER("name") – see README migration).
|
|
144
|
+
if ((0, UsersData_1.isUniqueViolationError)(error)) {
|
|
145
|
+
return res.status(400).send({ error: "Username Already Exists" });
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
return res.status(201).send({ user: newUser.toTransportJson() });
|
|
150
|
+
};
|
|
151
|
+
if ((await (0, UsersData_1.UsersDataCount)(context)) > 0) {
|
|
152
|
+
// If initialized, only admin can create users
|
|
97
153
|
try {
|
|
98
154
|
await (0, Auth_1.AuthMustBeAdmin)(req, res);
|
|
99
155
|
}
|
|
100
156
|
catch {
|
|
101
157
|
return;
|
|
102
158
|
}
|
|
159
|
+
return createUser(false);
|
|
103
160
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
if (await (0, UsersData_1.UsersDataGetByName)(context, req.body.name)) {
|
|
111
|
-
return res.status(400).send({ error: "Username Already Exists" });
|
|
112
|
-
}
|
|
113
|
-
const newUser = new User_1.User();
|
|
114
|
-
newUser.name = req.body.name;
|
|
115
|
-
// First user is always admin
|
|
116
|
-
if (isInitialized) {
|
|
117
|
-
newUser.role = req.body.role === "admin" ? "admin" : "user";
|
|
118
|
-
if (req.body.scopes && Array.isArray(req.body.scopes)) {
|
|
119
|
-
newUser.scopes = req.body.scopes.filter((s) => User_1.User.ALL_SCOPES.includes(s));
|
|
161
|
+
// Bootstrap: serialise the "no user yet → first admin" sequence across
|
|
162
|
+
// replicas (advisory lock on Postgres; single writer on SQLite).
|
|
163
|
+
return (0, DbUtils_1.DbUtilsWithLock)("users_bootstrap", async () => {
|
|
164
|
+
if ((await (0, UsersData_1.UsersDataCount)(context)) === 0) {
|
|
165
|
+
return createUser(true);
|
|
120
166
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
167
|
+
// Another replica bootstrapped first: require admin credentials.
|
|
168
|
+
try {
|
|
169
|
+
await (0, Auth_1.AuthMustBeAdmin)(req, res);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
return createUser(false);
|
|
175
|
+
});
|
|
129
176
|
});
|
|
130
177
|
fastify.put("/password", async (req, res) => {
|
|
178
|
+
var _a;
|
|
131
179
|
const context = requestSpan(req);
|
|
180
|
+
const body = (_a = req.body) !== null && _a !== void 0 ? _a : {};
|
|
132
181
|
const userSession = await (0, Auth_1.AuthGetUserSession)(req);
|
|
133
182
|
if (!userSession.isAuthenticated) {
|
|
134
183
|
return res.status(403).send({ error: "Access Denied" });
|
|
@@ -138,41 +187,75 @@ class UsersRoutes {
|
|
|
138
187
|
if (!user) {
|
|
139
188
|
return res.status(403).send({ error: "Access Denied" });
|
|
140
189
|
}
|
|
141
|
-
if (!
|
|
190
|
+
if (!body.password) {
|
|
142
191
|
return res.status(400).send({ error: "Missing: Password" });
|
|
143
192
|
}
|
|
144
|
-
if (!(await (0, UserPassword_1.UserPasswordCheckPassword)(context, user,
|
|
193
|
+
if (!(await (0, UserPassword_1.UserPasswordCheckPassword)(context, user, body.passwordOld))) {
|
|
145
194
|
return res.status(403).send({ error: "Old Password Wrong" });
|
|
146
195
|
}
|
|
147
|
-
await (0, UserPassword_1.UserPasswordSetPassword)(context, user,
|
|
196
|
+
await (0, UserPassword_1.UserPasswordSetPassword)(context, user, body.password);
|
|
148
197
|
await (0, UsersData_1.UsersDataUpdatePassword)(context, user);
|
|
149
|
-
|
|
198
|
+
if ((0, Auth_1.AuthJwtRevocationEnabled)()) {
|
|
199
|
+
await (0, UsersData_1.UsersDataBumpTokenVersion)(context, user.id);
|
|
200
|
+
}
|
|
201
|
+
return res.status(201).send({});
|
|
150
202
|
});
|
|
151
203
|
fastify.put("/:id", async (req, res) => {
|
|
204
|
+
var _a;
|
|
152
205
|
const context = requestSpan(req);
|
|
206
|
+
const body = (_a = req.body) !== null && _a !== void 0 ? _a : {};
|
|
153
207
|
try {
|
|
154
208
|
await (0, Auth_1.AuthMustBeAdmin)(req, res);
|
|
155
209
|
}
|
|
156
210
|
catch {
|
|
157
211
|
return;
|
|
158
212
|
}
|
|
213
|
+
// A body expecting nothing to change is malformed: answer 400, not 404/201
|
|
214
|
+
if (body.role === undefined &&
|
|
215
|
+
body.scopes === undefined &&
|
|
216
|
+
body.password === undefined) {
|
|
217
|
+
return res.status(400).send({ error: "Missing: Body" });
|
|
218
|
+
}
|
|
159
219
|
const user = await (0, UsersData_1.UsersDataGet)(context, req.params.id);
|
|
160
220
|
if (!user) {
|
|
161
221
|
return res.status(404).send({ error: "User Not Found" });
|
|
162
222
|
}
|
|
163
|
-
|
|
164
|
-
|
|
223
|
+
let changed = false;
|
|
224
|
+
if (body.role) {
|
|
225
|
+
const newRole = body.role === "admin" ? "admin" : "user";
|
|
226
|
+
if (newRole !== user.role) {
|
|
227
|
+
// At least 1 admin must remain (mirrors the delete guard)
|
|
228
|
+
if (user.role === "admin" &&
|
|
229
|
+
newRole === "user" &&
|
|
230
|
+
(await (0, UsersData_1.UsersDataCountAdmins)(context)) <= 1) {
|
|
231
|
+
return res
|
|
232
|
+
.status(400)
|
|
233
|
+
.send({ error: "At least 1 admin must be defined" });
|
|
234
|
+
}
|
|
235
|
+
user.role = newRole;
|
|
236
|
+
changed = true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (body.scopes && Array.isArray(body.scopes)) {
|
|
240
|
+
const newScopes = body.scopes.filter((s) => User_1.User.ALL_SCOPES.includes(s));
|
|
241
|
+
if (JSON.stringify(newScopes) !== JSON.stringify(user.scopes)) {
|
|
242
|
+
user.scopes = newScopes;
|
|
243
|
+
changed = true;
|
|
244
|
+
}
|
|
165
245
|
}
|
|
166
|
-
if (
|
|
167
|
-
|
|
246
|
+
if (changed) {
|
|
247
|
+
await (0, UsersData_1.UsersDataUpdateUser)(context, user);
|
|
168
248
|
}
|
|
169
|
-
await (0, UsersData_1.UsersDataUpdateUser)(context, user);
|
|
170
249
|
// If password change requested
|
|
171
|
-
if (
|
|
172
|
-
await (0, UserPassword_1.UserPasswordSetPassword)(context, user,
|
|
250
|
+
if (body.password) {
|
|
251
|
+
await (0, UserPassword_1.UserPasswordSetPassword)(context, user, body.password);
|
|
173
252
|
await (0, UsersData_1.UsersDataUpdatePassword)(context, user);
|
|
253
|
+
changed = true;
|
|
254
|
+
}
|
|
255
|
+
if (changed && (0, Auth_1.AuthJwtRevocationEnabled)()) {
|
|
256
|
+
await (0, UsersData_1.UsersDataBumpTokenVersion)(context, user.id);
|
|
174
257
|
}
|
|
175
|
-
res.status(201).send({ user: user.toTransportJson() });
|
|
258
|
+
return res.status(201).send({ user: user.toTransportJson() });
|
|
176
259
|
});
|
|
177
260
|
fastify.delete("/:id", async (req, res) => {
|
|
178
261
|
const context = requestSpan(req);
|
|
@@ -193,8 +276,8 @@ class UsersRoutes {
|
|
|
193
276
|
}
|
|
194
277
|
// Check that at least 1 admin remains
|
|
195
278
|
if (user.role === "admin") {
|
|
196
|
-
const admins =
|
|
197
|
-
if (admins
|
|
279
|
+
const admins = await (0, UsersData_1.UsersDataCountAdmins)(context);
|
|
280
|
+
if (admins <= 1) {
|
|
198
281
|
return res
|
|
199
282
|
.status(400)
|
|
200
283
|
.send({ error: "At least 1 admin must be defined" });
|
|
@@ -202,23 +285,57 @@ class UsersRoutes {
|
|
|
202
285
|
}
|
|
203
286
|
await (0, UsersData_1.UsersDataDelete)(context, req.params.id);
|
|
204
287
|
await (0, UsersApiTokensData_1.UsersApiTokensDataDeleteByUser)(context, req.params.id);
|
|
205
|
-
res.status(
|
|
288
|
+
return res.status(200).send({});
|
|
206
289
|
});
|
|
207
290
|
fastify.post("/tokens", async (req, res) => {
|
|
291
|
+
var _a;
|
|
208
292
|
const context = requestSpan(req);
|
|
293
|
+
const body = (_a = req.body) !== null && _a !== void 0 ? _a : {};
|
|
209
294
|
const userSession = await (0, Auth_1.AuthGetUserSession)(req);
|
|
210
295
|
if (!userSession.isAuthenticated) {
|
|
211
296
|
return res.status(403).send({ error: "Access Denied" });
|
|
212
297
|
}
|
|
213
|
-
if (!
|
|
298
|
+
if (!body.name || typeof body.name !== "string") {
|
|
214
299
|
return res.status(400).send({ error: "Missing: Name" });
|
|
215
300
|
}
|
|
301
|
+
if (body.name.length > API_TOKEN_NAME_MAX_LENGTH) {
|
|
302
|
+
return res.status(400).send({
|
|
303
|
+
error: `Name Too Long (max ${API_TOKEN_NAME_MAX_LENGTH} characters)`,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
let expiresAt = null;
|
|
307
|
+
if (body.expiresAt !== undefined && body.expiresAt !== null) {
|
|
308
|
+
const parsed = Date.parse(body.expiresAt);
|
|
309
|
+
if (!Number.isFinite(parsed)) {
|
|
310
|
+
return res
|
|
311
|
+
.status(400)
|
|
312
|
+
.send({ error: "Invalid: expiresAt (ISO 8601 date expected)" });
|
|
313
|
+
}
|
|
314
|
+
if (parsed <= Date.now()) {
|
|
315
|
+
return res
|
|
316
|
+
.status(400)
|
|
317
|
+
.send({ error: "Invalid: expiresAt must be in the future" });
|
|
318
|
+
}
|
|
319
|
+
if (!(await (0, UsersApiTokensData_1.UsersApiTokensDataSupportsOptionalColumns)(context))) {
|
|
320
|
+
return res.status(400).send({
|
|
321
|
+
error: "API token expiry requires the documented users_api_tokens migration",
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
expiresAt = new Date(parsed).toISOString();
|
|
325
|
+
}
|
|
326
|
+
const maxTokens = (0, Auth_1.AuthGetApiTokensMaxPerUser)();
|
|
327
|
+
if ((await (0, UsersApiTokensData_1.UsersApiTokensDataCountByUser)(context, userSession.userId)) >= maxTokens) {
|
|
328
|
+
return res
|
|
329
|
+
.status(400)
|
|
330
|
+
.send({ error: `Too many API tokens (max ${maxTokens})` });
|
|
331
|
+
}
|
|
216
332
|
const apiToken = new UserApiToken_1.UserApiToken();
|
|
217
|
-
apiToken.name =
|
|
333
|
+
apiToken.name = body.name;
|
|
218
334
|
// isAuthenticated implies userId is set
|
|
219
335
|
apiToken.userId = userSession.userId;
|
|
220
336
|
apiToken.dateCreated = new Date().toISOString();
|
|
221
|
-
|
|
337
|
+
apiToken.expiresAt = expiresAt;
|
|
338
|
+
const token = (0, crypto_1.randomBytes)(32).toString("base64url");
|
|
222
339
|
apiToken.tokenHash = (0, crypto_1.createHash)("sha256").update(token).digest("hex");
|
|
223
340
|
await (0, UsersApiTokensData_1.UsersApiTokensDataAdd)(context, apiToken);
|
|
224
341
|
// The plaintext token is returned once; only its hash is stored.
|
|
@@ -254,7 +371,7 @@ class UsersRoutes {
|
|
|
254
371
|
return res.status(403).send({ error: "Access Denied" });
|
|
255
372
|
}
|
|
256
373
|
await (0, UsersApiTokensData_1.UsersApiTokensDataDelete)(context, apiToken.id);
|
|
257
|
-
return res.status(
|
|
374
|
+
return res.status(200).send({});
|
|
258
375
|
});
|
|
259
376
|
}
|
|
260
377
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./User"), exports);
|
|
18
|
+
__exportStar(require("./UserSession"), exports);
|
|
19
|
+
__exportStar(require("./UserApiToken"), exports);
|
|
20
|
+
__exportStar(require("./Auth"), exports);
|
|
21
|
+
__exportStar(require("./UserPassword"), exports);
|
|
22
|
+
__exportStar(require("./UsersData"), exports);
|
|
23
|
+
__exportStar(require("./UsersApiTokensData"), exports);
|
|
24
|
+
__exportStar(require("./UsersRoutes"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devopsplaybook.io/common-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0-beta.24.d8f9aef",
|
|
4
4
|
"description": "Shared utility modules for devopsplaybook.io projects (DB, Config, OTel context, auth/users, notifications, LLM, system helpers)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Open Telemetry",
|
|
@@ -20,6 +20,63 @@
|
|
|
20
20
|
"type": "commonjs",
|
|
21
21
|
"main": "dist/index.js",
|
|
22
22
|
"types": "dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./package.json": "./package.json",
|
|
29
|
+
"./otel": {
|
|
30
|
+
"types": "./dist/src/OTelContext.d.ts",
|
|
31
|
+
"default": "./dist/src/OTelContext.js"
|
|
32
|
+
},
|
|
33
|
+
"./config": {
|
|
34
|
+
"types": "./dist/src/ConfigBase.d.ts",
|
|
35
|
+
"default": "./dist/src/ConfigBase.js"
|
|
36
|
+
},
|
|
37
|
+
"./db": {
|
|
38
|
+
"types": "./dist/src/DbUtils.d.ts",
|
|
39
|
+
"default": "./dist/src/DbUtils.js"
|
|
40
|
+
},
|
|
41
|
+
"./db/sqlite": {
|
|
42
|
+
"types": "./dist/src/SqlDbUtils.d.ts",
|
|
43
|
+
"default": "./dist/src/SqlDbUtils.js"
|
|
44
|
+
},
|
|
45
|
+
"./db/postgres": {
|
|
46
|
+
"types": "./dist/src/PostgresDbUtils.d.ts",
|
|
47
|
+
"default": "./dist/src/PostgresDbUtils.js"
|
|
48
|
+
},
|
|
49
|
+
"./db/no-telemetry": {
|
|
50
|
+
"types": "./dist/src/DbUtilsNoTelemetry.d.ts",
|
|
51
|
+
"default": "./dist/src/DbUtilsNoTelemetry.js"
|
|
52
|
+
},
|
|
53
|
+
"./users": {
|
|
54
|
+
"types": "./dist/src/users/index.d.ts",
|
|
55
|
+
"default": "./dist/src/users/index.js"
|
|
56
|
+
},
|
|
57
|
+
"./notifications": {
|
|
58
|
+
"types": "./dist/src/Notifications.d.ts",
|
|
59
|
+
"default": "./dist/src/Notifications.js"
|
|
60
|
+
},
|
|
61
|
+
"./llm": {
|
|
62
|
+
"types": "./dist/src/LLM.d.ts",
|
|
63
|
+
"default": "./dist/src/LLM.js"
|
|
64
|
+
},
|
|
65
|
+
"./system": {
|
|
66
|
+
"types": "./dist/src/SystemCommand.d.ts",
|
|
67
|
+
"default": "./dist/src/SystemCommand.js"
|
|
68
|
+
},
|
|
69
|
+
"./timeout": {
|
|
70
|
+
"types": "./dist/src/Timeout.d.ts",
|
|
71
|
+
"default": "./dist/src/Timeout.js"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"files": [
|
|
75
|
+
"dist"
|
|
76
|
+
],
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=22"
|
|
79
|
+
},
|
|
23
80
|
"scripts": {
|
|
24
81
|
"build": "tsc && tsc -p tsconfig.spec.json --noEmit",
|
|
25
82
|
"lint": "oxlint src",
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
name: Main Build
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: ["main"]
|
|
6
|
-
|
|
7
|
-
workflow_dispatch:
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
npm-merge:
|
|
11
|
-
uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-merge.yml@main
|
|
12
|
-
with:
|
|
13
|
-
npm_package_name: "@devopsplaybook.io/common-utils"
|
|
14
|
-
node_version: "22"
|
|
15
|
-
secrets:
|
|
16
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
17
|
-
QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
|
|
18
|
-
QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
name: PR Check
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches: ["main"]
|
|
6
|
-
|
|
7
|
-
workflow_dispatch:
|
|
8
|
-
|
|
9
|
-
concurrency:
|
|
10
|
-
group: pr-${{ github.event.pull_request.number || github.ref }}
|
|
11
|
-
cancel-in-progress: true
|
|
12
|
-
|
|
13
|
-
permissions:
|
|
14
|
-
contents: read
|
|
15
|
-
pull-requests: write
|
|
16
|
-
issues: write
|
|
17
|
-
|
|
18
|
-
jobs:
|
|
19
|
-
npm-pr:
|
|
20
|
-
uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-pr.yml@main
|
|
21
|
-
with:
|
|
22
|
-
npm_package_name: "@devopsplaybook.io/common-utils"
|
|
23
|
-
node_version: "22"
|
|
24
|
-
secrets:
|
|
25
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
26
|
-
QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
|
|
27
|
-
QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
|