Fix healthchecks: install curl in backend and frontend images
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
const express = require('express');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const User = require('../models/User');
|
||||
const authenticate = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'dev_secret_change_in_production';
|
||||
|
||||
// Register
|
||||
router.post('/register', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({ error: 'Email and password required' });
|
||||
}
|
||||
const existing = await User.findOne({ where: { email } });
|
||||
if (existing) {
|
||||
return res.status(409).json({ error: 'User already exists' });
|
||||
}
|
||||
const user = await User.create({ email, password });
|
||||
const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, { expiresIn: '7d' });
|
||||
res.status(201).json({ token, user: { id: user.id, email: user.email } });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Registration failed' });
|
||||
}
|
||||
});
|
||||
|
||||
// Login
|
||||
router.post('/login', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({ error: 'Email and password required' });
|
||||
}
|
||||
const user = await User.findOne({ where: { email } });
|
||||
if (!user || !(await user.validatePassword(password))) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, { expiresIn: '7d' });
|
||||
res.json({ token, user: { id: user.id, email: user.email } });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Login failed' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get current user
|
||||
router.get('/me', authenticate, async (req, res) => {
|
||||
res.json({ id: req.user.id, email: req.user.email });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,59 @@
|
||||
const express = require('express');
|
||||
const authenticate = require('../middleware/auth');
|
||||
const Block = require('../models/Block');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Public blocks (no auth for listing)
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const blocks = await Block.findAll({
|
||||
where: { is_public: true },
|
||||
attributes: ['id', 'name', 'svg_data', 'category'],
|
||||
});
|
||||
res.json(blocks);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to list blocks' });
|
||||
}
|
||||
});
|
||||
|
||||
// Protected: create a new block (requires auth)
|
||||
router.post('/', authenticate, async (req, res) => {
|
||||
try {
|
||||
const { name, svg_data, category, is_public } = req.body;
|
||||
if (!name || !svg_data) {
|
||||
return res.status(400).json({ error: 'Name and svg_data required' });
|
||||
}
|
||||
const block = await Block.create({
|
||||
name,
|
||||
svg_data,
|
||||
category: category || 'Allgemein',
|
||||
is_public: is_public !== undefined ? is_public : true,
|
||||
userId: req.user.id,
|
||||
});
|
||||
res.status(201).json(block);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to create block' });
|
||||
}
|
||||
});
|
||||
|
||||
// Protected: delete block (only owner)
|
||||
router.delete('/:id', authenticate, async (req, res) => {
|
||||
try {
|
||||
const block = await Block.findOne({
|
||||
where: { id: req.params.id, userId: req.user.id },
|
||||
});
|
||||
if (!block) {
|
||||
return res.status(404).json({ error: 'Block not found or not authorized' });
|
||||
}
|
||||
await block.destroy();
|
||||
res.json({ message: 'Block deleted' });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to delete block' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,94 @@
|
||||
const express = require('express');
|
||||
const authenticate = require('../middleware/auth');
|
||||
const Drawing = require('../models/Drawing');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// All routes require authentication
|
||||
router.use(authenticate);
|
||||
|
||||
// List drawings for current user
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const drawings = await Drawing.findAll({
|
||||
where: { userId: req.user.id },
|
||||
attributes: ['id', 'name', 'updatedAt'],
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
res.json(drawings);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to list drawings' });
|
||||
}
|
||||
});
|
||||
|
||||
// Create new drawing
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { name, canvas_json } = req.body;
|
||||
const drawing = await Drawing.create({
|
||||
name: name || 'Unbenannt',
|
||||
canvas_json: canvas_json || '',
|
||||
userId: req.user.id,
|
||||
});
|
||||
res.status(201).json(drawing);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to create drawing' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get single drawing
|
||||
router.get('/:id', async (req, res) => {
|
||||
try {
|
||||
const drawing = await Drawing.findOne({
|
||||
where: { id: req.params.id, userId: req.user.id },
|
||||
});
|
||||
if (!drawing) {
|
||||
return res.status(404).json({ error: 'Drawing not found' });
|
||||
}
|
||||
res.json(drawing);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to get drawing' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update drawing
|
||||
router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
const drawing = await Drawing.findOne({
|
||||
where: { id: req.params.id, userId: req.user.id },
|
||||
});
|
||||
if (!drawing) {
|
||||
return res.status(404).json({ error: 'Drawing not found' });
|
||||
}
|
||||
const { name, canvas_json } = req.body;
|
||||
if (name !== undefined) drawing.name = name;
|
||||
if (canvas_json !== undefined) drawing.canvas_json = canvas_json;
|
||||
await drawing.save();
|
||||
res.json(drawing);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to update drawing' });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete drawing
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
const drawing = await Drawing.findOne({
|
||||
where: { id: req.params.id, userId: req.user.id },
|
||||
});
|
||||
if (!drawing) {
|
||||
return res.status(404).json({ error: 'Drawing not found' });
|
||||
}
|
||||
await drawing.destroy();
|
||||
res.json({ message: 'Drawing deleted' });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to delete drawing' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,101 @@
|
||||
const express = require('express');
|
||||
const authenticate = require('../middleware/auth');
|
||||
const multer = require('multer');
|
||||
const { parseString } = require('dxf-parser');
|
||||
const router = express.Router();
|
||||
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 10 * 1024 * 1024 } });
|
||||
|
||||
// Upload DXF file -> parse and return JSON representation (Fabric.js compatible)
|
||||
router.post('/import', authenticate, upload.single('file'), async (req, res) => {
|
||||
try {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: 'No file uploaded' });
|
||||
}
|
||||
const dxfContent = req.file.buffer.toString('utf-8');
|
||||
console.log('DXF file size:', dxfContent.length);
|
||||
const parsed = parseString(dxfContent);
|
||||
// Convert parsed entities to Fabric.js objects (simplified)
|
||||
const fabricObjects = [];
|
||||
if (parsed && parsed.entities) {
|
||||
parsed.entities.forEach(entity => {
|
||||
const type = entity.type;
|
||||
if (type === 'LINE') {
|
||||
fabricObjects.push({
|
||||
type: 'line',
|
||||
x1: entity.start.x, y1: entity.start.y,
|
||||
x2: entity.end.x, y2: entity.end.y,
|
||||
stroke: '#000', strokeWidth: 1,
|
||||
});
|
||||
} else if (type === 'CIRCLE') {
|
||||
fabricObjects.push({
|
||||
type: 'circle',
|
||||
left: entity.center.x - entity.radius,
|
||||
top: entity.center.y - entity.radius,
|
||||
radius: entity.radius,
|
||||
fill: 'transparent', stroke: '#000',
|
||||
});
|
||||
} else if (type === 'ARC') {
|
||||
// simplified arc handling
|
||||
fabricObjects.push({
|
||||
type: 'path',
|
||||
path: `M ${entity.center.x} ${entity.center.y}`, // placeholder
|
||||
stroke: '#000', strokeWidth: 1,
|
||||
});
|
||||
} else if (type === 'POLYLINE' || type === 'LWPOLYLINE') {
|
||||
// convert vertices to polygon
|
||||
if (entity.vertices && entity.vertices.length >= 2) {
|
||||
const points = entity.vertices.map(v => ({ x: v.x, y: v.y }));
|
||||
fabricObjects.push({
|
||||
type: 'polygon',
|
||||
points,
|
||||
stroke: '#000', strokeWidth: 1, fill: 'transparent',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
res.json({ objects: fabricObjects });
|
||||
} catch (err) {
|
||||
console.error('DXF import error:', err);
|
||||
res.status(500).json({ error: 'Failed to parse DXF file: ' + err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Export canvas JSON to DXF string
|
||||
router.post('/export', authenticate, (req, res) => {
|
||||
try {
|
||||
const { objects } = req.body;
|
||||
if (!objects || !Array.isArray(objects)) {
|
||||
return res.status(400).json({ error: 'Invalid objects array' });
|
||||
}
|
||||
// Build DXF content
|
||||
let dxf = '0\nSECTION\n2\nHEADER\n0\nENDSEC\n';
|
||||
dxf += '0\nSECTION\n2\nTABLES\n0\nENDSEC\n';
|
||||
dxf += '0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n';
|
||||
dxf += '0\nSECTION\n2\nENTITIES\n';
|
||||
objects.forEach(obj => {
|
||||
if (obj.type === 'line') {
|
||||
dxf += `0\nLINE\n8\n0\n10\n${obj.x1}\n20\n${obj.y1}\n11\n${obj.x2}\n21\n${obj.y2}\n`;
|
||||
} else if (obj.type === 'circle') {
|
||||
const cx = obj.left + obj.radius;
|
||||
const cy = obj.top + obj.radius;
|
||||
dxf += `0\nCIRCLE\n8\n0\n10\n${cx}\n20\n${cy}\n40\n${obj.radius}\n`;
|
||||
} else if (obj.type === 'polygon') {
|
||||
// LWPOLYLINE
|
||||
dxf += `0\nLWPOLYLINE\n8\n0\n90\n${obj.points.length}\n`;
|
||||
obj.points.forEach((pt, i) => {
|
||||
dxf += `10\n${pt.x}\n20\n${pt.y}\n`;
|
||||
});
|
||||
}
|
||||
});
|
||||
dxf += '0\nENDSEC\n0\nEOF\n';
|
||||
res.setHeader('Content-Type', 'application/dxf');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=drawing.dxf');
|
||||
res.send(dxf);
|
||||
} catch (err) {
|
||||
console.error('DXF export error:', err);
|
||||
res.status(500).json({ error: 'Export failed' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,56 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const PLUGINS_DIR = path.join(__dirname, '..', 'plugins');
|
||||
|
||||
// Ensure plugins directory exists
|
||||
if (!fs.existsSync(PLUGINS_DIR)) {
|
||||
fs.mkdirSync(PLUGINS_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// List all available plugins
|
||||
router.get('/', (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(PLUGINS_DIR)) {
|
||||
return res.json([]);
|
||||
}
|
||||
const plugins = [];
|
||||
const dirs = fs.readdirSync(PLUGINS_DIR);
|
||||
for (const dir of dirs) {
|
||||
const pluginPath = path.join(PLUGINS_DIR, dir);
|
||||
const manifestPath = path.join(pluginPath, 'plugin.json');
|
||||
if (fs.existsSync(manifestPath)) {
|
||||
try {
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
||||
plugins.push({ id: dir, ...manifest });
|
||||
} catch (e) {
|
||||
console.warn(`Invalid plugin manifest: ${dir}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.json(plugins);
|
||||
} catch (err) {
|
||||
console.error('Plugin list error:', err);
|
||||
res.status(500).json({ error: 'Failed to list plugins' });
|
||||
}
|
||||
});
|
||||
|
||||
// Serve plugin code as JavaScript module
|
||||
router.get('/:id/code', (req, res) => {
|
||||
try {
|
||||
const pluginDir = path.join(PLUGINS_DIR, req.params.id);
|
||||
const codePath = path.join(pluginDir, 'plugin.js');
|
||||
if (!fs.existsSync(codePath)) {
|
||||
return res.status(404).json({ error: 'Plugin code not found' });
|
||||
}
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
res.sendFile(codePath);
|
||||
} catch (err) {
|
||||
console.error('Plugin code error:', err);
|
||||
res.status(500).json({ error: 'Failed to load plugin code' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user