Fix healthchecks: install curl in backend and frontend images
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
FROM node:22-alpine
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -0,0 +1,22 @@
|
||||
const { Sequelize } = require('sequelize');
|
||||
|
||||
const sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: process.env.DB_PATH || './database.sqlite',
|
||||
logging: false,
|
||||
});
|
||||
|
||||
const connectDB = async () => {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('Database connected.');
|
||||
await sequelize.sync();
|
||||
console.log('Database synced.');
|
||||
} catch (err) {
|
||||
console.error('Database connection error:', err);
|
||||
// Retry after 5s
|
||||
setTimeout(connectDB, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { sequelize, connectDB };
|
||||
@@ -0,0 +1,23 @@
|
||||
const passport = require('passport');
|
||||
const JwtStrategy = require('passport-jwt').Strategy;
|
||||
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||
const User = require('../models/User');
|
||||
|
||||
const opts = {
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: process.env.JWT_SECRET || 'dev_secret_change_in_production',
|
||||
};
|
||||
|
||||
passport.use(new JwtStrategy(opts, async (jwt_payload, done) => {
|
||||
try {
|
||||
const user = await User.findByPk(jwt_payload.id);
|
||||
if (user) {
|
||||
return done(null, user);
|
||||
}
|
||||
return done(null, false);
|
||||
} catch (err) {
|
||||
return done(err, false);
|
||||
}
|
||||
}));
|
||||
|
||||
module.exports = passport;
|
||||
@@ -0,0 +1,6 @@
|
||||
const passport = require('passport');
|
||||
|
||||
// Protect routes with JWT
|
||||
const authenticate = passport.authenticate('jwt', { session: false });
|
||||
|
||||
module.exports = authenticate;
|
||||
@@ -0,0 +1,33 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
const User = require('./User');
|
||||
|
||||
const Block = sequelize.define('Block', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
svg_data: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
},
|
||||
category: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: 'Allgemein',
|
||||
},
|
||||
is_public: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
});
|
||||
|
||||
Block.belongsTo(User, { foreignKey: 'userId', allowNull: true, onDelete: 'SET NULL' });
|
||||
User.hasMany(Block, { foreignKey: 'userId' });
|
||||
|
||||
module.exports = Block;
|
||||
@@ -0,0 +1,29 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
const User = require('./User');
|
||||
|
||||
const Drawing = sequelize.define('Drawing', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'Unbenannt',
|
||||
},
|
||||
canvas_json: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
thumbnail: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
});
|
||||
|
||||
Drawing.belongsTo(User, { foreignKey: 'userId', onDelete: 'CASCADE' });
|
||||
User.hasMany(Drawing, { foreignKey: 'userId' });
|
||||
|
||||
module.exports = Drawing;
|
||||
@@ -0,0 +1,31 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
const User = sequelize.define('User', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true,
|
||||
allowNull: false,
|
||||
validate: { isEmail: true },
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
});
|
||||
|
||||
User.beforeCreate(async (user) => {
|
||||
user.password = await bcrypt.hash(user.password, 10);
|
||||
});
|
||||
|
||||
User.prototype.validatePassword = async function (password) {
|
||||
return bcrypt.compare(password, this.password);
|
||||
};
|
||||
|
||||
module.exports = User;
|
||||
Generated
+1903
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcryptjs": "^3.0.3",
|
||||
"cors": "^2.8.6",
|
||||
"dxf-parser": "^1.1.2",
|
||||
"express": "^5.2.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"multer": "^2.1.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"sequelize": "^6.37.8",
|
||||
"sqlite3": "^6.0.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Dimension Tool",
|
||||
"version": "1.0.0",
|
||||
"description": "Fügt ein Bemaßungswerkzeug für horizontale und vertikale Messungen hinzu.",
|
||||
"author": "Agent Zero"
|
||||
}
|
||||
@@ -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;
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
const { sequelize, connectDB } = require('./config/database');
|
||||
const Block = require('./models/Block');
|
||||
|
||||
const eventBlocks = [
|
||||
{
|
||||
name: 'Stuhl (Standard)',
|
||||
category: 'Bestuhlung',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 40 40">
|
||||
<rect x="5" y="5" width="30" height="30" rx="2" fill="#4a90d9" stroke="#2c5f8a" stroke-width="2"/>
|
||||
<rect x="10" y="28" width="20" height="4" rx="1" fill="#d4e6f1"/>
|
||||
<circle cx="20" cy="18" r="8" fill="#d4e6f1" stroke="#2c5f8a" stroke-width="1.5"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Tisch (Rechteck 180x80)',
|
||||
category: 'Möbel',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 180 80">
|
||||
<rect x="0" y="0" width="180" height="80" rx="4" fill="#e8d5b7" stroke="#8b7355" stroke-width="2"/>
|
||||
<rect x="5" y="5" width="170" height="70" rx="2" fill="#f5ead0" stroke="#c4a882" stroke-width="1"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Tisch (Rund Ø120)',
|
||||
category: 'Möbel',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 120 120">
|
||||
<circle cx="60" cy="60" r="58" fill="#e8d5b7" stroke="#8b7355" stroke-width="2"/>
|
||||
<circle cx="60" cy="60" r="50" fill="#f5ead0" stroke="#c4a882" stroke-width="1"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Bühne (Modular 200x100)',
|
||||
category: 'Bühne',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 200 100">
|
||||
<rect x="0" y="0" width="200" height="100" fill="#8b7355" stroke="#6b5335" stroke-width="2"/>
|
||||
<line x1="0" y1="25" x2="200" y2="25" stroke="#a08060" stroke-width="1"/>
|
||||
<line x1="0" y1="50" x2="200" y2="50" stroke="#a08060" stroke-width="1"/>
|
||||
<line x1="0" y1="75" x2="200" y2="75" stroke="#a08060" stroke-width="1"/>
|
||||
<line x1="50" y1="0" x2="50" y2="100" stroke="#a08060" stroke-width="1"/>
|
||||
<line x1="100" y1="0" x2="100" y2="100" stroke="#a08060" stroke-width="1"/>
|
||||
<line x1="150" y1="0" x2="150" y2="100" stroke="#a08060" stroke-width="1"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Stehtisch',
|
||||
category: 'Möbel',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 60 60">
|
||||
<circle cx="30" cy="30" r="28" fill="#e8d5b7" stroke="#8b7355" stroke-width="2"/>
|
||||
<circle cx="30" cy="30" r="24" fill="#f5ead0" stroke="#c4a882" stroke-width="1"/>
|
||||
<circle cx="30" cy="30" r="5" fill="#8b7355"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Barhocker',
|
||||
category: 'Bestuhlung',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 30 30">
|
||||
<circle cx="15" cy="12" r="10" fill="#4a90d9" stroke="#2c5f8a" stroke-width="2"/>
|
||||
<rect x="12" y="20" width="6" height="8" fill="#7f8c8d"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Mischpult',
|
||||
category: 'Technik',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 120 60">
|
||||
<rect x="0" y="0" width="120" height="60" rx="3" fill="#2c3e50" stroke="#1a252f" stroke-width="2"/>
|
||||
<rect x="5" y="5" width="110" height="15" fill="#34495e" stroke="#2c3e50" stroke-width="1"/>
|
||||
<circle cx="20" cy="40" r="5" fill="#e74c3c"/>
|
||||
<circle cx="40" cy="40" r="5" fill="#e67e22"/>
|
||||
<circle cx="60" cy="40" r="5" fill="#f1c40f"/>
|
||||
<circle cx="80" cy="40" r="5" fill="#3498db"/>
|
||||
<circle cx="100" cy="40" r="5" fill="#2ecc71"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Lautsprecher',
|
||||
category: 'Technik',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 40 60">
|
||||
<rect x="0" y="0" width="40" height="60" rx="3" fill="#34495e" stroke="#1a252f" stroke-width="2"/>
|
||||
<rect x="8" y="8" width="24" height="25" rx="12" fill="#1a252f"/>
|
||||
<circle cx="20" cy="20" r="6" fill="#7f8c8d"/>
|
||||
<rect x="8" y="40" width="24" height="5" rx="2" fill="#1a252f"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Trennwand',
|
||||
category: 'Raum',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 10 100">
|
||||
<rect x="0" y="0" width="10" height="100" fill="#bdc3c7" stroke="#7f8c8d" stroke-width="1"/>
|
||||
<line x1="2" y1="0" x2="2" y2="100" stroke="#95a5a6" stroke-width="0.5"/>
|
||||
<line x1="5" y1="0" x2="5" y2="100" stroke="#95a5a6" stroke-width="0.5"/>
|
||||
<line x1="8" y1="0" x2="8" y2="100" stroke="#95a5a6" stroke-width="0.5"/>
|
||||
</svg>`
|
||||
},
|
||||
{
|
||||
name: 'Notausgang-Schild',
|
||||
category: 'Sicherheit',
|
||||
is_public: true,
|
||||
svg_data: `<svg viewBox="0 0 40 20">
|
||||
<rect x="0" y="0" width="40" height="20" rx="2" fill="#27ae60" stroke="#1e8449" stroke-width="1"/>
|
||||
<polygon points="8,5 3,10 8,15 7,15 2,10 7,5" fill="white"/>
|
||||
<text x="20" y="15" font-size="8" fill="white" text-anchor="middle" font-family="Arial">EXIT</text>
|
||||
</svg>`
|
||||
},
|
||||
];
|
||||
|
||||
async function seed() {
|
||||
try {
|
||||
await connectDB();
|
||||
// Clear existing blocks
|
||||
await Block.destroy({ where: {} });
|
||||
console.log('Existing blocks cleared.');
|
||||
// Insert new blocks
|
||||
for (const block of eventBlocks) {
|
||||
await Block.create(block);
|
||||
}
|
||||
console.log(`${eventBlocks.length} blocks created successfully.`);
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
console.error('Seed failed:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
seed();
|
||||
@@ -0,0 +1,30 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const passport = require('passport');
|
||||
require('./config/passport'); // Load JWT strategy
|
||||
const { sequelize, connectDB } = require('./config/database');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
app.use(passport.initialize());
|
||||
|
||||
// Routes
|
||||
app.use('/api/auth', require('./routes/auth'));
|
||||
app.use('/api/drawings', require('./routes/drawings'));
|
||||
app.use('/api/blocks', require('./routes/blocks'));
|
||||
app.use('/api/dxf', require('./routes/dxf'));
|
||||
app.use('/api/plugins', require('./routes/plugins'));
|
||||
|
||||
// Health check
|
||||
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
|
||||
|
||||
// Connect DB and start
|
||||
connectDB().then(() => {
|
||||
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
|
||||
}).catch(err => {
|
||||
console.error('Unable to start server:', err);
|
||||
});
|
||||
Reference in New Issue
Block a user