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.
Files changed (95) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/bin/omegga +1 -1
  3. package/dist/cli/terminal.js +46 -0
  4. package/dist/cli/terminal.js.map +1 -1
  5. package/dist/omegga/commandInjector.js +13 -6
  6. package/dist/omegga/commandInjector.js.map +1 -1
  7. package/dist/omegga/player.js +2 -2
  8. package/dist/omegga/player.js.map +1 -1
  9. package/dist/plugin.js.map +1 -1
  10. package/dist/webserver/backend/database.js +219 -7
  11. package/dist/webserver/backend/database.js.map +1 -1
  12. package/dist/webserver/backend/events.js +1 -1
  13. package/dist/webserver/backend/events.js.map +1 -1
  14. package/dist/webserver/backend/index.js +120 -2
  15. package/dist/webserver/backend/index.js.map +1 -1
  16. package/dist/webserver/backend/permissions.js +74 -0
  17. package/dist/webserver/backend/permissions.js.map +1 -0
  18. package/dist/webserver/backend/router/chat.js +9 -7
  19. package/dist/webserver/backend/router/chat.js.map +1 -1
  20. package/dist/webserver/backend/router/index.js +3 -1
  21. package/dist/webserver/backend/router/index.js.map +1 -1
  22. package/dist/webserver/backend/router/mfa.js +162 -0
  23. package/dist/webserver/backend/router/mfa.js.map +1 -0
  24. package/dist/webserver/backend/router/player.js +7 -6
  25. package/dist/webserver/backend/router/player.js.map +1 -1
  26. package/dist/webserver/backend/router/plugin.js +14 -10
  27. package/dist/webserver/backend/router/plugin.js.map +1 -1
  28. package/dist/webserver/backend/router/role.js +2 -1
  29. package/dist/webserver/backend/router/role.js.map +1 -1
  30. package/dist/webserver/backend/router/server.js +85 -71
  31. package/dist/webserver/backend/router/server.js.map +1 -1
  32. package/dist/webserver/backend/router/session.js +11 -2
  33. package/dist/webserver/backend/router/session.js.map +1 -1
  34. package/dist/webserver/backend/router/user.js +175 -13
  35. package/dist/webserver/backend/router/user.js.map +1 -1
  36. package/dist/webserver/backend/router/world.js +10 -9
  37. package/dist/webserver/backend/router/world.js.map +1 -1
  38. package/dist/webserver/backend/scopes.js +303 -0
  39. package/dist/webserver/backend/scopes.js.map +1 -0
  40. package/dist/webserver/backend/totp.js +32 -0
  41. package/dist/webserver/backend/totp.js.map +1 -0
  42. package/dist/webserver/backend/trpc.js +47 -4
  43. package/dist/webserver/backend/trpc.js.map +1 -1
  44. package/doc/permissions.md +193 -0
  45. package/package.json +7 -2
  46. package/public/app.html +932 -526
  47. package/public/assets/IconShieldCheck-Bjlm8mX2.js +2 -0
  48. package/public/assets/IconShieldCheck-Bjlm8mX2.js.map +1 -0
  49. package/public/assets/IconWorld-XmQNg6JI.js +2 -0
  50. package/public/assets/IconWorld-XmQNg6JI.js.map +1 -0
  51. package/public/assets/IconWorldPlus-CxGGv2eD.js +2 -0
  52. package/public/assets/IconWorldPlus-CxGGv2eD.js.map +1 -0
  53. package/public/assets/app-DMr_-wik.js +7 -0
  54. package/public/assets/app-DMr_-wik.js.map +1 -0
  55. package/public/assets/auth-CEYiizOt.js +2 -0
  56. package/public/assets/auth-CEYiizOt.js.map +1 -0
  57. package/public/assets/index-BDkarAkX.js +3 -0
  58. package/public/assets/index-BDkarAkX.js.map +1 -0
  59. package/public/assets/{index-2cgXLhrt.js → index-DEL-PD_8.js} +2 -2
  60. package/public/assets/{index-2cgXLhrt.js.map → index-DEL-PD_8.js.map} +1 -1
  61. package/public/assets/index-D_STNela.js +2 -0
  62. package/public/assets/index-D_STNela.js.map +1 -0
  63. package/public/assets/index-Dkh3HiBz.js +4 -0
  64. package/public/assets/index-Dkh3HiBz.js.map +1 -0
  65. package/public/assets/shared/@components-CgsSYMe6.js +2 -0
  66. package/public/assets/shared/@components-CgsSYMe6.js.map +1 -0
  67. package/public/assets/shared/@hooks-DT93N7wd.js +2 -0
  68. package/public/assets/shared/{@hooks-Ch_hJYh0.js.map → @hooks-DT93N7wd.js.map} +1 -1
  69. package/public/assets/shared/@tabler_icons-react-DrVFX3uL.js +3 -0
  70. package/public/assets/shared/{@tabler_icons-react-iQk11eD_.js.map → @tabler_icons-react-DrVFX3uL.js.map} +1 -1
  71. package/public/assets/shared/{wouter-CUC1caxu.js → wouter-CNISJ5i6.js} +2 -2
  72. package/public/assets/shared/wouter-CNISJ5i6.js.map +1 -0
  73. package/public/assets/useHasScope-BOdFktko.js +2 -0
  74. package/public/assets/useHasScope-BOdFktko.js.map +1 -0
  75. package/public/auth.html +97 -33
  76. package/templates/safe-ts/omegga.d.ts +1 -1
  77. package/public/assets/IconArrowRight-BuVxhqKX.js +0 -2
  78. package/public/assets/IconArrowRight-BuVxhqKX.js.map +0 -1
  79. package/public/assets/IconWorld-B6GK5qpx.js +0 -2
  80. package/public/assets/IconWorld-B6GK5qpx.js.map +0 -1
  81. package/public/assets/IconWorldPlus-Dkos72Ws.js +0 -2
  82. package/public/assets/IconWorldPlus-Dkos72Ws.js.map +0 -1
  83. package/public/assets/app-CLZ-5FIi.js +0 -6
  84. package/public/assets/app-CLZ-5FIi.js.map +0 -1
  85. package/public/assets/auth-6N37o421.js +0 -2
  86. package/public/assets/auth-6N37o421.js.map +0 -1
  87. package/public/assets/index-DLZ8-XzO.js +0 -4
  88. package/public/assets/index-DLZ8-XzO.js.map +0 -1
  89. package/public/assets/index-DY750sgZ.js +0 -2
  90. package/public/assets/index-DY750sgZ.js.map +0 -1
  91. package/public/assets/shared/@components-DPHARroA.js +0 -2
  92. package/public/assets/shared/@components-DPHARroA.js.map +0 -1
  93. package/public/assets/shared/@hooks-Ch_hJYh0.js +0 -2
  94. package/public/assets/shared/@tabler_icons-react-iQk11eD_.js +0 -3
  95. 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(({ hash: _, ...user }) => ({
20
- ...user,
21
- hash: "",
22
- seenAgo: user.lastOnline ? now - user.lastOnline : Infinity,
23
- createdAgo: now - user.created
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
- create: trpc.protectedProcedure().input(
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
- passwd: trpc.protectedProcedure().input(
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
- if (!ctx.user.isOwner && username !== ctx.user.username)
75
- return "missing permission";
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":"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"}
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":"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"}
@@ -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('world.list').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('world.revisions')\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('world.meta')\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('world.next').query(() => {\n const { omegga } = getContextDeps();\n return omegga.getNextWorld();\n }),\n\n active: protectedProcedure('world.active').query(() => {\n const { omegga } = getContextDeps();\n return omegga.getActiveWorld();\n }),\n\n load: protectedProcedure('world.load')\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('world.use')\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('world.create')\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('world.save')\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","getContextDeps","z","readBrdbMeta"],"mappings":";;;;;AAIO,MAAM,cAAcA,KAAAA,OAAO;AAAA,EAChC,OAAOA,KAAAA,OAAO;AAAA,IACZ,MAAMC,KAAAA,mBAA+B,EAAE,MAAM,MAAM;AACjD,YAAM,EAAE,OAAA,IAAWC,oBAAA;AACnB,YAAM,SAAS,OAAO,YAAY;AAClC,aAAO,OACJ,UAAA,EACA,IAAI,CAAA,UAAS,MAAM,QAAQ,QAAQ,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC;AAAA,IAClE,CAAC;AAAA,IAED,WAAWD,KAAAA,mBAAoC,EAC5C,MAAME,GAAAA,EAAE,OAAO,EAAE,OAAOA,GAAAA,EAAE,OAAA,EAAO,CAAG,CAAC,EACrC,MAAM,OAAO,EAAE,YAAY;AAC1B,YAAM,EAAE,OAAA,IAAWD,oBAAA;AACnB,UAAI;AACF,gBACG,MAAM,OAAO,kBAAkB,MAAM,KAAK,IAAI;AAAA,UAC7C,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,GAAG,MAAM,KAAK,UAAQ;AAAA,QAAE,KAC/C;AAAA,MAET,SAAS,MAAM;AACb,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAEH,MAAMD,KAAAA,mBAA+B,EAClC,MAAME,GAAAA,EAAE,OAAO,EAAE,OAAOA,GAAAA,EAAE,SAAO,CAAG,CAAC,EACrC,MAAM,OAAO,EAAE,OAAO,UAAU;AAC/B,YAAM,EAAE,OAAA,IAAWD,oBAAA;AACnB,UAAI;AACF,cAAM,OAAO,OAAO,aAAa,MAAM,KAAK;AAC5C,YAAI,CAAC,KAAM,QAAO;AAClB,eAAOE,KAAAA,aAAa,IAAI;AAAA,MAC1B,SAAS,KAAK;AACZ,YAAI,MAAM,kCAAkC,GAAG;AAC/C,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAEH,MAAMH,KAAAA,mBAA+B,EAAE,MAAM,MAAM;AACjD,YAAM,EAAE,OAAA,IAAWC,oBAAA;AACnB,aAAO,OAAO,aAAA;AAAA,IAChB,CAAC;AAAA,IAED,QAAQD,KAAAA,mBAAiC,EAAE,MAAM,MAAM;AACrD,YAAM,EAAE,OAAA,IAAWC,oBAAA;AACnB,aAAO,OAAO,eAAA;AAAA,IAChB,CAAC;AAAA,IAED,MAAMD,KAAAA,mBAA+B,EAClC;AAAA,MACCE,GAAAA,EAAE,OAAO;AAAA,QACP,OAAOA,GAAAA,EAAE,OAAA;AAAA,QACT,UAAUA,GAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,MAAS,CAC/B;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,OAAA,IAAWD,oBAAA;AACnB,YAAM,EAAE,KAAK,MAAA,IAAU;AACvB,UAAI;AACF,YAAI,MAAM;AACV,YAAI,MAAM,UAAU;AAClB;AAAA,YACE;AAAA,YACA,MAAM;AAAA,YACN;AAAA,YACA,MAAM,MAAM;AAAA,UAAA;AAEd,gBAAM,MAAM,OAAO,kBAAkB,MAAM,OAAO,MAAM,QAAQ;AAAA,QAClE,OAAO;AACL,cAAI,iBAAiB,MAAM,MAAM,MAAM;AACvC,gBAAM,MAAM,OAAO,UAAU,MAAM,KAAK;AAAA,QAC1C;AACA,YAAI,SAAS,2BAA2B;AAAA,iBAC/B,mBAAmB;AAC5B,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,6BAA6B,GAAG;AACtC,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAEH,KAAKD,KAAAA,mBAA8B,EAChC;AAAA,MACCE,GAAAA,EAAE,OAAO;AAAA,QACP,OAAOA,GAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,MAAS,CAC5B;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,OAAA,IAAWD,oBAAA;AACnB,YAAM,EAAE,KAAK,MAAA,IAAU;AACvB,UAAI;AACF,cAAM,KAAK,OAAO,eAAe,MAAM,SAAS,IAAI;AACpD,YAAI,GAAI,KAAI,yBAAyB,MAAM,SAAS,QAAQ,MAAM;AAClE,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,2BAA2B,GAAG;AACpC,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAEH,QAAQD,KAAAA,mBAAiC,EACtC;AAAA,MACCE,GAAAA,EAAE,OAAO;AAAA,QACP,MAAMA,GAAAA,EAAE,OAAA;AAAA,QACR,OAAOA,GAAAA,EAAE,KAAK,CAAC,SAAS,SAAS,UAAU,OAAO,CAAC,EAAE,SAAA;AAAA,MAAS,CAC/D;AAAA,IAAA,EAEF,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,OAAA,IAAWD,oBAAA;AACnB,YAAM,EAAE,KAAK,MAAA,IAAU;AACvB,UAAI;AACF;AAAA,UACE;AAAA,UACA,MAAM,KAAK;AAAA,UACX;AAAA,UACA,MAAM,OAAO,UAAU;AAAA,QAAA;AAEzB,cAAM,MAAM,MAAM,OAAO,iBAAiB,MAAM,MAAM,MAAM,KAAK;AACjE,YAAI,SAAS,4BAA4B;AAAA,iBAChC,uBAAuB;AAChC,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,8BAA8B,GAAG;AACvC,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAEH,MAAMD,KAAAA,mBAA+B,EAClC;AAAA,MACCE,GAAAA,EACG,OAAO;AAAA,QACN,MAAMA,GAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,MAAS,CAC3B,EACA,WACA,QAAQ,CAAA,CAAE;AAAA,IAAA,EAEd,SAAS,OAAO,EAAE,OAAO,UAAU;AAClC,YAAM,EAAE,OAAA,IAAWD,oBAAA;AACnB,YAAM,EAAE,KAAK,MAAA,IAAU;AACvB,UAAI;AACF,YAAI,MAAM;AACV,YAAI,MAAM,MAAM;AACd,cAAI,mBAAmB,MAAM,KAAK,MAAM;AACxC,gBAAM,MAAM,OAAO,YAAY,MAAM,IAAI;AAAA,QAC3C,OAAO;AACL,cAAI,sBAAsB;AAC1B,gBAAM,MAAM,OAAO,UAAA;AAAA,QACrB;AACA,YAAI,SAAS,0BAA0B;AAAA,iBAC9B,mBAAmB;AAC5B,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,4BAA4B,GAAG;AACrC,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EAAA,CACJ;AACH,CAAC;;"}
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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"}