@elementspanel/epanel-plugin-file 0.0.0-stage → 1.0.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/LICENSE +201 -0
- package/README.md +4 -2
- package/daemon/backend/index.cjs +3885 -0
- package/daemon/plugin.json +9 -0
- package/package.json +49 -4
- package/panel/backend/index.cjs +513 -0
- package/panel/frontend/assets/style-0-style-9-_panel-plugin-build-entry_file-eb11377f.css +1 -0
- package/panel/frontend/panel-plugin-file-b9399327.js +1 -0
- package/panel/plugin.json +16 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "file",
|
|
3
|
+
"name": "File Manager",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"dual": true,
|
|
6
|
+
"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.",
|
|
7
|
+
"priority": 30,
|
|
8
|
+
"backend": "backend/index.cjs"
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementspanel/epanel-plugin-file",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
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
|
+
"directory": "panel",
|
|
32
|
+
"folder": "file"
|
|
33
|
+
},
|
|
34
|
+
"daemon": {
|
|
35
|
+
"directory": "daemon",
|
|
36
|
+
"folder": "file"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@elementspanel/panel": "1.0.0",
|
|
41
|
+
"@elementspanel/daemon": "1.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"@elementspanel/panel": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
47
|
+
"@elementspanel/daemon": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -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}}
|