@elementspanel/epanel-plugin-file 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "file",
3
+ "version": "1.0.1",
4
+ "dual": true,
5
+ "backend": "backend/index.cjs"
6
+ }
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@elementspanel/epanel-plugin-file",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "Owns the file manager: every /api/files route, the instance file manager card and its Desktop window, the file editor, the image viewer, the upload queue and the upload/download dialogs.",
5
+ "license": "Apache-2.0",
6
+ "author": "Csituka_D <694428809@qq.com> (https://github.com/JessDaodao)",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/ElementsPanel/ElementsPanel"
10
+ },
11
+ "engines": {
12
+ "node": ">=22.15.0"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "panel/",
19
+ "daemon/",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "exports": {
24
+ "./package.json": "./package.json",
25
+ "./panel/plugin.json": "./panel/plugin.json",
26
+ "./daemon/plugin.json": "./daemon/plugin.json"
27
+ },
28
+ "elementspanel": {
29
+ "id": "file",
30
+ "panel": {
31
+ "priority": 120,
32
+ "frontendInject": [
33
+ "console",
34
+ "i18n"
35
+ ],
36
+ "styles": [
37
+ "frontend/assets/style-0-style-9-_panel-plugin-build-entry_file-eb11377f.css"
38
+ ],
39
+ "description": "Owns the file manager: every /api/files route, the instance file manager card and its Desktop window, the file editor, the image viewer, the upload queue and the upload/download dialogs.",
40
+ "directory": "panel",
41
+ "folder": "file"
42
+ },
43
+ "daemon": {
44
+ "priority": 30,
45
+ "displayName": "File Manager",
46
+ "description": "Owns the daemon's file subsystem: the sandboxed FileManager, chunked uploads, the file/* protocol events and the upload/download HTTP routes. Provides ctx.files, so instance creation, the Java manager, SteamCMD and the mod service reach instance files through it.",
47
+ "directory": "daemon",
48
+ "folder": "file"
49
+ }
50
+ },
51
+ "peerDependencies": {
52
+ "@elementspanel/panel": "1.0.0",
53
+ "@elementspanel/daemon": "1.0.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "@elementspanel/panel": {
57
+ "optional": true
58
+ },
59
+ "@elementspanel/daemon": {
60
+ "optional": true
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,513 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ "use strict";
3
+ /******/ var __webpack_modules__ = ({
4
+
5
+ /***/ "./plugins/file/src/backend/file_router.ts"
6
+ (__unused_webpack_module, exports, __webpack_require__) {
7
+
8
+
9
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
10
+ exports.registerFileRoutes = registerFileRoutes;
11
+ const uuid_1 = __webpack_require__("uuid");
12
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
13
+ /** The upload/download passport key. Its own copy of the core's `timeUuid`. */
14
+ function timeUuid() {
15
+ return (0, uuid_1.v4)().replace(/-/gim, "") + new Date().getTime();
16
+ }
17
+ /**
18
+ * Every route the file manager UI talks to, all under `/api/files`.
19
+ *
20
+ * Registered from `apply()`, so the whole surface — the gate included — leaves
21
+ * with the plugin.
22
+ */
23
+ function registerFileRoutes() {
24
+ const router = (0, runtime_1.koa)().router("/api/files");
25
+ const permission = (0, runtime_1.middleware)().permission;
26
+ const validator = (0, runtime_1.middleware)().validator;
27
+ const ROLE = (0, runtime_1.roles)();
28
+ // The user plugin decides whether ordinary users may open the file manager;
29
+ // the same guard also decides who may reach a particular instance.
30
+ router.use(async (ctx, next) => {
31
+ if (!(0, runtime_1.identity)().accessPolicy.canFileManager && !(0, runtime_1.identity)().of(ctx).elevated) {
32
+ ctx.status = 403;
33
+ ctx.body = new Error((0, runtime_1.$t)("TXT_CODE_router.file.off"));
34
+ return;
35
+ }
36
+ await next();
37
+ });
38
+ router.use((0, runtime_1.middleware)().instanceAccess);
39
+ router.get("/status", permission({ level: ROLE.USER, speedLimit: false }), validator({
40
+ query: { daemonId: String, uuid: String }
41
+ }), async (ctx) => {
42
+ try {
43
+ const daemonId = String(ctx.query.daemonId);
44
+ const instanceUuid = String(ctx.query.uuid);
45
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
46
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/status", {
47
+ instanceUuid
48
+ });
49
+ if (!(0, runtime_1.identity)().of(ctx).elevated)
50
+ delete result.disk;
51
+ ctx.body = result;
52
+ }
53
+ catch (err) {
54
+ ctx.body = err;
55
+ }
56
+ });
57
+ router.get("/list", permission({ level: ROLE.USER, speedLimit: false }), validator({
58
+ query: { daemonId: String, uuid: String, target: String, page: Number, page_size: Number }
59
+ }), async (ctx) => {
60
+ try {
61
+ const target = String(ctx.query.target);
62
+ const daemonId = String(ctx.query.daemonId);
63
+ const instanceUuid = String(ctx.query.uuid);
64
+ const page = Math.max(0, Number(ctx.query.page) || 0);
65
+ const pageSize = Math.min(100, Math.max(1, Number(ctx.query.page_size) || 10));
66
+ const fileName = String(ctx.query.file_name || "");
67
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
68
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/list", {
69
+ instanceUuid,
70
+ target,
71
+ pageSize,
72
+ page,
73
+ fileName
74
+ });
75
+ ctx.body = result;
76
+ }
77
+ catch (err) {
78
+ ctx.body = err;
79
+ }
80
+ });
81
+ router.get("/preview", permission({ level: ROLE.USER, speedLimit: false }), validator({
82
+ query: { daemonId: String, uuid: String, target: String, code: String }
83
+ }), async (ctx) => {
84
+ try {
85
+ const daemonId = String(ctx.query.daemonId);
86
+ const instanceUuid = String(ctx.query.uuid);
87
+ const target = String(ctx.query.target);
88
+ const code = String(ctx.query.code || "utf-8");
89
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
90
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/preview", {
91
+ instanceUuid,
92
+ target,
93
+ code
94
+ });
95
+ ctx.body = result;
96
+ }
97
+ catch (err) {
98
+ ctx.body = err;
99
+ }
100
+ });
101
+ router.put("/chmod", permission({ level: ROLE.USER }), validator({
102
+ query: { daemonId: String, uuid: String },
103
+ body: { target: String, chmod: Number, deep: Boolean }
104
+ }), async (ctx) => {
105
+ try {
106
+ const daemonId = String(ctx.query.daemonId);
107
+ const instanceUuid = String(ctx.query.uuid);
108
+ const target = String(ctx.request.body.target);
109
+ const chmod = Number(ctx.request.body.chmod);
110
+ const deep = Boolean(ctx.request.body.deep);
111
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
112
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/chmod", {
113
+ target,
114
+ instanceUuid,
115
+ chmod,
116
+ deep
117
+ });
118
+ ctx.body = result;
119
+ }
120
+ catch (err) {
121
+ ctx.body = err;
122
+ }
123
+ });
124
+ router.put("/chmod_batch", permission({ level: ROLE.USER }), validator({
125
+ query: { daemonId: String, uuid: String },
126
+ body: { targets: Array, chmod: Number, deep: Boolean }
127
+ }), async (ctx) => {
128
+ try {
129
+ const daemonId = String(ctx.query.daemonId);
130
+ const instanceUuid = String(ctx.query.uuid);
131
+ const targets = ctx.request.body.targets.map((target) => String(target));
132
+ const chmod = Number(ctx.request.body.chmod);
133
+ const deep = Boolean(ctx.request.body.deep);
134
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
135
+ const timeout = Math.min(5 * 60 * 1000, Math.max(15 * 1000, targets.length * 2 * 1000));
136
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/chmod_batch", {
137
+ targets,
138
+ instanceUuid,
139
+ chmod,
140
+ deep
141
+ }, timeout);
142
+ ctx.body = result;
143
+ }
144
+ catch (err) {
145
+ ctx.body = err;
146
+ }
147
+ });
148
+ router.post("/touch", permission({ level: ROLE.USER }), validator({ query: { daemonId: String, uuid: String }, body: { target: String } }), async (ctx) => {
149
+ try {
150
+ const daemonId = String(ctx.query.daemonId);
151
+ const instanceUuid = String(ctx.query.uuid);
152
+ const target = String(ctx.request.body.target);
153
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
154
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/touch", {
155
+ target,
156
+ instanceUuid
157
+ });
158
+ ctx.body = result;
159
+ }
160
+ catch (err) {
161
+ ctx.body = err;
162
+ }
163
+ });
164
+ router.post("/mkdir", permission({ level: ROLE.USER }), validator({ query: { daemonId: String, uuid: String }, body: { target: String } }), async (ctx) => {
165
+ try {
166
+ const daemonId = String(ctx.query.daemonId);
167
+ const instanceUuid = String(ctx.query.uuid);
168
+ const target = String(ctx.request.body.target);
169
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
170
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/mkdir", {
171
+ target,
172
+ instanceUuid
173
+ });
174
+ ctx.body = result;
175
+ }
176
+ catch (err) {
177
+ ctx.body = err;
178
+ }
179
+ });
180
+ router.put("/", permission({ level: ROLE.USER }), validator({ query: { daemonId: String, uuid: String }, body: { target: String } }), async (ctx) => {
181
+ try {
182
+ const daemonId = String(ctx.query.daemonId);
183
+ const instanceUuid = String(ctx.query.uuid);
184
+ const target = String(ctx.request.body.target);
185
+ const text = ctx.request.body.text;
186
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
187
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/edit", {
188
+ instanceUuid,
189
+ target,
190
+ text
191
+ }, 100000);
192
+ (0, runtime_1.operations)().log("instance_file_update", {
193
+ operator_ip: ctx.ip,
194
+ operator_name: (0, runtime_1.identity)().of(ctx).userName,
195
+ instance_id: instanceUuid,
196
+ daemon_id: daemonId,
197
+ file: target
198
+ });
199
+ ctx.body = result;
200
+ }
201
+ catch (err) {
202
+ ctx.body = err;
203
+ }
204
+ });
205
+ router.post("/copy", permission({ level: ROLE.USER }), validator({ query: { daemonId: String, uuid: String }, body: { targets: Array } }), async (ctx) => {
206
+ try {
207
+ const daemonId = String(ctx.query.daemonId);
208
+ const instanceUuid = String(ctx.query.uuid);
209
+ const targets = ctx.request.body.targets;
210
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
211
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/copy", {
212
+ instanceUuid,
213
+ targets
214
+ });
215
+ ctx.body = result;
216
+ }
217
+ catch (err) {
218
+ ctx.body = err;
219
+ }
220
+ });
221
+ router.post("/download_from_url", permission({ level: ROLE.USER }), validator({
222
+ query: { uuid: String, daemonId: String },
223
+ body: { url: String, file_name: String }
224
+ }), async (ctx) => {
225
+ try {
226
+ const daemonId = String(ctx.query.daemonId);
227
+ const instanceUuid = String(ctx.query.uuid);
228
+ const url = String(ctx.request.body.url);
229
+ const fileName = String(ctx.request.body.file_name);
230
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
231
+ if (!remoteService)
232
+ throw new Error((0, runtime_1.$t)("TXT_CODE_dd559000") + ` Daemon ID: ${daemonId}`);
233
+ const downloadId = timeUuid();
234
+ ctx.body = downloadId;
235
+ (0, runtime_1.operations)().log("instance_file_download_from_url", {
236
+ operator_ip: ctx.ip,
237
+ operator_name: (0, runtime_1.identity)().of(ctx).userName,
238
+ instance_id: instanceUuid,
239
+ daemon_id: daemonId,
240
+ url: url,
241
+ fileName: fileName
242
+ });
243
+ await new ((0, runtime_1.remote)().Request)(remoteService).request("file/download_from_url", {
244
+ url,
245
+ fileName,
246
+ instanceUuid
247
+ });
248
+ }
249
+ catch (err) {
250
+ ctx.body = err;
251
+ }
252
+ });
253
+ router.put("/move", permission({ level: ROLE.USER }), validator({ query: { daemonId: String, uuid: String }, body: { targets: Array } }), async (ctx) => {
254
+ try {
255
+ const daemonId = String(ctx.query.daemonId);
256
+ const instanceUuid = String(ctx.query.uuid);
257
+ const targets = ctx.request.body.targets;
258
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
259
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/move", {
260
+ instanceUuid,
261
+ targets
262
+ });
263
+ ctx.body = result;
264
+ }
265
+ catch (err) {
266
+ ctx.body = err;
267
+ }
268
+ });
269
+ router.delete("/", permission({ level: ROLE.USER }), validator({ query: { daemonId: String, uuid: String }, body: { targets: Array } }), async (ctx) => {
270
+ try {
271
+ const daemonId = String(ctx.query.daemonId);
272
+ const instanceUuid = ctx.query.uuid;
273
+ const targets = ctx.request.body.targets;
274
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
275
+ const result = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/delete", {
276
+ instanceUuid,
277
+ targets
278
+ });
279
+ (0, runtime_1.operations)().log("instance_file_delete", {
280
+ operator_ip: ctx.ip,
281
+ operator_name: (0, runtime_1.identity)().of(ctx).userName,
282
+ instance_id: String(instanceUuid),
283
+ daemon_id: daemonId,
284
+ file: targets
285
+ });
286
+ ctx.body = result;
287
+ }
288
+ catch (err) {
289
+ ctx.body = err;
290
+ }
291
+ });
292
+ router.post("/compress", permission({ level: ROLE.USER }), validator({
293
+ query: { daemonId: String, uuid: String },
294
+ body: { source: String, type: Number, code: String }
295
+ }), async (ctx) => {
296
+ try {
297
+ const daemonId = String(ctx.query.daemonId);
298
+ const instanceUuid = String(ctx.query.uuid);
299
+ const source = String(ctx.request.body.source);
300
+ const targets = ctx.request.body.targets;
301
+ const type = Number(ctx.request.body.type);
302
+ if (typeof targets !== "string" &&
303
+ (!Array.isArray(targets) || !targets.every((target) => typeof target === "string"))) {
304
+ ctx.throw(400, "Invalid compression targets");
305
+ }
306
+ const code = String(ctx.request.body.code);
307
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
308
+ const res = await new ((0, runtime_1.remote)().Request)(remoteService).request("file/compress", {
309
+ instanceUuid,
310
+ targets,
311
+ source,
312
+ type,
313
+ code
314
+ }, 0);
315
+ ctx.body = res;
316
+ }
317
+ catch (err) {
318
+ ctx.body = err;
319
+ }
320
+ });
321
+ router.all("/download", permission({ level: ROLE.USER }), validator({ query: { uuid: String, daemonId: String, file_name: String } }), async (ctx) => {
322
+ try {
323
+ const daemonId = String(ctx.query.daemonId);
324
+ const instanceUuid = String(ctx.query.uuid);
325
+ const fileName = String(ctx.query.file_name);
326
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
327
+ if (!remoteService)
328
+ throw new Error((0, runtime_1.$t)("TXT_CODE_dd559000") + ` Daemon ID: ${daemonId}`);
329
+ const addr = remoteService.config.fullAddr;
330
+ const remoteMappings = remoteService.config.getConvertedRemoteMappings();
331
+ const password = timeUuid();
332
+ await new ((0, runtime_1.remote)().Request)(remoteService).request("passport/register", {
333
+ name: "download",
334
+ password: password,
335
+ parameter: {
336
+ fileName,
337
+ instanceUuid
338
+ }
339
+ });
340
+ (0, runtime_1.operations)().log("instance_file_download", {
341
+ operator_ip: ctx.ip,
342
+ operator_name: (0, runtime_1.identity)().of(ctx).userName,
343
+ instance_id: instanceUuid,
344
+ daemon_id: daemonId,
345
+ file: fileName
346
+ });
347
+ ctx.body = {
348
+ password,
349
+ addr,
350
+ remoteMappings
351
+ };
352
+ }
353
+ catch (err) {
354
+ ctx.body = err;
355
+ }
356
+ });
357
+ router.all("/upload", permission({ level: ROLE.USER }), validator({ query: { uuid: String, daemonId: String, upload_dir: String } }), async (ctx) => {
358
+ try {
359
+ const daemonId = String(ctx.query.daemonId);
360
+ const instanceUuid = String(ctx.query.uuid);
361
+ const uploadDir = String(ctx.query.upload_dir);
362
+ const remoteService = (0, runtime_1.remote)().services.getInstance(daemonId);
363
+ if (!remoteService)
364
+ throw new Error((0, runtime_1.$t)("TXT_CODE_dd559000") + ` Daemon ID: ${daemonId}`);
365
+ const addr = remoteService.config.fullAddr;
366
+ const remoteMappings = remoteService.config.getConvertedRemoteMappings();
367
+ const password = timeUuid();
368
+ await new ((0, runtime_1.remote)().Request)(remoteService).request("passport/register", {
369
+ name: "upload",
370
+ password: password,
371
+ parameter: {
372
+ uploadDir,
373
+ instanceUuid
374
+ }
375
+ });
376
+ (0, runtime_1.operations)().log("instance_file_upload", {
377
+ operator_ip: ctx.ip,
378
+ operator_name: (0, runtime_1.identity)().of(ctx).userName,
379
+ instance_id: instanceUuid,
380
+ daemon_id: daemonId,
381
+ file: uploadDir
382
+ });
383
+ ctx.body = {
384
+ password,
385
+ addr,
386
+ remoteMappings
387
+ };
388
+ }
389
+ catch (err) {
390
+ ctx.body = err;
391
+ }
392
+ });
393
+ }
394
+
395
+
396
+ /***/ },
397
+
398
+ /***/ "./plugins/file/src/backend/runtime.ts"
399
+ (__unused_webpack_module, exports) {
400
+
401
+
402
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
403
+ exports.$t = exports.operations = exports.identity = exports.remote = exports.roles = exports.middleware = exports.koa = void 0;
404
+ exports.setPluginContext = setPluginContext;
405
+ exports.core = core;
406
+ /**
407
+ * This plugin's private handle on its cordis context.
408
+ *
409
+ * The panel core is bundled into `app.js`, so a plugin cannot import its
410
+ * singletons. The route handlers below already own the name `ctx` — Koa's — so
411
+ * the plugin context is reached through these accessors rather than threaded in
412
+ * as a second parameter that would have to be renamed everywhere.
413
+ */
414
+ let context;
415
+ function setPluginContext(value) {
416
+ context = value;
417
+ }
418
+ function core() {
419
+ if (!context)
420
+ throw new Error("The file plugin has not been initialized yet.");
421
+ return context;
422
+ }
423
+ const koa = () => core().koa;
424
+ exports.koa = koa;
425
+ const middleware = () => core().middleware;
426
+ exports.middleware = middleware;
427
+ const roles = () => core().roles;
428
+ exports.roles = roles;
429
+ const remote = () => core().remote;
430
+ exports.remote = remote;
431
+ const identity = () => core().identity;
432
+ exports.identity = identity;
433
+ const operations = () => core().operations;
434
+ exports.operations = operations;
435
+ const $t = (key, options) => core().i18n.$t(key, options);
436
+ exports.$t = $t;
437
+
438
+
439
+ /***/ },
440
+
441
+ /***/ "uuid"
442
+ (module) {
443
+
444
+ module.exports = require("uuid");
445
+
446
+ /***/ }
447
+
448
+ /******/ });
449
+ /************************************************************************/
450
+ /******/ // The module cache
451
+ /******/ var __webpack_module_cache__ = {};
452
+ /******/
453
+ /******/ // The require function
454
+ /******/ function __webpack_require__(moduleId) {
455
+ /******/ // Check if module is in cache
456
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
457
+ /******/ if (cachedModule !== undefined) {
458
+ /******/ return cachedModule.exports;
459
+ /******/ }
460
+ /******/ // Create a new module (and put it into the cache)
461
+ /******/ var module = __webpack_module_cache__[moduleId] = {
462
+ /******/ // no module.id needed
463
+ /******/ // no module.loaded needed
464
+ /******/ exports: {}
465
+ /******/ };
466
+ /******/
467
+ /******/ // Execute the module function
468
+ /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
469
+ /******/
470
+ /******/ // Return the exports of the module
471
+ /******/ return module.exports;
472
+ /******/ }
473
+ /******/
474
+ /************************************************************************/
475
+ var __webpack_exports__ = {};
476
+ // This entry needs to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
477
+ (() => {
478
+ var exports = __webpack_exports__;
479
+
480
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
481
+ exports.inject = void 0;
482
+ exports.apply = apply;
483
+ const file_router_1 = __webpack_require__("./plugins/file/src/backend/file_router.ts");
484
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
485
+ // Panel side of the file manager. It owns every route the file manager UI talks
486
+ // to; the bytes themselves never pass through here — each route forwards to the
487
+ // daemon that holds the instance, over the node subsystem.
488
+ //
489
+ // The daemon half is `daemon/plugins/file`, which owns the `file/*`
490
+ // protocol events and the upload/download HTTP routes these forward to.
491
+ exports.inject = [
492
+ "koa",
493
+ "i18n",
494
+ "middleware",
495
+ "roles",
496
+ "remote",
497
+ "identity",
498
+ "operations"
499
+ ];
500
+ function apply(ctx) {
501
+ (0, runtime_1.setPluginContext)(ctx);
502
+ // No catalogue of its own: the two strings these routes answer with — the
503
+ // "file manager is off" refusal and the instance-forbidden one — are both
504
+ // shared with the core (`mod_manager_router`, the permission middleware), so
505
+ // they stay in the root catalogue.
506
+ (0, file_router_1.registerFileRoutes)();
507
+ }
508
+
509
+ })();
510
+
511
+ module.exports = __webpack_exports__;
512
+ /******/ })()
513
+ ;
@@ -0,0 +1 @@
1
+ .dialog-form[data-v-5e5851c8]{display:flex;flex-direction:column;gap:14px}.dialog-help[data-v-5e5851c8]{color:var(--color-gray-7);font-size:12px}.upload-container[data-v-bd0a501e]{min-height:110px;display:flex;flex-direction:column;justify-content:center;gap:14px}.image-view[data-v-0130dca5]{min-height:240px;margin-bottom:12px;display:flex;justify-content:center;align-items:center}.image-name[data-v-0130dca5]{text-align:center}.editor-wrapper[data-v-d58e7142]{height:var(--v2127b31e);overflow:auto;background:#1e1e1e;border-radius:6px;touch-action:pan-y}@media (max-width: 768px){.editor-wrapper[data-v-d58e7142]{height:var(--v5c9baeb6);border-radius:0}}.editor-container[data-v-d58e7142]{min-height:100%;transform-origin:0 0}.file-editor[data-v-d58e7142] .cm-editor{min-height:var(--v2127b31e);transition:font-size .1s ease-out}.file-editor[data-v-d58e7142] .cm-lint-marker-error{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23dc3545'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z'/%3E%3C/svg%3E");width:1.2em;height:1.2em;background-size:contain;margin-left:.2em;transition:all .1s ease-out}.file-editor[data-v-d58e7142] .cm-lintRange-error{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 3'%3E%3Cg fill='%23dc3545'%3E%3Cpolygon points='5.5,0 2.5,3 1.1,3 4.1,0'/%3E%3Cpolygon points='4,0 6,2 6,0.6 5.4,0'/%3E%3Cpolygon points='0,2 1,3 2.4,3 0,0.6'/%3E%3C/g%3E%3C/svg%3E");background-position:bottom left;background-repeat:repeat-x;background-size:auto calc(.15em + 1px)}.dfe[data-v-200ec863]{height:100%;display:flex;flex-direction:column;color:var(--desktop-window-text);font-size:13px;overflow:hidden}.dfe-toolbar[data-v-200ec863]{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;flex-shrink:0}.dfe-toolbar__left[data-v-200ec863],.dfe-toolbar__right[data-v-200ec863]{display:flex;align-items:center;gap:6px}.dfe-filename[data-v-200ec863]{font-size:13px;font-weight:500;color:var(--desktop-window-text)}.dfe-btn[data-v-200ec863]{background:var(--desktop-window-titlebar-bg);color:var(--desktop-window-text)}.dfe-btn[data-v-200ec863]:hover:not(:disabled){background:var(--desktop-window-control-hover)}.dfe-btn[data-v-200ec863]:disabled{opacity:.4;cursor:not-allowed}.dfe-btn--primary[data-v-200ec863]{color:#52c41a;background:rgba(82,196,26,.1)}.dfe-btn--primary[data-v-200ec863]:hover:not(:disabled){background:rgba(82,196,26,.2)}.dfe-body[data-v-200ec863]{flex:1;overflow:hidden;padding:8px}.dfe-loading[data-v-200ec863]{padding:16px}.file-tabs-row[data-v-bf140866]{display:flex;align-items:center;flex-shrink:0;min-width:0;margin-bottom:12px}.file-tabs-row[data-v-bf140866] .v-tabs{min-width:0;flex:1}.file-folder-tab[data-v-bf140866]{position:relative;padding-inline:28px}.file-tab-label[data-v-bf140866]{transition:transform .16s ease}.file-tab-close[data-v-bf140866]{position:absolute;inset-inline-end:4px;top:50%;transform:translateY(-50%);opacity:0;pointer-events:none;transition:opacity .16s ease}.file-folder-tab:hover .file-tab-label[data-v-bf140866],.file-folder-tab:focus-visible .file-tab-label[data-v-bf140866],.file-folder-tab:has(.file-tab-close:focus-visible) .file-tab-label[data-v-bf140866]{transform:translate(-12px)}.file-folder-tab:hover .file-tab-close[data-v-bf140866],.file-folder-tab:focus-visible .file-tab-close[data-v-bf140866],.file-folder-tab:has(.file-tab-close:focus-visible) .file-tab-close[data-v-bf140866]{opacity:1;pointer-events:auto}.file-tabs-empty[data-v-bf140866]{display:flex;flex:1;min-height:240px;flex-direction:column;align-items:center;justify-content:center;gap:16px;color:var(--text-color)}.file-tabs-empty p[data-v-bf140866]{margin:0}@media (hover: none){.file-folder-tab .file-tab-label[data-v-bf140866]{transform:translate(-12px)}.file-folder-tab .file-tab-close[data-v-bf140866]{opacity:1;pointer-events:auto}}.archive-preview[data-v-89686aa5]{display:flex;flex-direction:column;min-height:420px;width:100%;min-width:0}.archive-preview--compact[data-v-89686aa5]{min-height:0;height:100%;padding:12px;box-sizing:border-box}.archive-preview__table-wrap[data-v-89686aa5]{flex:1;min-height:0;overflow:auto}.archive-preview__name[data-v-89686aa5]{display:flex;align-items:center;gap:8px;min-width:0}.archive-preview[data-v-89686aa5] .v-table{width:100%!important;min-width:890px!important;background:transparent;color:var(--text-color)}.archive-preview[data-v-89686aa5] .v-data-table__th{background:var(--background-color, #f5f5f5);color:var(--text-color);font-size:11px;font-weight:600}.archive-preview[data-v-89686aa5] .v-data-table__td{border-bottom:1px solid var(--color-gray-4);font-size:12px;white-space:nowrap}.archive-preview--compact[data-v-89686aa5] .v-data-table__th{background:var(--background-color, #f5f5f5);color:var(--desktop-window-text-secondary, var(--text-color))}.archive-preview--compact[data-v-89686aa5] .v-data-table__td{color:var(--desktop-window-text, var(--text-color));border-bottom-color:var(--desktop-window-border, var(--color-gray-4))}.archive-preview[data-v-89686aa5] .v-data-table__tr:hover>td{background:var(--color-gray-2)}.archive-preview__name span[data-v-89686aa5]{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dfm[data-v-c8dc3471]{height:100%;display:flex;flex-direction:column;color:var(--desktop-window-text);font-size:13px;overflow:hidden}.dfm-toolbar[data-v-c8dc3471]{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;flex-shrink:0;flex-wrap:wrap}.dfm-toolbar__left[data-v-c8dc3471],.dfm-toolbar__right[data-v-c8dc3471]{display:flex;align-items:center;gap:6px}.dfm-toolbar__left[data-v-c8dc3471]{flex:1;min-width:0}.dfm-upload-progress[data-v-c8dc3471]{padding:6px 12px;flex-shrink:0}.dfm-upload-progress__info[data-v-c8dc3471]{display:flex;align-items:center;gap:4px;font-size:11px}.dfm-upload-progress__bar[data-v-c8dc3471]{margin:4px 0}.dfm-body[data-v-c8dc3471]{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;padding:0 12px 12px}.dfm-body>.v-alert[data-v-c8dc3471]{flex:0 0 auto}.dfm-nav[data-v-c8dc3471]{display:flex;align-items:center;flex-shrink:0;gap:8px;margin-bottom:8px}.dfm-breadcrumbs[data-v-c8dc3471]{border:1px solid var(--desktop-window-border);border-radius:6px;flex:1;min-width:0}.dfm-breadcrumbs__item[data-v-c8dc3471]{padding:6px 8px;cursor:pointer;display:inline-block;transition:all .2s;min-width:32px;text-align:center;font-size:12px}.dfm-breadcrumbs__item[data-v-c8dc3471]:hover{background:var(--desktop-window-control-hover)}.dfm-file-name[data-v-c8dc3471]{color:inherit;font-size:12px}.dfm-permission[data-v-c8dc3471]{display:flex;gap:24px;flex-wrap:wrap}.dfm-table-wrapper[data-v-c8dc3471]{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;-webkit-user-select:none;user-select:none}.dfm-table[data-v-c8dc3471]{flex:1;min-height:0;overflow:hidden;background:transparent}[data-v-c8dc3471] .dfm-table>.v-table__wrapper{min-height:0;overflow:auto;overscroll-behavior:contain}[data-v-c8dc3471] .dfm-table .v-data-table__th,[data-v-c8dc3471] .dfm-table .v-data-table__td{font-size:12px}[data-v-c8dc3471] .dfm-table .v-data-table__th{border-bottom:1px solid var(--desktop-window-border)!important;color:var(--desktop-window-text-secondary)!important;font-size:11px!important;padding:6px 10px!important}[data-v-c8dc3471] .dfm-table .v-data-table__td{border-bottom:1px solid rgba(var(--v-border-color),var(--v-border-opacity))!important;padding:2px 8px!important;line-height:1.4}[data-v-c8dc3471] .dfm-table tbody tr:hover>td{background:var(--desktop-window-control-hover)!important}[data-v-c8dc3471] .dfm-table .v-data-table-footer{flex-shrink:0;font-size:12px}[data-v-c8dc3471] .dfm-table .v-btn{font-size:11px}[data-v-c8dc3471] .dfm-table .v-data-table__tr--mobile{display:none!important}.dfm-drag-select-rect[data-v-c8dc3471]{position:absolute;background:rgba(22,119,255,.12);border-radius:3px;pointer-events:none;z-index:10}.dfm-action-btn[data-v-c8dc3471]{color:var(--desktop-window-text-muted);font-size:11px;padding:0 4px;min-width:20px;height:20px}.dfm-action-btn[data-v-c8dc3471]:hover{color:var(--desktop-window-text);background:var(--desktop-window-control-hover)}.dfm-dialog__warn-icon[data-v-c8dc3471]{font-size:36px;color:var(--color-warning, #faad14)}.dfm-dialog__desc[data-v-c8dc3471]{margin:0;color:var(--desktop-window-text);font-size:14px;text-align:center;line-height:1.6}.dfm-overwrite-options[data-v-c8dc3471]{display:flex;align-items:center;gap:8px;margin-top:4px}.dfm-btn[data-v-c8dc3471]{padding:7px 16px;border:none;border-radius:6px;font-size:13px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all .2s;color:var(--desktop-window-text);white-space:nowrap}.dfm-btn[data-v-c8dc3471]:disabled{opacity:.5;cursor:not-allowed}.dfm-btn--primary[data-v-c8dc3471]{background:var(--color-blue-5, #1677ff);color:#fff}.dfm-btn--primary[data-v-c8dc3471]:hover:not(:disabled){background:var(--color-blue-6, #4096ff)}.dfm-btn--default[data-v-c8dc3471]{background:var(--desktop-window-titlebar-bg);border:1px solid var(--desktop-window-border)}.dfm-btn--default[data-v-c8dc3471]:hover:not(:disabled){background:var(--desktop-window-control-hover)}.dfm-vuetify[data-v-c8dc3471]{min-width:0}.dfm-vuetify .dfm-toolbar[data-v-c8dc3471]{display:flex;align-items:center;gap:8px;padding:8px 12px}.dfm-vuetify .dfm-toolbar>.v-input[data-v-c8dc3471]{max-width:260px}.dfm-nav[data-v-c8dc3471]{display:flex;align-items:center;gap:8px;padding:8px 12px}.dfm-breadcrumbs[data-v-c8dc3471]{display:flex;align-items:center;flex-wrap:wrap;gap:2px}.dfm-breadcrumb-separator[data-v-c8dc3471]{margin-left:8px;opacity:.5}.dfm-menu-list[data-v-c8dc3471]{display:flex;flex-direction:column;gap:2px;padding:8px!important}.dfm-menu-list .v-btn[data-v-c8dc3471]{justify-content:flex-start}.dfm-file-name[data-v-c8dc3471]{display:inline-flex;align-items:center;gap:8px}.dfm-empty[data-v-c8dc3471],.dfm-loading-dialog[data-v-c8dc3471]{display:flex;align-items:center;justify-content:center;gap:10px;padding:32px;opacity:.65}.dfm-dialog-label[data-v-c8dc3471]{margin:12px 0 4px;font-weight:600}.dfm-permission[data-v-c8dc3471]{margin:10px 0}.desktop-image-viewer[data-v-7d972d04]{display:flex;flex-direction:column;height:100%;color:var(--desktop-window-text)}.image-view[data-v-7d972d04]{flex:1;display:flex;justify-content:center;align-items:center;overflow:auto;padding:16px}.image-preview[data-v-7d972d04]{width:100%;height:100%;max-width:100%;max-height:100%}.image-footer[data-v-7d972d04]{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;background-color:var(--desktop-window-titlebar-bg)}.image-name[data-v-7d972d04]{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:70%}.file-editor-card{display:flex;flex-direction:column;width:100%;min-height:0}.file-editor-content{flex:1;min-height:0;overflow:hidden}.file-editor-loading{display:grid;min-height:60vh;place-items:center}.file-editor-dialog--full .file-editor-card{height:100svh;border-radius:0!important}.file-manager-page[data-v-a1fed8c3]{width:100%;min-height:100%;overflow-x:hidden;box-sizing:border-box;transition:opacity .15s ease}.file-manager-container[data-v-a1fed8c3]{width:100%;max-width:var(--app-max-width);margin:0 auto;padding:20px 24px 32px;box-sizing:border-box}.file-manager-toolbar[data-v-a1fed8c3]{width:100%;margin-bottom:16px}.file-manager-toolbar>.v-col[data-v-a1fed8c3]{min-width:0}.file-manager-title[data-v-a1fed8c3],.file-manager-search[data-v-a1fed8c3],.file-manager-actions[data-v-a1fed8c3]{display:flex;align-items:center}.file-manager-title[data-v-a1fed8c3]{gap:10px;color:var(--text-color);font-size:20px;font-weight:600}.file-manager-search[data-v-a1fed8c3]{justify-content:center}.file-manager-actions[data-v-a1fed8c3]{justify-content:flex-end;flex-wrap:wrap;gap:8px}.file-upload-input[data-v-a1fed8c3]{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none}.file-manager-card[data-v-a1fed8c3]{overflow:hidden;background:var(--background-color-white)}.file-manager-card[data-v-a1fed8c3]>.v-card-text{padding:20px}.upload-status[data-v-a1fed8c3]{margin-bottom:12px}.upload-status-line[data-v-a1fed8c3],.upload-progress-line[data-v-a1fed8c3],.upload-task-list[data-v-a1fed8c3],.path-row[data-v-a1fed8c3]{display:flex;align-items:center}.upload-status-line[data-v-a1fed8c3]{min-width:0;gap:6px}.upload-file-name[data-v-a1fed8c3]{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.upload-task-list[data-v-a1fed8c3]{gap:3px}.upload-progress-line[data-v-a1fed8c3]{gap:12px;margin-top:4px;font-size:12px;white-space:nowrap}.path-row[data-v-a1fed8c3]{align-items:stretch;gap:10px;margin-bottom:16px}.disk-select[data-v-a1fed8c3]{flex:0 0 125px}.selected-count[data-v-a1fed8c3]{white-space:nowrap;color:var(--color-gray-7);font-size:12px}.selected-count-empty[data-v-a1fed8c3]{visibility:hidden}.file-name[data-v-a1fed8c3]{max-width:min(440px,42vw);justify-content:flex-start;color:inherit;text-transform:none}.file-name[data-v-a1fed8c3]:hover{color:rgb(var(--v-theme-primary))}.file-table[data-v-a1fed8c3]{background:transparent}.file-table[data-v-a1fed8c3] th,.file-table[data-v-a1fed8c3] td{white-space:nowrap}.file-breadcrumbs[data-v-a1fed8c3]{display:flex;flex:1;min-width:0;align-items:center;overflow-x:auto;padding:2px 8px;border-radius:16px;background:var(--color-gray-2)}.file-breadcrumbs .file-breadcrumbs-item[data-v-a1fed8c3]{min-width:32px;padding:0 8px;text-transform:none}.breadcrumb-separator[data-v-a1fed8c3]{color:var(--color-gray-7);font-size:12px}.dialog-stack[data-v-a1fed8c3]{display:flex;flex-direction:column;gap:12px}.dialog-help[data-v-a1fed8c3]{color:var(--color-gray-7);font-size:12px}.permission-grid[data-v-a1fed8c3]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}@media (max-width: 992px){.file-manager-container[data-v-a1fed8c3]{padding:16px 12px 28px}.file-manager-title[data-v-a1fed8c3]{font-size:18px}.file-manager-search[data-v-a1fed8c3],.file-manager-actions[data-v-a1fed8c3]{justify-content:flex-start}.path-row[data-v-a1fed8c3],.permission-grid[data-v-a1fed8c3]{grid-template-columns:1fr;flex-direction:column}.disk-select[data-v-a1fed8c3]{flex-basis:auto;width:100%}.file-name[data-v-a1fed8c3]{max-width:55vw}}