@pixelbyte-software/pixcode 1.51.4 → 1.51.5
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/dist/assets/{index-HfGHXhD6.js → index-CpbSjzK7.js} +179 -182
- package/dist/assets/index-oDq76nSq.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/cli.js +1 -1
- package/dist-server/server/cli.js.map +1 -1
- package/dist-server/server/index.js +530 -34
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/middleware/auth.js +20 -3
- package/dist-server/server/middleware/auth.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +33 -17
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/routes/commands.js +20 -9
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/live-view.js +30 -7
- package/dist-server/server/routes/live-view.js.map +1 -1
- package/dist-server/server/routes/messages.js +30 -0
- package/dist-server/server/routes/messages.js.map +1 -1
- package/dist-server/server/routes/network.js +3 -2
- package/dist-server/server/routes/network.js.map +1 -1
- package/dist-server/server/routes/projects.js +4 -3
- package/dist-server/server/routes/projects.js.map +1 -1
- package/dist-server/server/routes/remote.js +2 -1
- package/dist-server/server/routes/remote.js.map +1 -1
- package/dist-server/server/services/platformization.js +73 -0
- package/dist-server/server/services/platformization.js.map +1 -1
- package/package.json +1 -1
- package/scripts/update-git-install.mjs +15 -10
- package/server/cli.js +4 -4
- package/server/index.js +552 -34
- package/server/middleware/auth.js +26 -2
- package/server/modules/providers/provider.routes.ts +91 -53
- package/server/routes/commands.js +43 -32
- package/server/routes/live-view.js +47 -24
- package/server/routes/messages.js +47 -12
- package/server/routes/network.js +9 -8
- package/server/routes/projects.js +7 -6
- package/server/routes/remote.js +6 -5
- package/server/services/platformization.js +86 -13
- package/dist/assets/index-B9N-gfOQ.css +0 -32
|
@@ -3,9 +3,10 @@ import path from 'path';
|
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
|
|
6
|
-
import express from 'express';
|
|
7
|
-
|
|
8
|
-
import { addProjectManually, extractProjectDirectory } from '../projects.js';
|
|
6
|
+
import express from 'express';
|
|
7
|
+
|
|
8
|
+
import { addProjectManually, extractProjectDirectory } from '../projects.js';
|
|
9
|
+
import { requireAdmin } from '../middleware/auth.js';
|
|
9
10
|
|
|
10
11
|
const router = express.Router();
|
|
11
12
|
|
|
@@ -302,7 +303,7 @@ async function projectHasAnySessions(workspacePath) {
|
|
|
302
303
|
* composer and surface a "directory deleted" warning instead of letting
|
|
303
304
|
* the user fire prompts into a void.
|
|
304
305
|
*/
|
|
305
|
-
router.get('/:projectName/dir-status', async (req, res) => {
|
|
306
|
+
router.get('/:projectName/dir-status', requireAdmin, async (req, res) => {
|
|
306
307
|
const { projectName } = req.params;
|
|
307
308
|
try {
|
|
308
309
|
const actualPath = await extractProjectDirectory(projectName);
|
|
@@ -340,7 +341,7 @@ router.get('/:projectName/dir-status', async (req, res) => {
|
|
|
340
341
|
* matches ChatGPT's "New chat" which reuses the empty canvas until the
|
|
341
342
|
* user actually commits a message.
|
|
342
343
|
*/
|
|
343
|
-
router.post('/quick-start', async (req, res) => {
|
|
344
|
+
router.post('/quick-start', requireAdmin, async (req, res) => {
|
|
344
345
|
try {
|
|
345
346
|
await fs.mkdir(WORKSPACES_BASE, { recursive: true });
|
|
346
347
|
|
|
@@ -424,7 +425,7 @@ router.post('/quick-start', async (req, res) => {
|
|
|
424
425
|
* - githubTokenId?: number (optional, ID of stored token)
|
|
425
426
|
* - newGithubToken?: string (optional, one-time token)
|
|
426
427
|
*/
|
|
427
|
-
router.post('/create-workspace', async (req, res) => {
|
|
428
|
+
router.post('/create-workspace', requireAdmin, async (req, res) => {
|
|
428
429
|
try {
|
|
429
430
|
const { workspaceType, path: workspacePath, githubUrl, githubTokenId, newGithubToken, subfolderName } = req.body;
|
|
430
431
|
|
package/server/routes/remote.js
CHANGED
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
getPublicRemoteConnectionConfig,
|
|
6
6
|
saveRemoteConnectionConfig,
|
|
7
7
|
} from '../services/remote-connection.js';
|
|
8
|
-
import {
|
|
9
|
-
buildControlRoomSnapshot,
|
|
10
|
-
buildMobileConsoleLayout,
|
|
11
|
-
} from '../services/control-room.js';
|
|
8
|
+
import {
|
|
9
|
+
buildControlRoomSnapshot,
|
|
10
|
+
buildMobileConsoleLayout,
|
|
11
|
+
} from '../services/control-room.js';
|
|
12
|
+
import { requireAdmin } from '../middleware/auth.js';
|
|
12
13
|
|
|
13
14
|
const router = express.Router();
|
|
14
15
|
|
|
@@ -16,7 +17,7 @@ router.get('/config', (req, res) => {
|
|
|
16
17
|
res.json({ success: true, connection: getPublicRemoteConnectionConfig() });
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
router.put('/config', (req, res) => {
|
|
20
|
+
router.put('/config', requireAdmin, (req, res) => {
|
|
20
21
|
try {
|
|
21
22
|
const connection = saveRemoteConnectionConfig(req.body || {});
|
|
22
23
|
res.json({ success: true, connection });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
4
5
|
import { promisify } from 'node:util';
|
|
5
6
|
|
|
6
7
|
import bcrypt from 'bcryptjs';
|
|
@@ -174,6 +175,25 @@ function projectMatches(collaborator, project = {}) {
|
|
|
174
175
|
);
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
function isPathInside(basePath, targetPath) {
|
|
179
|
+
const relative = path.relative(path.resolve(basePath), path.resolve(targetPath));
|
|
180
|
+
return relative === '' || (relative && !relative.startsWith('..') && !path.isAbsolute(relative));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeAllowedRoots(input) {
|
|
184
|
+
const roots = Array.isArray(input) ? input : [];
|
|
185
|
+
const normalized = roots
|
|
186
|
+
.filter((entry) => typeof entry === 'string')
|
|
187
|
+
.map((entry) => entry.trim().replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/g, ''))
|
|
188
|
+
.map((entry) => entry || '.')
|
|
189
|
+
.filter((entry) => !entry.includes('..'));
|
|
190
|
+
return Array.from(new Set(normalized.length > 0 ? normalized : ['.']));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function collaboratorAllowedRoots(collaborator) {
|
|
194
|
+
return normalizeAllowedRoots(collaborator.allowedRoots || collaborator.allowedFolders || ['.']);
|
|
195
|
+
}
|
|
196
|
+
|
|
177
197
|
export function userHasProjectAccess(user, project, capability = 'viewFiles') {
|
|
178
198
|
if (isAdminUser(user)) return true;
|
|
179
199
|
if (!user?.id && !user?.userId) return false;
|
|
@@ -198,6 +218,55 @@ export function userHasProjectAccess(user, project, capability = 'viewFiles') {
|
|
|
198
218
|
});
|
|
199
219
|
}
|
|
200
220
|
|
|
221
|
+
export function getProjectAccessForUser(user, project, capability = 'viewFiles') {
|
|
222
|
+
if (isAdminUser(user)) {
|
|
223
|
+
return { unrestricted: true, allowedRoots: ['.'] };
|
|
224
|
+
}
|
|
225
|
+
if (!user?.id && !user?.userId) return { unrestricted: false, allowedRoots: [] };
|
|
226
|
+
|
|
227
|
+
const userId = Number(user.id ?? user.userId);
|
|
228
|
+
const username = String(user.username || '').toLowerCase();
|
|
229
|
+
const store = readStore();
|
|
230
|
+
const allowedRoots = [];
|
|
231
|
+
|
|
232
|
+
for (const collaborator of store.projectCollaborators) {
|
|
233
|
+
if (collaborator.status === 'disabled') continue;
|
|
234
|
+
if (!projectMatches(collaborator, project)) continue;
|
|
235
|
+
const sameUser = Number(collaborator.userId) === userId ||
|
|
236
|
+
String(collaborator.userRef || '').toLowerCase() === username;
|
|
237
|
+
if (!sameUser) continue;
|
|
238
|
+
const capabilityAllowed = capability === 'viewFiles'
|
|
239
|
+
? collaborator.capabilities?.viewFiles !== false
|
|
240
|
+
: collaborator.capabilities?.[capability] === true;
|
|
241
|
+
if (!capabilityAllowed) continue;
|
|
242
|
+
allowedRoots.push(...collaboratorAllowedRoots(collaborator));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return { unrestricted: false, allowedRoots: Array.from(new Set(allowedRoots)) };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function userHasProjectPathAccess(user, project, targetPath, capability = 'viewFiles') {
|
|
249
|
+
if (isAdminUser(user)) return true;
|
|
250
|
+
const projectPath = project?.fullPath || project?.path || project?.projectPath;
|
|
251
|
+
if (!projectPath || !targetPath) return false;
|
|
252
|
+
const access = getProjectAccessForUser(user, project, capability);
|
|
253
|
+
return access.allowedRoots.some((root) => {
|
|
254
|
+
const allowedPath = root === '.' ? projectPath : path.resolve(projectPath, root);
|
|
255
|
+
return isPathInside(allowedPath, targetPath);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function filterFileTreeForUser(files = [], user, project, capability = 'viewFiles') {
|
|
260
|
+
if (isAdminUser(user)) return files;
|
|
261
|
+
const projectPath = project?.fullPath || project?.path || project?.projectPath;
|
|
262
|
+
if (!projectPath) return [];
|
|
263
|
+
return files.filter((entry) => {
|
|
264
|
+
const entryPath = entry?.path || entry?.fullPath || entry?.relativePath || '';
|
|
265
|
+
const absoluteEntryPath = path.isAbsolute(entryPath) ? entryPath : path.resolve(projectPath, entryPath);
|
|
266
|
+
return userHasProjectPathAccess(user, { ...project, fullPath: projectPath }, absoluteEntryPath, capability);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
201
270
|
export function filterProjectsForUser(projects = [], user) {
|
|
202
271
|
if (isAdminUser(user)) return projects;
|
|
203
272
|
return projects.filter((project) => userHasProjectAccess(user, project, 'viewFiles'));
|
|
@@ -427,11 +496,12 @@ export function createProjectCollaborator(input = {}, actorId = null) {
|
|
|
427
496
|
userId: targetUser.id,
|
|
428
497
|
userRef,
|
|
429
498
|
role,
|
|
430
|
-
capabilities: {
|
|
431
|
-
...capabilities,
|
|
432
|
-
...(input.capabilities && typeof input.capabilities === 'object' ? input.capabilities : {}),
|
|
433
|
-
},
|
|
434
|
-
|
|
499
|
+
capabilities: {
|
|
500
|
+
...capabilities,
|
|
501
|
+
...(input.capabilities && typeof input.capabilities === 'object' ? input.capabilities : {}),
|
|
502
|
+
},
|
|
503
|
+
allowedRoots: normalizeAllowedRoots(input.allowedRoots || input.allowedFolders || ['.']),
|
|
504
|
+
status: input.status || 'active',
|
|
435
505
|
createdAt: nowIso(),
|
|
436
506
|
updatedAt: nowIso(),
|
|
437
507
|
};
|
|
@@ -451,11 +521,14 @@ export function updateProjectCollaborator(collaboratorId, patch = {}, actorId =
|
|
|
451
521
|
...collaborator,
|
|
452
522
|
...patch,
|
|
453
523
|
id: collaborator.id,
|
|
454
|
-
capabilities: {
|
|
455
|
-
...collaborator.capabilities,
|
|
456
|
-
...(patch.capabilities && typeof patch.capabilities === 'object' ? patch.capabilities : {}),
|
|
457
|
-
},
|
|
458
|
-
|
|
524
|
+
capabilities: {
|
|
525
|
+
...collaborator.capabilities,
|
|
526
|
+
...(patch.capabilities && typeof patch.capabilities === 'object' ? patch.capabilities : {}),
|
|
527
|
+
},
|
|
528
|
+
allowedRoots: patch.allowedRoots || patch.allowedFolders
|
|
529
|
+
? normalizeAllowedRoots(patch.allowedRoots || patch.allowedFolders)
|
|
530
|
+
: collaboratorAllowedRoots(collaborator),
|
|
531
|
+
updatedAt: nowIso(),
|
|
459
532
|
};
|
|
460
533
|
return updated;
|
|
461
534
|
});
|