Add admin invites and user workspace management tools.
Implement invite-token registration with optional email delivery, add admin UI actions for creating invites and opening user workspaces, and support superuser workspace override while preserving per-user code isolation with shared read-only lib. Made-with: Cursor
This commit is contained in:
@@ -42,16 +42,22 @@ class TextEditor {
|
||||
this.ledPanelDismissed = false;
|
||||
this.lastLedFrame = null;
|
||||
this.ledPanelWindow = null;
|
||||
this.workspaceUserId = null;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
try {
|
||||
const fromQuery = new URLSearchParams(window.location.search).get('api_key');
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fromQuery = params.get('api_key');
|
||||
const workspaceUserId = params.get('workspace_user_id');
|
||||
if (fromQuery) {
|
||||
sessionStorage.setItem('python-editor.api_key', fromQuery);
|
||||
}
|
||||
if (workspaceUserId && /^\d+$/.test(workspaceUserId)) {
|
||||
this.workspaceUserId = workspaceUserId;
|
||||
}
|
||||
} catch (_error) {
|
||||
// Ignore query / storage failures.
|
||||
}
|
||||
@@ -61,10 +67,22 @@ class TextEditor {
|
||||
this.setupDevAutoReload();
|
||||
this.updateRunButtonState();
|
||||
this.setLspStatus('LSP: n/a', 'Open a Python file for diagnostics');
|
||||
this.updateWorkspaceBanner();
|
||||
this.prewarmPyWorker();
|
||||
this.loadInitialDirectoryState().then(() => this.restoreSessionTabs());
|
||||
}
|
||||
|
||||
updateWorkspaceBanner() {
|
||||
const badge = document.getElementById('workspace-badge');
|
||||
if (!badge) return;
|
||||
if (this.workspaceUserId) {
|
||||
badge.textContent = `Workspace: user ${this.workspaceUserId}`;
|
||||
badge.classList.remove('hidden');
|
||||
} else {
|
||||
badge.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
setupDevAutoReload() {
|
||||
const isLocalhost =
|
||||
window.location.hostname === 'localhost' ||
|
||||
@@ -105,7 +123,19 @@ class TextEditor {
|
||||
}
|
||||
next.headers = headers;
|
||||
next.credentials = 'include';
|
||||
return fetch(url, next);
|
||||
let finalUrl = url;
|
||||
if (this.workspaceUserId && typeof url === 'string' && url.startsWith('/api/')) {
|
||||
try {
|
||||
const parsed = new URL(url, window.location.origin);
|
||||
if (!parsed.searchParams.has('workspace_user_id')) {
|
||||
parsed.searchParams.set('workspace_user_id', this.workspaceUserId);
|
||||
}
|
||||
finalUrl = parsed.pathname + parsed.search;
|
||||
} catch (_error) {
|
||||
// ignore URL parse failure and use original
|
||||
}
|
||||
}
|
||||
return fetch(finalUrl, next);
|
||||
}
|
||||
|
||||
disposePyWorker() {
|
||||
|
||||
Reference in New Issue
Block a user