massive changes and it works

This commit is contained in:
2025-11-23 10:58:24 -06:00
parent 30aa969433
commit 2a0ff98834
3499 changed files with 7770 additions and 634687 deletions

View File

@@ -1,9 +1,39 @@
const API_BASE = '/api';
// Global auth error handler - will be set by useAuth hook
let onAuthError = null;
export const setAuthErrorHandler = (handler) => {
onAuthError = handler;
};
const handleAuthError = (response) => {
if (response.status === 401 || response.status === 403) {
// Trigger auth error handler if set (this will clear user state)
if (onAuthError) {
onAuthError();
}
// Force a re-check of auth status to ensure login is shown
// This ensures the App component re-renders with user=null
if (typeof window !== 'undefined') {
// Dispatch a custom event that useAuth can listen to
window.dispatchEvent(new CustomEvent('auth-error'));
}
}
};
export const api = {
async get(endpoint) {
const response = await fetch(`${API_BASE}${endpoint}`);
const response = await fetch(`${API_BASE}${endpoint}`, {
credentials: 'include', // Include cookies for session
});
if (!response.ok) {
// Handle auth errors before parsing response
// Don't redirect on /auth/me - that's the auth check itself
if ((response.status === 401 || response.status === 403) && !endpoint.startsWith('/auth/')) {
handleAuthError(response);
// Don't redirect - let React handle UI change through state
}
const errorData = await response.json().catch(() => null);
const errorMessage = errorData?.error || response.statusText;
throw new Error(errorMessage);
@@ -16,8 +46,15 @@ export const api = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
credentials: 'include', // Include cookies for session
});
if (!response.ok) {
// Handle auth errors before parsing response
// Don't redirect on /auth/* endpoints - those are login/logout
if ((response.status === 401 || response.status === 403) && !endpoint.startsWith('/auth/')) {
handleAuthError(response);
// Don't redirect - let React handle UI change through state
}
const errorData = await response.json().catch(() => null);
const errorMessage = errorData?.error || response.statusText;
throw new Error(errorMessage);
@@ -28,8 +65,15 @@ export const api = {
async delete(endpoint) {
const response = await fetch(`${API_BASE}${endpoint}`, {
method: 'DELETE',
credentials: 'include', // Include cookies for session
});
if (!response.ok) {
// Handle auth errors before parsing response
// Don't redirect on /auth/* endpoints
if ((response.status === 401 || response.status === 403) && !endpoint.startsWith('/auth/')) {
handleAuthError(response);
// Don't redirect - let React handle UI change through state
}
const errorData = await response.json().catch(() => null);
const errorMessage = errorData?.error || response.statusText;
throw new Error(errorMessage);
@@ -37,19 +81,61 @@ export const api = {
return response.json();
},
async uploadFile(endpoint, file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_BASE}${endpoint}`, {
method: 'POST',
body: formData,
async uploadFile(endpoint, file, onProgress, mainBlendFile) {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('file', file);
if (mainBlendFile) {
formData.append('main_blend_file', mainBlendFile);
}
const xhr = new XMLHttpRequest();
// Track upload progress
if (onProgress) {
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
onProgress(percentComplete);
}
});
}
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const response = JSON.parse(xhr.responseText);
resolve(response);
} catch (err) {
resolve(xhr.responseText);
}
} else {
// Handle auth errors
if (xhr.status === 401 || xhr.status === 403) {
handleAuthError({ status: xhr.status });
// Don't redirect - let React handle UI change through state
}
try {
const errorData = JSON.parse(xhr.responseText);
reject(new Error(errorData.error || xhr.statusText));
} catch {
reject(new Error(xhr.statusText));
}
}
});
xhr.addEventListener('error', () => {
reject(new Error('Upload failed'));
});
xhr.addEventListener('abort', () => {
reject(new Error('Upload aborted'));
});
xhr.open('POST', `${API_BASE}${endpoint}`);
xhr.withCredentials = true; // Include cookies for session
xhr.send(formData);
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
const errorMessage = errorData?.error || response.statusText;
throw new Error(errorMessage);
}
return response.json();
},
};
@@ -61,6 +147,30 @@ export const auth = {
async logout() {
return api.post('/auth/logout');
},
async getProviders() {
return api.get('/auth/providers');
},
async isLocalLoginAvailable() {
return api.get('/auth/local/available');
},
async localRegister(email, name, password) {
return api.post('/auth/local/register', { email, name, password });
},
async localLogin(username, password) {
return api.post('/auth/local/login', { username, password });
},
async changePassword(oldPassword, newPassword, targetUserId = null) {
const body = { old_password: oldPassword, new_password: newPassword };
if (targetUserId !== null) {
body.target_user_id = targetUserId;
}
return api.post('/auth/change-password', body);
},
};
export const jobs = {
@@ -80,19 +190,23 @@ export const jobs = {
return api.delete(`/jobs/${id}`);
},
async uploadFile(jobId, file) {
return api.uploadFile(`/jobs/${jobId}/upload`, file);
async delete(id) {
return api.post(`/jobs/${id}/delete`);
},
async uploadFile(jobId, file, onProgress, mainBlendFile) {
return api.uploadFile(`/jobs/${jobId}/upload`, file, onProgress, mainBlendFile);
},
async getFiles(jobId) {
return api.get(`/jobs/${jobId}/files`);
},
async downloadFile(jobId, fileId) {
downloadFile(jobId, fileId) {
return `${API_BASE}/jobs/${jobId}/files/${fileId}/download`;
},
async getVideoUrl(jobId) {
getVideoUrl(jobId) {
return `${API_BASE}/jobs/${jobId}/video`;
},
@@ -131,9 +245,7 @@ export const jobs = {
};
export const runners = {
async list() {
return api.get('/runners');
},
// Non-admin runner list removed - use admin.listRunners() instead
};
export const admin = {
@@ -160,5 +272,25 @@ export const admin = {
async deleteRunner(runnerId) {
return api.delete(`/admin/runners/${runnerId}`);
},
async listUsers() {
return api.get('/admin/users');
},
async getUserJobs(userId) {
return api.get(`/admin/users/${userId}/jobs`);
},
async setUserAdminStatus(userId, isAdmin) {
return api.post(`/admin/users/${userId}/admin`, { is_admin: isAdmin });
},
async getRegistrationEnabled() {
return api.get('/admin/settings/registration');
},
async setRegistrationEnabled(enabled) {
return api.post('/admin/settings/registration', { enabled });
},
};