Files
MyClub/frontend/src/services/files.ts
T
Tomas Dvorak 8762bde4bf dev day #89
2025-11-11 10:29:30 +01:00

138 lines
3.5 KiB
TypeScript

import api from './api';
// Use shared API_URL which already resolves to '/api/v1' under current origin
export interface FileInfo {
id: number;
filename: string;
file_path: string;
file_url: string;
url: string;
file_size: number;
size: number;
mime_type: string;
uploaded_by?: {
id: number;
email: string;
first_name: string;
last_name: string;
};
created_at: string;
usages?: FileUsage[];
usage_count: number;
md5_hash?: string;
}
export interface FileUsage {
id: number;
file_id: number;
entity_type: string;
entity_id: number;
field_name: string;
entity_info?: {
type: string;
id: number;
title?: string;
name?: string;
slug?: string;
url?: string;
position?: string;
};
}
export interface DuplicateFiles {
[hash: string]: FileInfo[];
}
export interface StorageUsage {
used_bytes: number;
used_count: number;
quota_mb: number;
quota_bytes: number;
percent: number;
warn_percent: number;
critical_percent: number;
status: 'ok' | 'warn' | 'critical';
}
export const getAllFiles = async (params?: {
search?: string;
mime_type?: string;
sort_by?: string;
sort_order?: string;
}): Promise<FileInfo[]> => {
const response = await api.get(`/admin/files`, { params });
return response.data;
};
export const getUnusedFiles = async (): Promise<FileInfo[]> => {
const response = await api.get(`/admin/files/unused`);
return response.data;
};
export const getDuplicateFiles = async (): Promise<DuplicateFiles> => {
const response = await api.get(`/admin/files/duplicates`);
return response.data;
};
export const getStorageUsage = async (): Promise<StorageUsage> => {
const response = await api.get(`/admin/files/usage`);
return response.data;
};
export const getFileUsages = async (fileId: number): Promise<any[]> => {
const response = await api.get(`/admin/files/${fileId}/usages`);
return response.data;
};
export const deleteFile = async (fileId: number, force: boolean = false): Promise<void> => {
await api.delete(`/admin/files/${fileId}`, { params: { force } });
};
export const scanAndSyncFiles = async (): Promise<{
message: string;
found_files: number;
new_files: number;
orphaned_files: number;
skipped_files?: number;
new_files_list?: string[];
orphaned_list?: string[];
}> => {
const response = await api.post(`/admin/files/scan`, {});
return response.data;
};
export const refreshFileTracking = async (entityType?: string): Promise<{
message: string;
stats: {
articles_scanned: number;
events_scanned: number;
players_scanned: number;
sponsors_scanned: number;
contacts_scanned: number;
teams_scanned: number;
settings_scanned: number;
};
}> => {
const response = await api.post(`/admin/files/refresh-tracking`, {}, { params: entityType ? { entity_type: entityType } : {} });
return response.data;
};
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
};
import { IconType } from 'react-icons';
import { FiImage, FiFileText, FiVideo, FiFile } from 'react-icons/fi';
export const getFileIcon = (mimeType: string): IconType => {
if (mimeType.startsWith('image/')) return FiImage;
if (mimeType === 'application/pdf') return FiFileText;
if (mimeType.startsWith('video/')) return FiVideo;
return FiFile;
};