Spaces:
Running
Running
Download src/services/file-system.js from Luca448/APP-Backend: direct link, hf CLI and curl.
- Browser
- Download file 8.79 kB
-
https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/src/services/file-system.js
- Command line
-
hf download hf://spaces/Luca448/APP-Backend/src/services/file-system.js
-
curl -L -o file-system.js https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/src/services/file-system.js
8.79 kB
| import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; | |
| import { db } from '../db/database.js'; | |
| class FileSystemService { | |
| constructor() { | |
| this.directory = Directory.Documents; | |
| this.baseFolder = 'SchoolMind'; | |
| } | |
| async init() { | |
| try { | |
| await Filesystem.mkdir({ | |
| path: this.baseFolder, | |
| directory: this.directory, | |
| recursive: true | |
| }); | |
| } catch (e) {} | |
| try { | |
| await Filesystem.mkdir({ | |
| path: `${this.baseFolder}/.trash`, | |
| directory: this.directory, | |
| recursive: true | |
| }); | |
| } catch (e) {} | |
| // Run trash cleanup on init | |
| this.cleanupTrash(); | |
| } | |
| /** | |
| * Liest alle Dateien in einem Ordner. | |
| */ | |
| async readDir(path = '') { | |
| try { | |
| const fullPath = path ? `${this.baseFolder}/${path}` : this.baseFolder; | |
| const result = await Filesystem.readdir({ | |
| path: fullPath, | |
| directory: this.directory | |
| }); | |
| // Filter out internal .trash folder | |
| return result.files.filter(f => { | |
| const name = typeof f === 'string' ? f : f.name; | |
| return name && !name.includes('.trash'); | |
| }); | |
| } catch (e) { | |
| console.error('Error reading directory:', e); | |
| return []; | |
| } | |
| } | |
| /** | |
| * Liest den Inhalt einer Datei als String. | |
| */ | |
| async readFile(filePath) { | |
| try { | |
| const fullPath = `${this.baseFolder}/${filePath}`; | |
| const result = await Filesystem.readFile({ | |
| path: fullPath, | |
| directory: this.directory, | |
| encoding: Encoding.UTF8 | |
| }); | |
| return result.data; | |
| } catch (e) { | |
| console.error('Error reading file:', e); | |
| return null; | |
| } | |
| } | |
| /** | |
| * Liest eine Datei als Base64-String. | |
| */ | |
| async readBinaryFile(filePath) { | |
| try { | |
| const fullPath = `${this.baseFolder}/${filePath}`; | |
| const result = await Filesystem.readFile({ | |
| path: fullPath, | |
| directory: this.directory | |
| }); | |
| return result.data; // returns base64 | |
| } catch (e) { | |
| console.error('Error reading binary file:', e); | |
| return null; | |
| } | |
| } | |
| /** | |
| * Speichert einen Text in einer Datei (überschreibt bestehende). | |
| */ | |
| async writeFile(filePath, data) { | |
| try { | |
| const fullPath = `${this.baseFolder}/${filePath}`; | |
| // Ensure parent directory exists first by getting the dir part | |
| const parts = fullPath.split('/'); | |
| parts.pop(); | |
| if (parts.length > 1) { | |
| const parentDir = parts.join('/'); | |
| try { | |
| await Filesystem.mkdir({ | |
| path: parentDir, | |
| directory: this.directory, | |
| recursive: true | |
| }); | |
| } catch (e) { | |
| // ignore "Directory exists" error | |
| } | |
| } | |
| await Filesystem.writeFile({ | |
| path: fullPath, | |
| data: data, | |
| directory: this.directory, | |
| encoding: Encoding.UTF8 | |
| }); | |
| return true; | |
| } catch (e) { | |
| console.error('Error writing file:', e); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Speichert eine binäre Datei (z.B. PDF) aus einem Base64 String. | |
| */ | |
| async writeBinaryFile(filePath, base64Data) { | |
| try { | |
| const fullPath = `${this.baseFolder}/${filePath}`; | |
| const parts = fullPath.split('/'); | |
| parts.pop(); | |
| if (parts.length > 1) { | |
| const parentDir = parts.join('/'); | |
| try { | |
| await Filesystem.mkdir({ path: parentDir, directory: this.directory, recursive: true }); | |
| } catch (e) {} | |
| } | |
| await Filesystem.writeFile({ | |
| path: fullPath, | |
| data: base64Data, // without encoding it writes binary | |
| directory: this.directory | |
| }); | |
| return true; | |
| } catch (e) { | |
| console.error('Error writing binary file:', e); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Erstellt einen neuen Ordner. | |
| */ | |
| async createFolder(path) { | |
| try { | |
| const fullPath = `${this.baseFolder}/${path}`; | |
| await Filesystem.mkdir({ | |
| path: fullPath, | |
| directory: this.directory, | |
| recursive: true | |
| }); | |
| return true; | |
| } catch (e) { | |
| console.error('Error creating folder:', e); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Löscht eine Datei oder einen Ordner. | |
| */ | |
| async deletePath(path, isDir = false) { | |
| try { | |
| const fullPath = `${this.baseFolder}/${path}`; | |
| if (isDir) { | |
| await Filesystem.rmdir({ | |
| path: fullPath, | |
| directory: this.directory, | |
| recursive: true | |
| }); | |
| } else { | |
| await Filesystem.deleteFile({ | |
| path: fullPath, | |
| directory: this.directory | |
| }); | |
| } | |
| return true; | |
| } catch (e) { | |
| console.error('Error deleting path:', e); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Verschiebt eine Datei oder einen Ordner in den Papierkorb (.trash). | |
| */ | |
| async moveToTrash(path, isDir = false) { | |
| try { | |
| const id = 'trash_' + Date.now(); | |
| const fromPath = `${this.baseFolder}/${path}`; | |
| const toPath = `${this.baseFolder}/.trash/${id}`; | |
| await Filesystem.rename({ | |
| from: fromPath, | |
| to: toPath, | |
| directory: this.directory | |
| }); | |
| db.addTrashItem(id, path, isDir); | |
| return true; | |
| } catch (e) { | |
| console.error('Error moving to trash:', e); | |
| alert('Konnte Element nicht in den Papierkorb verschieben: ' + e.message); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Stellt ein Element aus dem Papierkorb wieder her. | |
| */ | |
| async restoreFromTrash(id) { | |
| try { | |
| const items = db.getTrashItems(); | |
| const item = items.find(i => i.id === id); | |
| if (!item) return false; | |
| const fromPath = `${this.baseFolder}/.trash/${id}`; | |
| let toPath = `${this.baseFolder}/${item.original_path}`; | |
| // Verify the source file actually exists | |
| try { | |
| await Filesystem.stat({ | |
| path: fromPath, | |
| directory: this.directory | |
| }); | |
| } catch (e) { | |
| // If it doesn't exist, remove from DB and clean up the UI | |
| db.removeTrashItem(id); | |
| alert('Dieses Element konnte nicht wiederhergestellt werden, da die eigentliche Datei nicht mehr im Papierkorb-Ordner gefunden wurde (vermutlich wurde sie bereits gelöscht). Der fehlerhafte Eintrag wird jetzt entfernt.'); | |
| return false; | |
| } | |
| // Ensure parent directory exists for the restored item | |
| const parts = toPath.split('/'); | |
| parts.pop(); | |
| if (parts.length > 1) { | |
| const parentDir = parts.join('/'); | |
| try { | |
| await Filesystem.mkdir({ | |
| path: parentDir, | |
| directory: this.directory, | |
| recursive: true | |
| }); | |
| } catch (e) {} | |
| } | |
| // Conflict resolution: Check if destination already exists | |
| let finalToPath = toPath; | |
| let suffix = 1; | |
| let nameWithoutExt = item.original_path; | |
| let ext = ''; | |
| const isDir = item.is_dir === 1; | |
| if (!isDir && item.original_path.includes('.')) { | |
| const lastDot = item.original_path.lastIndexOf('.'); | |
| nameWithoutExt = item.original_path.substring(0, lastDot); | |
| ext = item.original_path.substring(lastDot); | |
| } | |
| while (true) { | |
| try { | |
| await Filesystem.stat({ | |
| path: finalToPath, | |
| directory: this.directory | |
| }); | |
| // If stat succeeds, file exists! We must try a new name. | |
| finalToPath = `${this.baseFolder}/${nameWithoutExt} (${suffix})${ext}`; | |
| suffix++; | |
| } catch (e) { | |
| // Stat failed, meaning file does NOT exist. We found a valid path! | |
| break; | |
| } | |
| } | |
| await Filesystem.rename({ | |
| from: fromPath, | |
| to: finalToPath, | |
| directory: this.directory | |
| }); | |
| // If the name changed and it was a folder, we might want to update metadata if needed, but it's okay, it just loses its custom icon if renamed. | |
| db.removeTrashItem(id); | |
| return true; | |
| } catch (e) { | |
| console.error('Error restoring from trash:', e); | |
| alert('Restore Error: ' + e.message); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Löscht endgültig alle Elemente, die älter als 30 Tage sind. | |
| */ | |
| async cleanupTrash() { | |
| try { | |
| const items = db.getTrashItems(); | |
| const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; | |
| const now = Date.now(); | |
| for (const item of items) { | |
| const deletedAt = new Date(item.deleted_at).getTime(); | |
| if (now - deletedAt > THIRTY_DAYS_MS) { | |
| const isDir = item.is_dir === 1; | |
| const success = await this.deletePath(`.trash/${item.id}`, isDir); | |
| if (success) { | |
| db.removeTrashItem(item.id); | |
| } | |
| } | |
| } | |
| } catch (e) { | |
| console.error('Error during trash cleanup:', e); | |
| } | |
| } | |
| } | |
| export const fsService = new FileSystemService(); | |