mirror of
https://github.com/stan-smith/FossFLOW.git
synced 2026-04-23 08:31:16 -04:00
247 lines
8.0 KiB
JavaScript
247 lines
8.0 KiB
JavaScript
import express from 'express';
|
|
import cors from 'cors';
|
|
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import dotenv from 'dotenv';
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const app = express();
|
|
const PORT = process.env.BACKEND_PORT || 3001;
|
|
|
|
// Configuration from environment variables
|
|
const STORAGE_ENABLED = process.env.ENABLE_SERVER_STORAGE === 'true';
|
|
const STORAGE_PATH = process.env.STORAGE_PATH || '/data/diagrams';
|
|
const ENABLE_GIT_BACKUP = process.env.ENABLE_GIT_BACKUP === 'true';
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(express.json({ limit: '10mb' }));
|
|
|
|
// Health check / Storage status endpoint
|
|
app.get('/api/storage/status', (req, res) => {
|
|
res.json({
|
|
enabled: STORAGE_ENABLED,
|
|
gitBackup: ENABLE_GIT_BACKUP,
|
|
version: '1.0.0'
|
|
});
|
|
});
|
|
|
|
// Only enable storage endpoints if storage is enabled
|
|
if (STORAGE_ENABLED) {
|
|
// Ensure storage directory exists
|
|
async function ensureStorageDir() {
|
|
try {
|
|
await fs.access(STORAGE_PATH);
|
|
console.log(`Storage directory exists: ${STORAGE_PATH}`);
|
|
|
|
// Log current files
|
|
const files = await fs.readdir(STORAGE_PATH);
|
|
console.log(`Current files in storage: ${files.length} files`);
|
|
if (files.length > 0) {
|
|
console.log('Files:', files.join(', '));
|
|
}
|
|
} catch {
|
|
console.log(`Creating storage directory: ${STORAGE_PATH}`);
|
|
await fs.mkdir(STORAGE_PATH, { recursive: true });
|
|
console.log(`Created storage directory: ${STORAGE_PATH}`);
|
|
}
|
|
}
|
|
|
|
// Initialize storage
|
|
ensureStorageDir().catch((err) => {
|
|
console.error('Failed to initialize storage:', err);
|
|
});
|
|
|
|
// List all diagrams
|
|
app.get('/api/diagrams', async (req, res) => {
|
|
try {
|
|
// First check if storage directory exists
|
|
try {
|
|
await fs.access(STORAGE_PATH);
|
|
} catch (err) {
|
|
console.error(`Storage directory does not exist: ${STORAGE_PATH}`);
|
|
return res.json([]); // Return empty array if directory doesn't exist
|
|
}
|
|
|
|
const files = await fs.readdir(STORAGE_PATH);
|
|
console.log(`Found ${files.length} files in ${STORAGE_PATH}:`, files);
|
|
const diagrams = [];
|
|
|
|
for (const file of files) {
|
|
if (file.endsWith('.json') && file !== 'metadata.json') {
|
|
try {
|
|
const filePath = path.join(STORAGE_PATH, file);
|
|
const stats = await fs.stat(filePath);
|
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
const data = JSON.parse(content);
|
|
|
|
// Extract name from various possible locations
|
|
const name = data.name || data.title || 'Untitled Diagram';
|
|
|
|
console.log(`Successfully read diagram: ${file} (name: ${name})`);
|
|
|
|
diagrams.push({
|
|
id: file.replace('.json', ''),
|
|
name: name,
|
|
lastModified: stats.mtime,
|
|
size: stats.size
|
|
});
|
|
} catch (fileError) {
|
|
console.error(`Error reading diagram file ${file}:`, fileError.message);
|
|
// Skip this file and continue with others
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Returning ${diagrams.length} diagrams`);
|
|
res.json(diagrams);
|
|
} catch (error) {
|
|
console.error('Error listing diagrams:', error);
|
|
res.status(500).json({ error: 'Failed to list diagrams', details: error.message });
|
|
}
|
|
});
|
|
|
|
// Get specific diagram
|
|
app.get('/api/diagrams/:id', async (req, res) => {
|
|
const diagramId = req.params.id;
|
|
console.log(`[GET /api/diagrams/${diagramId}] Loading diagram...`);
|
|
|
|
try {
|
|
const filePath = path.join(STORAGE_PATH, `${diagramId}.json`);
|
|
console.log(`[GET /api/diagrams/${diagramId}] Reading from: ${filePath}`);
|
|
|
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
const data = JSON.parse(content);
|
|
|
|
console.log(`[GET /api/diagrams/${diagramId}] Successfully loaded, size: ${content.length} bytes, items: ${data.items?.length || 0}`);
|
|
res.json(data);
|
|
} catch (error) {
|
|
if (error.code === 'ENOENT') {
|
|
console.error(`[GET /api/diagrams/${diagramId}] Diagram not found`);
|
|
res.status(404).json({ error: 'Diagram not found' });
|
|
} else {
|
|
console.error(`[GET /api/diagrams/${diagramId}] Error reading diagram:`, error);
|
|
res.status(500).json({ error: 'Failed to read diagram' });
|
|
}
|
|
}
|
|
});
|
|
|
|
// Save or update diagram
|
|
app.put('/api/diagrams/:id', async (req, res) => {
|
|
const diagramId = req.params.id;
|
|
console.log(`[PUT /api/diagrams/${diagramId}] Saving diagram...`);
|
|
|
|
try {
|
|
const filePath = path.join(STORAGE_PATH, `${diagramId}.json`);
|
|
const data = {
|
|
...req.body,
|
|
id: diagramId,
|
|
lastModified: new Date().toISOString()
|
|
};
|
|
|
|
const iconCount = data.icons?.length || 0;
|
|
const importedIconCount = (data.icons || []).filter(icon => icon.collection === 'imported').length;
|
|
console.log(`[PUT /api/diagrams/${diagramId}] Writing to: ${filePath}`);
|
|
console.log(`[PUT /api/diagrams/${diagramId}] Items: ${data.items?.length || 0}, Icons: ${iconCount} (${importedIconCount} imported)`);
|
|
|
|
await fs.writeFile(filePath, JSON.stringify(data, null, 2));
|
|
console.log(`[PUT /api/diagrams/${diagramId}] Successfully saved`);
|
|
|
|
// Git backup if enabled
|
|
if (ENABLE_GIT_BACKUP) {
|
|
// TODO: Implement git commit
|
|
console.log('[PUT] Git backup not yet implemented');
|
|
}
|
|
|
|
res.json({ success: true, id: diagramId });
|
|
} catch (error) {
|
|
console.error(`[PUT /api/diagrams/${diagramId}] Error saving diagram:`, error);
|
|
res.status(500).json({ error: 'Failed to save diagram' });
|
|
}
|
|
});
|
|
|
|
// Delete diagram
|
|
app.delete('/api/diagrams/:id', async (req, res) => {
|
|
try {
|
|
const filePath = path.join(STORAGE_PATH, `${req.params.id}.json`);
|
|
await fs.unlink(filePath);
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
if (error.code === 'ENOENT') {
|
|
res.status(404).json({ error: 'Diagram not found' });
|
|
} else {
|
|
console.error('Error deleting diagram:', error);
|
|
res.status(500).json({ error: 'Failed to delete diagram' });
|
|
}
|
|
}
|
|
});
|
|
|
|
// Create a new diagram
|
|
app.post('/api/diagrams', async (req, res) => {
|
|
try {
|
|
const id = req.body.id || `diagram_${Date.now()}`;
|
|
const filePath = path.join(STORAGE_PATH, `${id}.json`);
|
|
|
|
// Check if already exists
|
|
try {
|
|
await fs.access(filePath);
|
|
return res.status(409).json({ error: 'Diagram already exists' });
|
|
} catch {
|
|
// File doesn't exist, proceed
|
|
}
|
|
|
|
const data = {
|
|
...req.body,
|
|
id,
|
|
created: new Date().toISOString(),
|
|
lastModified: new Date().toISOString()
|
|
};
|
|
|
|
await fs.writeFile(filePath, JSON.stringify(data, null, 2));
|
|
res.status(201).json({ success: true, id });
|
|
} catch (error) {
|
|
console.error('Error creating diagram:', error);
|
|
res.status(500).json({ error: 'Failed to create diagram' });
|
|
}
|
|
});
|
|
|
|
} else {
|
|
// Storage disabled - return appropriate responses
|
|
app.get('/api/diagrams', (req, res) => {
|
|
res.status(503).json({ error: 'Server storage is disabled' });
|
|
});
|
|
|
|
app.get('/api/diagrams/:id', (req, res) => {
|
|
res.status(503).json({ error: 'Server storage is disabled' });
|
|
});
|
|
|
|
app.put('/api/diagrams/:id', (req, res) => {
|
|
res.status(503).json({ error: 'Server storage is disabled' });
|
|
});
|
|
|
|
app.delete('/api/diagrams/:id', (req, res) => {
|
|
res.status(503).json({ error: 'Server storage is disabled' });
|
|
});
|
|
|
|
app.post('/api/diagrams', (req, res) => {
|
|
res.status(503).json({ error: 'Server storage is disabled' });
|
|
});
|
|
}
|
|
|
|
// Start server
|
|
app.listen(PORT, () => {
|
|
console.log(`FossFLOW Backend Server running on port ${PORT}`);
|
|
console.log(`Server storage: ${STORAGE_ENABLED ? 'ENABLED' : 'DISABLED'}`);
|
|
if (STORAGE_ENABLED) {
|
|
console.log(`Storage path: ${STORAGE_PATH}`);
|
|
console.log(`Git backup: ${ENABLE_GIT_BACKUP ? 'ENABLED' : 'DISABLED'}`);
|
|
}
|
|
}); |