nas-samba: rebuilt as single Ubuntu image with Samba + CloudNAS (no MongoDB)
This commit is contained in:
136
apps/nas-samba/source/app/frontend/src/lib/api.js
Normal file
136
apps/nas-samba/source/app/frontend/src/lib/api.js
Normal file
@@ -0,0 +1,136 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_BACKEND_URL || '';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor to add auth token
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// Response interceptor to handle auth errors
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
window.location.reload();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Auth
|
||||
export const login = (username, password) =>
|
||||
api.post('/api/auth/login', { username, password });
|
||||
|
||||
export const getMe = () =>
|
||||
api.get('/api/auth/me');
|
||||
|
||||
// Users
|
||||
export const getUsers = () =>
|
||||
api.get('/api/users');
|
||||
|
||||
export const createUser = (userData) =>
|
||||
api.post('/api/users', userData);
|
||||
|
||||
export const updateUser = (userId, userData) =>
|
||||
api.put(`/api/users/${userId}`, userData);
|
||||
|
||||
export const deleteUser = (userId) =>
|
||||
api.delete(`/api/users/${userId}`);
|
||||
|
||||
// Files
|
||||
export const getFiles = (parentId = null, search = null) => {
|
||||
const params = new URLSearchParams();
|
||||
if (parentId) params.append('parent_id', parentId);
|
||||
if (search) params.append('search', search);
|
||||
return api.get(`/api/files?${params.toString()}`);
|
||||
};
|
||||
|
||||
export const getFile = (fileId) =>
|
||||
api.get(`/api/files/${fileId}`);
|
||||
|
||||
export const createFolder = (name, parentId = null) =>
|
||||
api.post('/api/files/folder', { name, parent_id: parentId, is_folder: true });
|
||||
|
||||
export const uploadFile = (file, parentId, onProgress) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
if (parentId) {
|
||||
formData.append('parent_id', parentId);
|
||||
}
|
||||
|
||||
return api.post('/api/files/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (onProgress) {
|
||||
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
onProgress(progress);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const renameFile = (fileId, newName) =>
|
||||
api.put(`/api/files/${fileId}/rename`, { new_name: newName });
|
||||
|
||||
export const moveFile = (fileId, destinationId) =>
|
||||
api.put(`/api/files/${fileId}/move`, { destination_id: destinationId });
|
||||
|
||||
export const copyFile = (fileId, destinationId) =>
|
||||
api.post(`/api/files/${fileId}/copy`, { destination_id: destinationId });
|
||||
|
||||
export const deleteFile = (fileId) =>
|
||||
api.delete(`/api/files/${fileId}`);
|
||||
|
||||
export const bulkDeleteFiles = (fileIds) =>
|
||||
api.post('/api/files/bulk-delete', fileIds);
|
||||
|
||||
export const getDownloadUrl = (fileId) =>
|
||||
`${API_URL}/api/files/${fileId}/download`;
|
||||
|
||||
export const getPreviewUrl = (fileId) =>
|
||||
`${API_URL}/api/files/${fileId}/preview`;
|
||||
|
||||
export const getBreadcrumb = (fileId) =>
|
||||
api.get(`/api/files/${fileId}/breadcrumb`);
|
||||
|
||||
// Shares
|
||||
export const getShares = () =>
|
||||
api.get('/api/shares');
|
||||
|
||||
export const createShare = (fileId, expiresInHours = 24, password = null) =>
|
||||
api.post('/api/shares', { file_id: fileId, expires_in_hours: expiresInHours, password });
|
||||
|
||||
export const deleteShare = (shareId) =>
|
||||
api.delete(`/api/shares/${shareId}`);
|
||||
|
||||
export const getSharedFileInfo = (token) =>
|
||||
api.get(`/api/share/${token}`);
|
||||
|
||||
export const downloadSharedFile = (token, password = null) => {
|
||||
const params = password ? `?password=${encodeURIComponent(password)}` : '';
|
||||
return `${API_URL}/api/share/${token}/download${params}`;
|
||||
};
|
||||
|
||||
// Activity
|
||||
export const getActivity = (limit = 50) =>
|
||||
api.get(`/api/activity?limit=${limit}`);
|
||||
|
||||
// Stats
|
||||
export const getStats = () =>
|
||||
api.get('/api/stats');
|
||||
|
||||
export default api;
|
||||
33
apps/nas-samba/source/app/frontend/src/lib/utils.js
Normal file
33
apps/nas-samba/source/app/frontend/src/lib/utils.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function formatBytes(bytes, decimals = 1) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
export function formatDate(dateString) {
|
||||
if (!dateString) return '-';
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diff = Math.floor((now - date) / 1000);
|
||||
|
||||
if (diff < 60) return 'Just now';
|
||||
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
||||
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
|
||||
if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`;
|
||||
|
||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
||||
}
|
||||
|
||||
export function getFileExtension(filename) {
|
||||
return filename.includes('.') ? filename.split('.').pop().toLowerCase() : '';
|
||||
}
|
||||
Reference in New Issue
Block a user