omegga 1.5.2 → 1.6.0
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/CHANGELOG.md +19 -2
- package/bin/omegga +1 -1
- package/dist/cli/terminal.js +46 -0
- package/dist/cli/terminal.js.map +1 -1
- package/dist/omegga/commandInjector.js +13 -6
- package/dist/omegga/commandInjector.js.map +1 -1
- package/dist/omegga/player.js +2 -2
- package/dist/omegga/player.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/webserver/backend/database.js +219 -7
- package/dist/webserver/backend/database.js.map +1 -1
- package/dist/webserver/backend/events.js +1 -1
- package/dist/webserver/backend/events.js.map +1 -1
- package/dist/webserver/backend/index.js +120 -2
- package/dist/webserver/backend/index.js.map +1 -1
- package/dist/webserver/backend/permissions.js +74 -0
- package/dist/webserver/backend/permissions.js.map +1 -0
- package/dist/webserver/backend/router/chat.js +9 -7
- package/dist/webserver/backend/router/chat.js.map +1 -1
- package/dist/webserver/backend/router/index.js +3 -1
- package/dist/webserver/backend/router/index.js.map +1 -1
- package/dist/webserver/backend/router/mfa.js +162 -0
- package/dist/webserver/backend/router/mfa.js.map +1 -0
- package/dist/webserver/backend/router/player.js +7 -6
- package/dist/webserver/backend/router/player.js.map +1 -1
- package/dist/webserver/backend/router/plugin.js +14 -10
- package/dist/webserver/backend/router/plugin.js.map +1 -1
- package/dist/webserver/backend/router/role.js +2 -1
- package/dist/webserver/backend/router/role.js.map +1 -1
- package/dist/webserver/backend/router/server.js +85 -71
- package/dist/webserver/backend/router/server.js.map +1 -1
- package/dist/webserver/backend/router/session.js +11 -2
- package/dist/webserver/backend/router/session.js.map +1 -1
- package/dist/webserver/backend/router/user.js +175 -13
- package/dist/webserver/backend/router/user.js.map +1 -1
- package/dist/webserver/backend/router/world.js +10 -9
- package/dist/webserver/backend/router/world.js.map +1 -1
- package/dist/webserver/backend/scopes.js +303 -0
- package/dist/webserver/backend/scopes.js.map +1 -0
- package/dist/webserver/backend/totp.js +32 -0
- package/dist/webserver/backend/totp.js.map +1 -0
- package/dist/webserver/backend/trpc.js +47 -4
- package/dist/webserver/backend/trpc.js.map +1 -1
- package/doc/permissions.md +193 -0
- package/package.json +7 -2
- package/public/app.html +932 -526
- package/public/assets/IconShieldCheck-Bjlm8mX2.js +2 -0
- package/public/assets/IconShieldCheck-Bjlm8mX2.js.map +1 -0
- package/public/assets/IconWorld-XmQNg6JI.js +2 -0
- package/public/assets/IconWorld-XmQNg6JI.js.map +1 -0
- package/public/assets/IconWorldPlus-CxGGv2eD.js +2 -0
- package/public/assets/IconWorldPlus-CxGGv2eD.js.map +1 -0
- package/public/assets/app-DMr_-wik.js +7 -0
- package/public/assets/app-DMr_-wik.js.map +1 -0
- package/public/assets/auth-CEYiizOt.js +2 -0
- package/public/assets/auth-CEYiizOt.js.map +1 -0
- package/public/assets/index-BDkarAkX.js +3 -0
- package/public/assets/index-BDkarAkX.js.map +1 -0
- package/public/assets/{index-2cgXLhrt.js → index-DEL-PD_8.js} +2 -2
- package/public/assets/{index-2cgXLhrt.js.map → index-DEL-PD_8.js.map} +1 -1
- package/public/assets/index-D_STNela.js +2 -0
- package/public/assets/index-D_STNela.js.map +1 -0
- package/public/assets/index-Dkh3HiBz.js +4 -0
- package/public/assets/index-Dkh3HiBz.js.map +1 -0
- package/public/assets/shared/@components-CgsSYMe6.js +2 -0
- package/public/assets/shared/@components-CgsSYMe6.js.map +1 -0
- package/public/assets/shared/@hooks-DT93N7wd.js +2 -0
- package/public/assets/shared/{@hooks-Ch_hJYh0.js.map → @hooks-DT93N7wd.js.map} +1 -1
- package/public/assets/shared/@tabler_icons-react-DrVFX3uL.js +3 -0
- package/public/assets/shared/{@tabler_icons-react-iQk11eD_.js.map → @tabler_icons-react-DrVFX3uL.js.map} +1 -1
- package/public/assets/shared/{wouter-CUC1caxu.js → wouter-CNISJ5i6.js} +2 -2
- package/public/assets/shared/wouter-CNISJ5i6.js.map +1 -0
- package/public/assets/useHasScope-BOdFktko.js +2 -0
- package/public/assets/useHasScope-BOdFktko.js.map +1 -0
- package/public/auth.html +97 -33
- package/templates/safe-ts/omegga.d.ts +1 -1
- package/public/assets/IconArrowRight-BuVxhqKX.js +0 -2
- package/public/assets/IconArrowRight-BuVxhqKX.js.map +0 -1
- package/public/assets/IconWorld-B6GK5qpx.js +0 -2
- package/public/assets/IconWorld-B6GK5qpx.js.map +0 -1
- package/public/assets/IconWorldPlus-Dkos72Ws.js +0 -2
- package/public/assets/IconWorldPlus-Dkos72Ws.js.map +0 -1
- package/public/assets/app-CLZ-5FIi.js +0 -6
- package/public/assets/app-CLZ-5FIi.js.map +0 -1
- package/public/assets/auth-6N37o421.js +0 -2
- package/public/assets/auth-6N37o421.js.map +0 -1
- package/public/assets/index-DLZ8-XzO.js +0 -4
- package/public/assets/index-DLZ8-XzO.js.map +0 -1
- package/public/assets/index-DY750sgZ.js +0 -2
- package/public/assets/index-DY750sgZ.js.map +0 -1
- package/public/assets/shared/@components-DPHARroA.js +0 -2
- package/public/assets/shared/@components-DPHARroA.js.map +0 -1
- package/public/assets/shared/@hooks-Ch_hJYh0.js +0 -2
- package/public/assets/shared/@tabler_icons-react-iQk11eD_.js +0 -3
- package/public/assets/shared/wouter-CUC1caxu.js.map +0 -1
|
@@ -1,10 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
2
24
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
25
|
const v4 = require("zod/v4");
|
|
26
|
+
const permissions = require("../permissions.js");
|
|
27
|
+
const scopes = require("../scopes.js");
|
|
4
28
|
const trpc = require("../trpc.js");
|
|
29
|
+
const PermissionSetSchema = v4.z.object({
|
|
30
|
+
root: v4.z.enum(["all", "read", "off"]),
|
|
31
|
+
domains: v4.z.record(v4.z.string(), v4.z.enum(["all", "read", "none"])),
|
|
32
|
+
scopes: v4.z.record(v4.z.string(), v4.z.boolean())
|
|
33
|
+
});
|
|
5
34
|
const userRouter = trpc.router({
|
|
6
35
|
user: trpc.router({
|
|
7
|
-
list: trpc.protectedProcedure().input(
|
|
36
|
+
list: trpc.protectedProcedure(scopes.ScopeName.UserList).input(
|
|
8
37
|
v4.z.object({
|
|
9
38
|
page: v4.z.number().optional().default(0),
|
|
10
39
|
search: v4.z.string().optional().default(""),
|
|
@@ -16,15 +45,42 @@ const userRouter = trpc.router({
|
|
|
16
45
|
const { page, search, sort, direction } = input;
|
|
17
46
|
const resp = await database.getUsers({ page, search, sort, direction });
|
|
18
47
|
const now = Date.now();
|
|
19
|
-
resp.users = resp.users.map(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
48
|
+
resp.users = resp.users.map(
|
|
49
|
+
({
|
|
50
|
+
hash: _h,
|
|
51
|
+
totpSecret: _ts,
|
|
52
|
+
recoveryCodes: _rc,
|
|
53
|
+
passkeys: _pk,
|
|
54
|
+
permissions: _perms,
|
|
55
|
+
...user
|
|
56
|
+
}) => ({
|
|
57
|
+
...user,
|
|
58
|
+
hash: "",
|
|
59
|
+
permissions: permissions.decodePermissions(_perms),
|
|
60
|
+
totpEnabled: user.totpEnabled ?? false,
|
|
61
|
+
passkeyCount: _pk?.length ?? 0,
|
|
62
|
+
seenAgo: user.lastOnline ? now - user.lastOnline : Infinity,
|
|
63
|
+
createdAgo: now - user.created
|
|
64
|
+
})
|
|
65
|
+
);
|
|
25
66
|
return resp;
|
|
26
67
|
}),
|
|
27
|
-
|
|
68
|
+
self: trpc.protectedProcedure(scopes.ScopeName.SessionInfo).query(async ({ ctx }) => {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
return {
|
|
71
|
+
username: ctx.user.username || "Admin",
|
|
72
|
+
isOwner: ctx.user.isOwner,
|
|
73
|
+
isBanned: ctx.user.isBanned ?? false,
|
|
74
|
+
lastOnline: ctx.user.lastOnline ?? 0,
|
|
75
|
+
created: ctx.user.created ?? 0,
|
|
76
|
+
seenAgo: ctx.user.lastOnline ? now - ctx.user.lastOnline : Infinity,
|
|
77
|
+
createdAgo: now - (ctx.user.created ?? now),
|
|
78
|
+
permissions: ctx.user.permissions,
|
|
79
|
+
totpEnabled: ctx.user.totpEnabled ?? false,
|
|
80
|
+
passkeyCount: ctx.user.passkeys?.length ?? 0
|
|
81
|
+
};
|
|
82
|
+
}),
|
|
83
|
+
create: trpc.protectedProcedure(scopes.ScopeName.UserCreate).input(
|
|
28
84
|
v4.z.object({
|
|
29
85
|
username: v4.z.string(),
|
|
30
86
|
password: v4.z.string()
|
|
@@ -33,7 +89,6 @@ const userRouter = trpc.router({
|
|
|
33
89
|
const { database } = trpc.getContextDeps();
|
|
34
90
|
const { username, password } = input;
|
|
35
91
|
const { log, error } = ctx;
|
|
36
|
-
if (!ctx.user.isOwner) return "missing permission";
|
|
37
92
|
if (typeof username !== "string" || typeof password !== "string")
|
|
38
93
|
return "username/password not a string";
|
|
39
94
|
if (!username.match(/^\w{0,32}$/)) return "username is not allowed";
|
|
@@ -62,17 +117,29 @@ const userRouter = trpc.router({
|
|
|
62
117
|
log(`created new user "${username.yellow}"`);
|
|
63
118
|
return "";
|
|
64
119
|
}),
|
|
65
|
-
|
|
120
|
+
// self-service: any authenticated user can change their own password
|
|
121
|
+
// changing another user's password requires user.passwd permission (enforced here)
|
|
122
|
+
passwd: trpc.protectedProcedure(scopes.ScopeName.SessionInfo).input(
|
|
66
123
|
v4.z.object({
|
|
67
124
|
username: v4.z.string(),
|
|
68
|
-
password: v4.z.string()
|
|
125
|
+
password: v4.z.string(),
|
|
126
|
+
currentPassword: v4.z.string().optional()
|
|
69
127
|
})
|
|
70
128
|
).mutation(async ({ input, ctx }) => {
|
|
71
129
|
const { database } = trpc.getContextDeps();
|
|
72
130
|
const { username, password } = input;
|
|
73
131
|
const { log, error } = ctx;
|
|
74
|
-
|
|
75
|
-
|
|
132
|
+
const isSelf = username === ctx.user.username;
|
|
133
|
+
if (isSelf) {
|
|
134
|
+
if (!input.currentPassword) return "current password required";
|
|
135
|
+
const bcryptLib = await import("bcryptjs");
|
|
136
|
+
if (!await bcryptLib.compare(input.currentPassword, ctx.user.hash))
|
|
137
|
+
return "incorrect current password";
|
|
138
|
+
} else {
|
|
139
|
+
const defaults = await database.getDefaultPermissions();
|
|
140
|
+
if (!permissions.userHasScope(ctx.user, scopes.ScopeName.UserPasswd, defaults))
|
|
141
|
+
return "missing permission";
|
|
142
|
+
}
|
|
76
143
|
if (typeof username !== "string" || typeof password !== "string")
|
|
77
144
|
return "username/password not a string";
|
|
78
145
|
if (!username.match(/^\w{0,32}$/)) return "username is not allowed";
|
|
@@ -86,6 +153,101 @@ const userRouter = trpc.router({
|
|
|
86
153
|
}
|
|
87
154
|
log(`changed password for "${username.yellow}"`);
|
|
88
155
|
return "";
|
|
156
|
+
}),
|
|
157
|
+
ban: trpc.protectedProcedure(scopes.ScopeName.UserBan).input(
|
|
158
|
+
v4.z.object({
|
|
159
|
+
username: v4.z.string(),
|
|
160
|
+
banned: v4.z.boolean()
|
|
161
|
+
})
|
|
162
|
+
).mutation(async ({ input, ctx }) => {
|
|
163
|
+
const { database } = trpc.getContextDeps();
|
|
164
|
+
const { username, banned } = input;
|
|
165
|
+
const { log } = ctx;
|
|
166
|
+
if (username === ctx.user.username) return "cannot disable yourself";
|
|
167
|
+
if (!await database.userExists(username))
|
|
168
|
+
return "user does not exist";
|
|
169
|
+
const target = await database.stores.users.findOne({ type: "user", username });
|
|
170
|
+
if (target?.isOwner) return "cannot disable the owner";
|
|
171
|
+
await database.banUser(username, banned);
|
|
172
|
+
log(`${banned ? "disabled" : "enabled"} user "${username.yellow}"`);
|
|
173
|
+
return "";
|
|
174
|
+
}),
|
|
175
|
+
delete: trpc.protectedProcedure(scopes.ScopeName.UserDelete).input(
|
|
176
|
+
v4.z.object({
|
|
177
|
+
username: v4.z.string()
|
|
178
|
+
})
|
|
179
|
+
).mutation(async ({ input, ctx }) => {
|
|
180
|
+
const { database } = trpc.getContextDeps();
|
|
181
|
+
const { username } = input;
|
|
182
|
+
const { log } = ctx;
|
|
183
|
+
if (username === ctx.user.username) return "cannot delete yourself";
|
|
184
|
+
if (!await database.userExists(username))
|
|
185
|
+
return "user does not exist";
|
|
186
|
+
const target = await database.stores.users.findOne({ type: "user", username });
|
|
187
|
+
if (target?.isOwner) return "cannot delete the owner";
|
|
188
|
+
await database.deleteUser(username);
|
|
189
|
+
log(`deleted user "${username.yellow}"`);
|
|
190
|
+
return "";
|
|
191
|
+
}),
|
|
192
|
+
permissions: trpc.protectedProcedure(scopes.ScopeName.UserPermissions).input(
|
|
193
|
+
v4.z.object({
|
|
194
|
+
username: v4.z.string(),
|
|
195
|
+
permissions: PermissionSetSchema
|
|
196
|
+
})
|
|
197
|
+
).mutation(async ({ input, ctx }) => {
|
|
198
|
+
const { database } = trpc.getContextDeps();
|
|
199
|
+
const { username } = input;
|
|
200
|
+
const permissions$1 = input.permissions;
|
|
201
|
+
const { log } = ctx;
|
|
202
|
+
if (username === ctx.user.username)
|
|
203
|
+
return "cannot edit own permissions";
|
|
204
|
+
const target = await database.stores.users.findOne({ type: "user", username });
|
|
205
|
+
if (!target) return "user does not exist";
|
|
206
|
+
if (target.isOwner) return "cannot edit the owner";
|
|
207
|
+
if (!ctx.user.isOwner) {
|
|
208
|
+
const defaults = await database.getDefaultPermissions();
|
|
209
|
+
const myScopes = permissions.resolveAllScopes(ctx.user.permissions, defaults);
|
|
210
|
+
const grantedScopes = permissions.resolveAllScopes(permissions$1, defaults);
|
|
211
|
+
for (const [scope, granted] of Object.entries(grantedScopes)) {
|
|
212
|
+
if (granted && !myScopes[scope])
|
|
213
|
+
return `cannot grant permission you do not have: ${scope}`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
await database.setUserPermissions(username, permissions$1);
|
|
217
|
+
log(`updated permissions for "${username.yellow}"`);
|
|
218
|
+
return "";
|
|
219
|
+
}),
|
|
220
|
+
defaultPermissions: trpc.router({
|
|
221
|
+
get: trpc.protectedProcedure(scopes.ScopeName.UserPermissions).query(async () => {
|
|
222
|
+
const { database } = trpc.getContextDeps();
|
|
223
|
+
const defaults = await database.getDefaultPermissions();
|
|
224
|
+
return {
|
|
225
|
+
root: defaults.root,
|
|
226
|
+
domains: defaults.domains,
|
|
227
|
+
scopes: defaults.scopes
|
|
228
|
+
};
|
|
229
|
+
}),
|
|
230
|
+
set: trpc.protectedProcedure(scopes.ScopeName.UserPermissions).input(PermissionSetSchema).mutation(async ({ input, ctx }) => {
|
|
231
|
+
const { database } = trpc.getContextDeps();
|
|
232
|
+
const { log } = ctx;
|
|
233
|
+
await database.setDefaultPermissions(
|
|
234
|
+
input
|
|
235
|
+
);
|
|
236
|
+
log(`updated default permissions`);
|
|
237
|
+
return "";
|
|
238
|
+
})
|
|
239
|
+
}),
|
|
240
|
+
resetMfa: trpc.protectedProcedure(scopes.ScopeName.UserResetMfa).input(v4.z.object({ username: v4.z.string() })).mutation(async ({ input, ctx }) => {
|
|
241
|
+
const { database } = trpc.getContextDeps();
|
|
242
|
+
const { username } = input;
|
|
243
|
+
const { log } = ctx;
|
|
244
|
+
if (username === ctx.user.username) return "cannot reset own MFA";
|
|
245
|
+
const target = await database.stores.users.findOne({ type: "user", username });
|
|
246
|
+
if (!target) return "user does not exist";
|
|
247
|
+
if (target.isOwner) return "cannot reset the owner";
|
|
248
|
+
await database.resetUserMfa(username);
|
|
249
|
+
log(`reset MFA for "${username.yellow}"`);
|
|
250
|
+
return "";
|
|
89
251
|
})
|
|
90
252
|
})
|
|
91
253
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.js","sources":["../../../../src/webserver/backend/router/user.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { router, protectedProcedure, getContextDeps } from '../trpc';\n\nexport const userRouter = router({\n user: router({\n list: protectedProcedure('user.list')\n .input(\n z\n .object({\n page: z.number().optional().default(0),\n search: z.string().optional().default(''),\n sort: z.string().optional().default('name'),\n direction: z.number().optional().default(1),\n })\n .optional()\n .default({ page: 0, search: '', sort: 'name', direction: 1 }),\n )\n .query(async ({ input }) => {\n const { database } = getContextDeps();\n const { page, search, sort, direction } = input;\n const resp = await database.getUsers({ page, search, sort, direction });\n const now = Date.now();\n resp.users = resp.users.map(({ hash: _, ...user }) => ({\n ...user,\n hash: '',\n seenAgo: user.lastOnline ? now - user.lastOnline : Infinity,\n createdAgo: now - user.created,\n }));\n return resp;\n }),\n\n create: protectedProcedure('user.create')\n .input(\n z.object({\n username: z.string(),\n password: z.string(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username, password } = input;\n const { log, error } = ctx;\n\n if (!ctx.user.isOwner) return 'missing permission';\n if (typeof username !== 'string' || typeof password !== 'string')\n return 'username/password not a string';\n if (!username.match(/^\\w{0,32}$/)) return 'username is not allowed';\n if (password.length === 0 || password.length > 128)\n return 'invalid password size';\n\n if (ctx.user.isOwner && ctx.user.username === '') {\n try {\n await database.stores.users.update(\n { _id: ctx.user._id },\n { username, hash: await database.hash(password) },\n );\n } catch (e) {\n error('error setting owner password', e);\n return 'error setting owner password';\n }\n log(`created account as \"${username.yellow}\"`);\n return '';\n }\n\n if (await database.userExists(username)) return 'user already exists';\n\n try {\n await database.createUser(username, password);\n } catch (e) {\n error('error creating new user', e);\n return 'error creating new user';\n }\n log(`created new user \"${username.yellow}\"`);\n return '';\n }),\n\n passwd: protectedProcedure('user.passwd')\n .input(\n z.object({\n username: z.string(),\n password: z.string(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username, password } = input;\n const { log, error } = ctx;\n\n if (!ctx.user.isOwner && username !== ctx.user.username)\n return 'missing permission';\n if (typeof username !== 'string' || typeof password !== 'string')\n return 'username/password not a string';\n if (!username.match(/^\\w{0,32}$/)) return 'username is not allowed';\n if (!(await database.userExists(username)))\n return 'user does not exist';\n\n try {\n await database.userPasswd(username, password);\n } catch (e) {\n error('error setting user password', e);\n return \"error setting user's password\";\n }\n log(`changed password for \"${username.yellow}\"`);\n return '';\n }),\n }),\n});\n"],"names":["router","protectedProcedure","z","getContextDeps"],"mappings":";;;;AAGO,MAAM,aAAaA,KAAAA,OAAO;AAAA,EAC/B,MAAMA,KAAAA,OAAO;AAAA,IACX,MAAMC,KAAAA,mBAA8B,EACjC;AAAA,MACCC,GAAAA,EACG,OAAO;AAAA,QACN,MAAMA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,CAAC;AAAA,QACrC,QAAQA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,EAAE;AAAA,QACxC,MAAMA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,MAAM;AAAA,QAC1C,WAAWA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,CAAC;AAAA,MAAA,CAC3C,EACA,SAAA,EACA,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,MAAM,QAAQ,WAAW,GAAG;AAAA,IAAA,EAE/D,MAAM,OAAO,EAAE,YAAY;AAC1B,YAAM,EAAE,SAAA,IAAaC,oBAAA;AACrB,YAAM,EAAE,MAAM,QAAQ,MAAM,cAAc;AAC1C,YAAM,OAAO,MAAM,SAAS,SAAS,EAAE,MAAM,QAAQ,MAAM,WAAW;AACtE,YAAM,MAAM,KAAK,IAAA;AACjB,WAAK,QAAQ,KAAK,MAAM,IAAI,CAAC,EAAE,MAAM,GAAG,GAAG,YAAY;AAAA,QACrD,GAAG;AAAA,QACH,MAAM;AAAA,QACN,SAAS,KAAK,aAAa,MAAM,KAAK,aAAa;AAAA,QACnD,YAAY,MAAM,KAAK;AAAA,MAAA,EACvB;AACF,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,QAAQF,KAAAA,mBAAgC,EACrC;AAAA,MACCC,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,UAAUA,GAAAA,EAAE,OAAA;AAAA,MAAO,CACpB;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaC,oBAAA;AACrB,YAAM,EAAE,UAAU,SAAA,IAAa;AAC/B,YAAM,EAAE,KAAK,MAAA,IAAU;AAEvB,UAAI,CAAC,IAAI,KAAK,QAAS,QAAO;AAC9B,UAAI,OAAO,aAAa,YAAY,OAAO,aAAa;AACtD,eAAO;AACT,UAAI,CAAC,SAAS,MAAM,YAAY,EAAG,QAAO;AAC1C,UAAI,SAAS,WAAW,KAAK,SAAS,SAAS;AAC7C,eAAO;AAET,UAAI,IAAI,KAAK,WAAW,IAAI,KAAK,aAAa,IAAI;AAChD,YAAI;AACF,gBAAM,SAAS,OAAO,MAAM;AAAA,YAC1B,EAAE,KAAK,IAAI,KAAK,IAAA;AAAA,YAChB,EAAE,UAAU,MAAM,MAAM,SAAS,KAAK,QAAQ,EAAA;AAAA,UAAE;AAAA,QAEpD,SAAS,GAAG;AACV,gBAAM,gCAAgC,CAAC;AACvC,iBAAO;AAAA,QACT;AACA,YAAI,uBAAuB,SAAS,MAAM,GAAG;AAC7C,eAAO;AAAA,MACT;AAEA,UAAI,MAAM,SAAS,WAAW,QAAQ,EAAG,QAAO;AAEhD,UAAI;AACF,cAAM,SAAS,WAAW,UAAU,QAAQ;AAAA,MAC9C,SAAS,GAAG;AACV,cAAM,2BAA2B,CAAC;AAClC,eAAO;AAAA,MACT;AACA,UAAI,qBAAqB,SAAS,MAAM,GAAG;AAC3C,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,QAAQF,KAAAA,mBAAgC,EACrC;AAAA,MACCC,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,UAAUA,GAAAA,EAAE,OAAA;AAAA,MAAO,CACpB;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaC,oBAAA;AACrB,YAAM,EAAE,UAAU,SAAA,IAAa;AAC/B,YAAM,EAAE,KAAK,MAAA,IAAU;AAEvB,UAAI,CAAC,IAAI,KAAK,WAAW,aAAa,IAAI,KAAK;AAC7C,eAAO;AACT,UAAI,OAAO,aAAa,YAAY,OAAO,aAAa;AACtD,eAAO;AACT,UAAI,CAAC,SAAS,MAAM,YAAY,EAAG,QAAO;AAC1C,UAAI,CAAE,MAAM,SAAS,WAAW,QAAQ;AACtC,eAAO;AAET,UAAI;AACF,cAAM,SAAS,WAAW,UAAU,QAAQ;AAAA,MAC9C,SAAS,GAAG;AACV,cAAM,+BAA+B,CAAC;AACtC,eAAO;AAAA,MACT;AACA,UAAI,yBAAyB,SAAS,MAAM,GAAG;AAC/C,aAAO;AAAA,IACT,CAAC;AAAA,EAAA,CACJ;AACH,CAAC;;"}
|
|
1
|
+
{"version":3,"file":"user.js","sources":["../../../../src/webserver/backend/router/user.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport {\n decodePermissions,\n resolveAllScopes,\n userHasScope,\n type PermissionSet,\n} from '../permissions';\nimport { ScopeName } from '../scopes';\nimport { router, protectedProcedure, getContextDeps } from '../trpc';\nimport type { IStoreUser } from '../types';\n\nconst PermissionSetSchema = z.object({\n root: z.enum(['all', 'read', 'off']),\n domains: z.record(z.string(), z.enum(['all', 'read', 'none'])),\n scopes: z.record(z.string(), z.boolean()),\n});\n\nexport const userRouter = router({\n user: router({\n list: protectedProcedure(ScopeName.UserList)\n .input(\n z\n .object({\n page: z.number().optional().default(0),\n search: z.string().optional().default(''),\n sort: z.string().optional().default('name'),\n direction: z.number().optional().default(1),\n })\n .optional()\n .default({ page: 0, search: '', sort: 'name', direction: 1 }),\n )\n .query(async ({ input }) => {\n const { database } = getContextDeps();\n const { page, search, sort, direction } = input;\n const resp = await database.getUsers({ page, search, sort, direction });\n const now = Date.now();\n resp.users = resp.users.map(\n ({\n hash: _h,\n totpSecret: _ts,\n recoveryCodes: _rc,\n passkeys: _pk,\n permissions: _perms,\n ...user\n }) => ({\n ...user,\n hash: '',\n permissions: decodePermissions(_perms as any),\n totpEnabled: user.totpEnabled ?? false,\n passkeyCount: _pk?.length ?? 0,\n seenAgo: user.lastOnline ? now - user.lastOnline : Infinity,\n createdAgo: now - user.created,\n }),\n );\n return resp;\n }),\n\n self: protectedProcedure(ScopeName.SessionInfo).query(async ({ ctx }) => {\n const now = Date.now();\n return {\n username: ctx.user.username || 'Admin',\n isOwner: ctx.user.isOwner,\n isBanned: ctx.user.isBanned ?? false,\n lastOnline: ctx.user.lastOnline ?? 0,\n created: ctx.user.created ?? 0,\n seenAgo: ctx.user.lastOnline ? now - ctx.user.lastOnline : Infinity,\n createdAgo: now - (ctx.user.created ?? now),\n permissions: ctx.user.permissions,\n totpEnabled: ctx.user.totpEnabled ?? false,\n passkeyCount: ctx.user.passkeys?.length ?? 0,\n };\n }),\n\n create: protectedProcedure(ScopeName.UserCreate)\n .input(\n z.object({\n username: z.string(),\n password: z.string(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username, password } = input;\n const { log, error } = ctx;\n\n if (typeof username !== 'string' || typeof password !== 'string')\n return 'username/password not a string';\n if (!username.match(/^\\w{0,32}$/)) return 'username is not allowed';\n if (password.length === 0 || password.length > 128)\n return 'invalid password size';\n\n if (ctx.user.isOwner && ctx.user.username === '') {\n try {\n await database.stores.users.update(\n { _id: ctx.user._id },\n { username, hash: await database.hash(password) },\n );\n } catch (e) {\n error('error setting owner password', e);\n return 'error setting owner password';\n }\n log(`created account as \"${username.yellow}\"`);\n return '';\n }\n\n if (await database.userExists(username)) return 'user already exists';\n\n try {\n await database.createUser(username, password);\n } catch (e) {\n error('error creating new user', e);\n return 'error creating new user';\n }\n log(`created new user \"${username.yellow}\"`);\n return '';\n }),\n\n // self-service: any authenticated user can change their own password\n // changing another user's password requires user.passwd permission (enforced here)\n passwd: protectedProcedure(ScopeName.SessionInfo)\n .input(\n z.object({\n username: z.string(),\n password: z.string(),\n currentPassword: z.string().optional(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username, password } = input;\n const { log, error } = ctx;\n\n const isSelf = username === ctx.user.username;\n if (isSelf) {\n if (!input.currentPassword) return 'current password required';\n const bcryptLib = await import('bcryptjs');\n if (!(await bcryptLib.compare(input.currentPassword, ctx.user.hash)))\n return 'incorrect current password';\n } else {\n const defaults = await database.getDefaultPermissions();\n if (!userHasScope(ctx.user, ScopeName.UserPasswd, defaults))\n return 'missing permission';\n }\n\n if (typeof username !== 'string' || typeof password !== 'string')\n return 'username/password not a string';\n if (!username.match(/^\\w{0,32}$/)) return 'username is not allowed';\n if (!(await database.userExists(username)))\n return 'user does not exist';\n\n try {\n await database.userPasswd(username, password);\n } catch (e) {\n error('error setting user password', e);\n return \"error setting user's password\";\n }\n log(`changed password for \"${username.yellow}\"`);\n return '';\n }),\n\n ban: protectedProcedure(ScopeName.UserBan)\n .input(\n z.object({\n username: z.string(),\n banned: z.boolean(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username, banned } = input;\n const { log } = ctx;\n\n if (username === ctx.user.username) return 'cannot disable yourself';\n if (!(await database.userExists(username)))\n return 'user does not exist';\n\n const target = await database.stores.users.findOne<\n IStoreUser & { _id: string }\n >({ type: 'user', username });\n if (target?.isOwner) return 'cannot disable the owner';\n\n await database.banUser(username, banned);\n log(`${banned ? 'disabled' : 'enabled'} user \"${username.yellow}\"`);\n return '';\n }),\n\n delete: protectedProcedure(ScopeName.UserDelete)\n .input(\n z.object({\n username: z.string(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username } = input;\n const { log } = ctx;\n\n if (username === ctx.user.username) return 'cannot delete yourself';\n if (!(await database.userExists(username)))\n return 'user does not exist';\n\n const target = await database.stores.users.findOne<\n IStoreUser & { _id: string }\n >({ type: 'user', username });\n if (target?.isOwner) return 'cannot delete the owner';\n\n await database.deleteUser(username);\n log(`deleted user \"${username.yellow}\"`);\n return '';\n }),\n\n permissions: protectedProcedure(ScopeName.UserPermissions)\n .input(\n z.object({\n username: z.string(),\n permissions: PermissionSetSchema,\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username } = input;\n const permissions = input.permissions as PermissionSet;\n const { log } = ctx;\n\n if (username === ctx.user.username)\n return 'cannot edit own permissions';\n\n const target = await database.stores.users.findOne<\n IStoreUser & { _id: string }\n >({ type: 'user', username });\n if (!target) return 'user does not exist';\n if (target.isOwner) return 'cannot edit the owner';\n\n // privilege escalation check: non-owner can't grant scopes they don't have\n if (!ctx.user.isOwner) {\n const defaults = await database.getDefaultPermissions();\n const myScopes = resolveAllScopes(ctx.user.permissions!, defaults);\n const grantedScopes = resolveAllScopes(permissions, defaults);\n for (const [scope, granted] of Object.entries(grantedScopes)) {\n if (granted && !myScopes[scope as keyof typeof myScopes])\n return `cannot grant permission you do not have: ${scope}`;\n }\n }\n\n await database.setUserPermissions(username, permissions);\n log(`updated permissions for \"${username.yellow}\"`);\n return '';\n }),\n\n defaultPermissions: router({\n get: protectedProcedure(ScopeName.UserPermissions).query(async () => {\n const { database } = getContextDeps();\n const defaults = await database.getDefaultPermissions();\n return {\n root: defaults.root,\n domains: defaults.domains,\n scopes: defaults.scopes,\n };\n }),\n\n set: protectedProcedure(ScopeName.UserPermissions)\n .input(PermissionSetSchema)\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { log } = ctx;\n await database.setDefaultPermissions(\n input as unknown as PermissionSet,\n );\n log(`updated default permissions`);\n return '';\n }),\n }),\n\n resetMfa: protectedProcedure(ScopeName.UserResetMfa)\n .input(z.object({ username: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { database } = getContextDeps();\n const { username } = input;\n const { log } = ctx;\n\n if (username === ctx.user.username) return 'cannot reset own MFA';\n\n const target = await database.stores.users.findOne<\n IStoreUser & { _id: string }\n >({ type: 'user', username });\n if (!target) return 'user does not exist';\n if (target.isOwner) return 'cannot reset the owner';\n\n await database.resetUserMfa(username);\n log(`reset MFA for \"${username.yellow}\"`);\n return '';\n }),\n }),\n});\n"],"names":["z","router","protectedProcedure","ScopeName","getContextDeps","decodePermissions","userHasScope","permissions","resolveAllScopes"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,MAAM,sBAAsBA,GAAAA,EAAE,OAAO;AAAA,EACnC,MAAMA,GAAAA,EAAE,KAAK,CAAC,OAAO,QAAQ,KAAK,CAAC;AAAA,EACnC,SAASA,GAAAA,EAAE,OAAOA,GAAAA,EAAE,OAAA,GAAUA,KAAE,KAAK,CAAC,OAAO,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7D,QAAQA,GAAAA,EAAE,OAAOA,GAAAA,EAAE,UAAUA,GAAAA,EAAE,SAAS;AAC1C,CAAC;AAEM,MAAM,aAAaC,KAAAA,OAAO;AAAA,EAC/B,MAAMA,KAAAA,OAAO;AAAA,IACX,MAAMC,KAAAA,mBAAmBC,iBAAU,QAAQ,EACxC;AAAA,MACCH,GAAAA,EACG,OAAO;AAAA,QACN,MAAMA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,CAAC;AAAA,QACrC,QAAQA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,EAAE;AAAA,QACxC,MAAMA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,MAAM;AAAA,QAC1C,WAAWA,GAAAA,EAAE,OAAA,EAAS,SAAA,EAAW,QAAQ,CAAC;AAAA,MAAA,CAC3C,EACA,SAAA,EACA,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,MAAM,QAAQ,WAAW,GAAG;AAAA,IAAA,EAE/D,MAAM,OAAO,EAAE,YAAY;AAC1B,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,MAAM,QAAQ,MAAM,cAAc;AAC1C,YAAM,OAAO,MAAM,SAAS,SAAS,EAAE,MAAM,QAAQ,MAAM,WAAW;AACtE,YAAM,MAAM,KAAK,IAAA;AACjB,WAAK,QAAQ,KAAK,MAAM;AAAA,QACtB,CAAC;AAAA,UACC,MAAM;AAAA,UACN,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,UAAU;AAAA,UACV,aAAa;AAAA,UACb,GAAG;AAAA,QAAA,OACE;AAAA,UACL,GAAG;AAAA,UACH,MAAM;AAAA,UACN,aAAaC,YAAAA,kBAAkB,MAAa;AAAA,UAC5C,aAAa,KAAK,eAAe;AAAA,UACjC,cAAc,KAAK,UAAU;AAAA,UAC7B,SAAS,KAAK,aAAa,MAAM,KAAK,aAAa;AAAA,UACnD,YAAY,MAAM,KAAK;AAAA,QAAA;AAAA,MACzB;AAEF,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,MAAMH,KAAAA,mBAAmBC,iBAAU,WAAW,EAAE,MAAM,OAAO,EAAE,UAAU;AACvE,YAAM,MAAM,KAAK,IAAA;AACjB,aAAO;AAAA,QACL,UAAU,IAAI,KAAK,YAAY;AAAA,QAC/B,SAAS,IAAI,KAAK;AAAA,QAClB,UAAU,IAAI,KAAK,YAAY;AAAA,QAC/B,YAAY,IAAI,KAAK,cAAc;AAAA,QACnC,SAAS,IAAI,KAAK,WAAW;AAAA,QAC7B,SAAS,IAAI,KAAK,aAAa,MAAM,IAAI,KAAK,aAAa;AAAA,QAC3D,YAAY,OAAO,IAAI,KAAK,WAAW;AAAA,QACvC,aAAa,IAAI,KAAK;AAAA,QACtB,aAAa,IAAI,KAAK,eAAe;AAAA,QACrC,cAAc,IAAI,KAAK,UAAU,UAAU;AAAA,MAAA;AAAA,IAE/C,CAAC;AAAA,IAED,QAAQD,KAAAA,mBAAmBC,iBAAU,UAAU,EAC5C;AAAA,MACCH,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,UAAUA,GAAAA,EAAE,OAAA;AAAA,MAAO,CACpB;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,UAAU,SAAA,IAAa;AAC/B,YAAM,EAAE,KAAK,MAAA,IAAU;AAEvB,UAAI,OAAO,aAAa,YAAY,OAAO,aAAa;AACtD,eAAO;AACT,UAAI,CAAC,SAAS,MAAM,YAAY,EAAG,QAAO;AAC1C,UAAI,SAAS,WAAW,KAAK,SAAS,SAAS;AAC7C,eAAO;AAET,UAAI,IAAI,KAAK,WAAW,IAAI,KAAK,aAAa,IAAI;AAChD,YAAI;AACF,gBAAM,SAAS,OAAO,MAAM;AAAA,YAC1B,EAAE,KAAK,IAAI,KAAK,IAAA;AAAA,YAChB,EAAE,UAAU,MAAM,MAAM,SAAS,KAAK,QAAQ,EAAA;AAAA,UAAE;AAAA,QAEpD,SAAS,GAAG;AACV,gBAAM,gCAAgC,CAAC;AACvC,iBAAO;AAAA,QACT;AACA,YAAI,uBAAuB,SAAS,MAAM,GAAG;AAC7C,eAAO;AAAA,MACT;AAEA,UAAI,MAAM,SAAS,WAAW,QAAQ,EAAG,QAAO;AAEhD,UAAI;AACF,cAAM,SAAS,WAAW,UAAU,QAAQ;AAAA,MAC9C,SAAS,GAAG;AACV,cAAM,2BAA2B,CAAC;AAClC,eAAO;AAAA,MACT;AACA,UAAI,qBAAqB,SAAS,MAAM,GAAG;AAC3C,aAAO;AAAA,IACT,CAAC;AAAA;AAAA;AAAA,IAIH,QAAQF,KAAAA,mBAAmBC,iBAAU,WAAW,EAC7C;AAAA,MACCH,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,iBAAiBA,GAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,MAAS,CACtC;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,UAAU,SAAA,IAAa;AAC/B,YAAM,EAAE,KAAK,MAAA,IAAU;AAEvB,YAAM,SAAS,aAAa,IAAI,KAAK;AACrC,UAAI,QAAQ;AACV,YAAI,CAAC,MAAM,gBAAiB,QAAO;AACnC,cAAM,YAAY,MAAM,OAAO,UAAU;AACzC,YAAI,CAAE,MAAM,UAAU,QAAQ,MAAM,iBAAiB,IAAI,KAAK,IAAI;AAChE,iBAAO;AAAA,MACX,OAAO;AACL,cAAM,WAAW,MAAM,SAAS,sBAAA;AAChC,YAAI,CAACE,YAAAA,aAAa,IAAI,MAAMH,OAAAA,UAAU,YAAY,QAAQ;AACxD,iBAAO;AAAA,MACX;AAEA,UAAI,OAAO,aAAa,YAAY,OAAO,aAAa;AACtD,eAAO;AACT,UAAI,CAAC,SAAS,MAAM,YAAY,EAAG,QAAO;AAC1C,UAAI,CAAE,MAAM,SAAS,WAAW,QAAQ;AACtC,eAAO;AAET,UAAI;AACF,cAAM,SAAS,WAAW,UAAU,QAAQ;AAAA,MAC9C,SAAS,GAAG;AACV,cAAM,+BAA+B,CAAC;AACtC,eAAO;AAAA,MACT;AACA,UAAI,yBAAyB,SAAS,MAAM,GAAG;AAC/C,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,KAAKD,KAAAA,mBAAmBC,iBAAU,OAAO,EACtC;AAAA,MACCH,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,QAAQA,GAAAA,EAAE,QAAA;AAAA,MAAQ,CACnB;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,UAAU,OAAA,IAAW;AAC7B,YAAM,EAAE,QAAQ;AAEhB,UAAI,aAAa,IAAI,KAAK,SAAU,QAAO;AAC3C,UAAI,CAAE,MAAM,SAAS,WAAW,QAAQ;AACtC,eAAO;AAET,YAAM,SAAS,MAAM,SAAS,OAAO,MAAM,QAEzC,EAAE,MAAM,QAAQ,UAAU;AAC5B,UAAI,QAAQ,QAAS,QAAO;AAE5B,YAAM,SAAS,QAAQ,UAAU,MAAM;AACvC,UAAI,GAAG,SAAS,aAAa,SAAS,UAAU,SAAS,MAAM,GAAG;AAClE,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,QAAQF,KAAAA,mBAAmBC,iBAAU,UAAU,EAC5C;AAAA,MACCH,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,MAAO,CACpB;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,aAAa;AACrB,YAAM,EAAE,QAAQ;AAEhB,UAAI,aAAa,IAAI,KAAK,SAAU,QAAO;AAC3C,UAAI,CAAE,MAAM,SAAS,WAAW,QAAQ;AACtC,eAAO;AAET,YAAM,SAAS,MAAM,SAAS,OAAO,MAAM,QAEzC,EAAE,MAAM,QAAQ,UAAU;AAC5B,UAAI,QAAQ,QAAS,QAAO;AAE5B,YAAM,SAAS,WAAW,QAAQ;AAClC,UAAI,iBAAiB,SAAS,MAAM,GAAG;AACvC,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,aAAaF,KAAAA,mBAAmBC,iBAAU,eAAe,EACtD;AAAA,MACCH,GAAAA,EAAE,OAAO;AAAA,QACP,UAAUA,GAAAA,EAAE,OAAA;AAAA,QACZ,aAAa;AAAA,MAAA,CACd;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,aAAa;AACrB,YAAMG,gBAAc,MAAM;AAC1B,YAAM,EAAE,QAAQ;AAEhB,UAAI,aAAa,IAAI,KAAK;AACxB,eAAO;AAET,YAAM,SAAS,MAAM,SAAS,OAAO,MAAM,QAEzC,EAAE,MAAM,QAAQ,UAAU;AAC5B,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI,OAAO,QAAS,QAAO;AAG3B,UAAI,CAAC,IAAI,KAAK,SAAS;AACrB,cAAM,WAAW,MAAM,SAAS,sBAAA;AAChC,cAAM,WAAWC,YAAAA,iBAAiB,IAAI,KAAK,aAAc,QAAQ;AACjE,cAAM,gBAAgBA,YAAAA,iBAAiBD,eAAa,QAAQ;AAC5D,mBAAW,CAAC,OAAO,OAAO,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC5D,cAAI,WAAW,CAAC,SAAS,KAA8B;AACrD,mBAAO,4CAA4C,KAAK;AAAA,QAC5D;AAAA,MACF;AAEA,YAAM,SAAS,mBAAmB,UAAUA,aAAW;AACvD,UAAI,4BAA4B,SAAS,MAAM,GAAG;AAClD,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,oBAAoBN,KAAAA,OAAO;AAAA,MACzB,KAAKC,KAAAA,mBAAmBC,OAAAA,UAAU,eAAe,EAAE,MAAM,YAAY;AACnE,cAAM,EAAE,SAAA,IAAaC,oBAAA;AACrB,cAAM,WAAW,MAAM,SAAS,sBAAA;AAChC,eAAO;AAAA,UACL,MAAM,SAAS;AAAA,UACf,SAAS,SAAS;AAAA,UAClB,QAAQ,SAAS;AAAA,QAAA;AAAA,MAErB,CAAC;AAAA,MAED,KAAKF,KAAAA,mBAAmBC,iBAAU,eAAe,EAC9C,MAAM,mBAAmB,EACzB,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,cAAM,EAAE,SAAA,IAAaC,oBAAA;AACrB,cAAM,EAAE,QAAQ;AAChB,cAAM,SAAS;AAAA,UACb;AAAA,QAAA;AAEF,YAAI,6BAA6B;AACjC,eAAO;AAAA,MACT,CAAC;AAAA,IAAA,CACJ;AAAA,IAED,UAAUF,KAAAA,mBAAmBC,iBAAU,YAAY,EAChD,MAAMH,GAAAA,EAAE,OAAO,EAAE,UAAUA,GAAAA,EAAE,OAAA,EAAO,CAAG,CAAC,EACxC,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,SAAA,IAAaI,oBAAA;AACrB,YAAM,EAAE,aAAa;AACrB,YAAM,EAAE,QAAQ;AAEhB,UAAI,aAAa,IAAI,KAAK,SAAU,QAAO;AAE3C,YAAM,SAAS,MAAM,SAAS,OAAO,MAAM,QAEzC,EAAE,MAAM,QAAQ,UAAU;AAC5B,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI,OAAO,QAAS,QAAO;AAE3B,YAAM,SAAS,aAAa,QAAQ;AACpC,UAAI,kBAAkB,SAAS,MAAM,GAAG;AACxC,aAAO;AAAA,IACT,CAAC;AAAA,EAAA,CACJ;AACH,CAAC;;"}
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const brdb = require("../../../util/brdb.js");
|
|
4
4
|
const v4 = require("zod/v4");
|
|
5
|
+
const scopes = require("../scopes.js");
|
|
5
6
|
const trpc = require("../trpc.js");
|
|
6
7
|
const worldRouter = trpc.router({
|
|
7
8
|
world: trpc.router({
|
|
8
|
-
list: trpc.protectedProcedure().query(() => {
|
|
9
|
+
list: trpc.protectedProcedure(scopes.ScopeName.WorldList).query(() => {
|
|
9
10
|
const { omegga } = trpc.getContextDeps();
|
|
10
11
|
const prefix = omegga.worldPath + "/";
|
|
11
12
|
return omegga.getWorlds().map((world) => world.replace(prefix, "").replace(/\.brdb$/, ""));
|
|
12
13
|
}),
|
|
13
|
-
revisions: trpc.protectedProcedure().input(v4.z.object({ world: v4.z.string() })).query(async ({ input }) => {
|
|
14
|
+
revisions: trpc.protectedProcedure(scopes.ScopeName.WorldRevisions).input(v4.z.object({ world: v4.z.string() })).query(async ({ input }) => {
|
|
14
15
|
const { omegga } = trpc.getContextDeps();
|
|
15
16
|
try {
|
|
16
17
|
return (await omegga.getWorldRevisions(input.world))?.map(
|
|
@@ -20,7 +21,7 @@ const worldRouter = trpc.router({
|
|
|
20
21
|
return null;
|
|
21
22
|
}
|
|
22
23
|
}),
|
|
23
|
-
meta: trpc.protectedProcedure().input(v4.z.object({ world: v4.z.string() })).query(async ({ input, ctx }) => {
|
|
24
|
+
meta: trpc.protectedProcedure(scopes.ScopeName.WorldMeta).input(v4.z.object({ world: v4.z.string() })).query(async ({ input, ctx }) => {
|
|
24
25
|
const { omegga } = trpc.getContextDeps();
|
|
25
26
|
try {
|
|
26
27
|
const path = omegga.getWorldPath(input.world);
|
|
@@ -31,15 +32,15 @@ const worldRouter = trpc.router({
|
|
|
31
32
|
return null;
|
|
32
33
|
}
|
|
33
34
|
}),
|
|
34
|
-
next: trpc.protectedProcedure().query(() => {
|
|
35
|
+
next: trpc.protectedProcedure(scopes.ScopeName.WorldNext).query(() => {
|
|
35
36
|
const { omegga } = trpc.getContextDeps();
|
|
36
37
|
return omegga.getNextWorld();
|
|
37
38
|
}),
|
|
38
|
-
active: trpc.protectedProcedure().query(() => {
|
|
39
|
+
active: trpc.protectedProcedure(scopes.ScopeName.WorldActive).query(() => {
|
|
39
40
|
const { omegga } = trpc.getContextDeps();
|
|
40
41
|
return omegga.getActiveWorld();
|
|
41
42
|
}),
|
|
42
|
-
load: trpc.protectedProcedure().input(
|
|
43
|
+
load: trpc.protectedProcedure(scopes.ScopeName.WorldLoad).input(
|
|
43
44
|
v4.z.object({
|
|
44
45
|
world: v4.z.string(),
|
|
45
46
|
revision: v4.z.number().optional()
|
|
@@ -69,7 +70,7 @@ const worldRouter = trpc.router({
|
|
|
69
70
|
return false;
|
|
70
71
|
}
|
|
71
72
|
}),
|
|
72
|
-
use: trpc.protectedProcedure().input(
|
|
73
|
+
use: trpc.protectedProcedure(scopes.ScopeName.WorldUse).input(
|
|
73
74
|
v4.z.object({
|
|
74
75
|
world: v4.z.string().optional()
|
|
75
76
|
})
|
|
@@ -85,7 +86,7 @@ const worldRouter = trpc.router({
|
|
|
85
86
|
return false;
|
|
86
87
|
}
|
|
87
88
|
}),
|
|
88
|
-
create: trpc.protectedProcedure().input(
|
|
89
|
+
create: trpc.protectedProcedure(scopes.ScopeName.WorldCreate).input(
|
|
89
90
|
v4.z.object({
|
|
90
91
|
name: v4.z.string(),
|
|
91
92
|
level: v4.z.enum(["Plate", "Space", "Studio", "Peaks"]).optional()
|
|
@@ -109,7 +110,7 @@ const worldRouter = trpc.router({
|
|
|
109
110
|
return false;
|
|
110
111
|
}
|
|
111
112
|
}),
|
|
112
|
-
save: trpc.protectedProcedure().input(
|
|
113
|
+
save: trpc.protectedProcedure(scopes.ScopeName.WorldSave).input(
|
|
113
114
|
v4.z.object({
|
|
114
115
|
name: v4.z.string().optional()
|
|
115
116
|
}).optional().default({})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"world.js","sources":["../../../../src/webserver/backend/router/world.ts"],"sourcesContent":["import { readBrdbMeta } from '@util/brdb';\nimport { z } from 'zod/v4';\nimport { getContextDeps, protectedProcedure, router } from '../trpc';\n\nexport const worldRouter = router({\n world: router({\n list: protectedProcedure(
|
|
1
|
+
{"version":3,"file":"world.js","sources":["../../../../src/webserver/backend/router/world.ts"],"sourcesContent":["import { readBrdbMeta } from '@util/brdb';\nimport { z } from 'zod/v4';\nimport { ScopeName } from '../scopes';\nimport { getContextDeps, protectedProcedure, router } from '../trpc';\n\nexport const worldRouter = router({\n world: router({\n list: protectedProcedure(ScopeName.WorldList).query(() => {\n const { omegga } = getContextDeps();\n const prefix = omegga.worldPath + '/';\n return omegga\n .getWorlds()\n .map(world => world.replace(prefix, '').replace(/\\.brdb$/, ''));\n }),\n\n revisions: protectedProcedure(ScopeName.WorldRevisions)\n .input(z.object({ world: z.string() }))\n .query(async ({ input }) => {\n const { omegga } = getContextDeps();\n try {\n return (\n (await omegga.getWorldRevisions(input.world))?.map(\n ({ date, ...r }) => ({ ...r, date: date.getTime() }),\n ) ?? null\n );\n } catch (_err) {\n return null;\n }\n }),\n\n meta: protectedProcedure(ScopeName.WorldMeta)\n .input(z.object({ world: z.string() }))\n .query(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n try {\n const path = omegga.getWorldPath(input.world);\n if (!path) return null;\n return readBrdbMeta(path);\n } catch (err) {\n ctx.error('Error while getting world meta', err);\n return null;\n }\n }),\n\n next: protectedProcedure(ScopeName.WorldNext).query(() => {\n const { omegga } = getContextDeps();\n return omegga.getNextWorld();\n }),\n\n active: protectedProcedure(ScopeName.WorldActive).query(() => {\n const { omegga } = getContextDeps();\n return omegga.getActiveWorld();\n }),\n\n load: protectedProcedure(ScopeName.WorldLoad)\n .input(\n z.object({\n world: z.string(),\n revision: z.number().optional(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log, error } = ctx;\n try {\n let res = false;\n if (input.revision) {\n log(\n 'Loading world revision',\n input.revision,\n 'of',\n input.world.yellow,\n );\n res = await omegga.loadWorldRevision(input.world, input.revision);\n } else {\n log('Loading world', input.world.yellow);\n res = await omegga.loadWorld(input.world);\n }\n if (res) log('World loaded successfully');\n else log('World load failed');\n return res;\n } catch (err) {\n error('Error while loading world', err);\n return false;\n }\n }),\n\n use: protectedProcedure(ScopeName.WorldUse)\n .input(\n z.object({\n world: z.string().optional(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log, error } = ctx;\n try {\n const ok = omegga.setActiveWorld(input.world || null);\n if (ok) log('Set default world to', (input.world || 'none').yellow);\n return ok;\n } catch (err) {\n error('Error while using world', err);\n return false;\n }\n }),\n\n create: protectedProcedure(ScopeName.WorldCreate)\n .input(\n z.object({\n name: z.string(),\n level: z.enum(['Plate', 'Space', 'Studio', 'Peaks']).optional(),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log, error } = ctx;\n try {\n log(\n 'Creating new world',\n input.name.yellow,\n 'with level',\n input.level?.yellow || 'Plate',\n );\n const res = await omegga.createEmptyWorld(input.name, input.level);\n if (res) log('World created successfully');\n else log('World creation failed');\n return res;\n } catch (err) {\n error('Error while creating world', err);\n return false;\n }\n }),\n\n save: protectedProcedure(ScopeName.WorldSave)\n .input(\n z\n .object({\n name: z.string().optional(),\n })\n .optional()\n .default({}),\n )\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log, error } = ctx;\n try {\n let res = false;\n if (input.name) {\n log('Saving world as', input.name.yellow);\n res = await omegga.saveWorldAs(input.name);\n } else {\n log('Saving current world');\n res = await omegga.saveWorld();\n }\n if (res) log('World saved successfully');\n else log('World save failed');\n return res;\n } catch (err) {\n error('Error while saving world', err);\n return false;\n }\n }),\n }),\n});\n"],"names":["router","protectedProcedure","ScopeName","getContextDeps","z","readBrdbMeta"],"mappings":"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"}
|