Dateien nach "/" hochladen
This commit is contained in:
commit
08631eaab5
5 files changed
+2777
No files matched your search
@@ -0,0 +1,2 @@
|
||||
ADMIN_WEBHOOK_URL=https://discord.com/api/webhooks/1402699603291275327/_yzlhPJt9-Qoc5-FysQDhzXRuJKBJp6nCAbvG4O3OBNCZ0esmr0DJhRDQR5LxzmiLZPT
|
||||
USER_WEBHOOK_URL=https://discord.com/api/webhooks/1401318995122061343/pJ6ShlG65cM3YoZnN37mT2julv-eDNYid9IyzNBxuzN5iYEmfWVkxfqDuIt4I9z0kTSu
|
||||
Generated
+2447
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "ticketsystem",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"body-parser": "^2.2.0",
|
||||
"cors": "^2.8.5",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"dotenv": "^17.2.1",
|
||||
"express": "^5.1.0",
|
||||
"luxon": "^3.7.1",
|
||||
"moment-timezone": "^0.6.0",
|
||||
"multer": "^2.0.2",
|
||||
"sqlite3": "^5.1.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import sqlite3 from 'sqlite3';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import moment from 'moment-timezone';
|
||||
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 = 3000;
|
||||
|
||||
// Load webhook URLs from environment
|
||||
const ADMIN_WEBHOOK_URL = process.env.ADMIN_WEBHOOK_URL;
|
||||
const USER_WEBHOOK_URL = process.env.USER_WEBHOOK_URL;
|
||||
|
||||
const timestamp = moment().utc().format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use('/assets', express.static(path.join(__dirname, 'assets')));
|
||||
|
||||
const dbPath = path.resolve(__dirname, 'tickets.db');
|
||||
const db = new sqlite3.Database(dbPath, err => {
|
||||
if (err) {
|
||||
console.error('Failed to connect to DB:', err.message);
|
||||
} else {
|
||||
console.log('Connected to SQLite DB.');
|
||||
}
|
||||
});
|
||||
|
||||
// Create tables
|
||||
db.serialize(() => {
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
requester TEXT,
|
||||
description TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
status TEXT,
|
||||
priority TEXT,
|
||||
category TEXT,
|
||||
total_time INTEGER DEFAULT 0
|
||||
)
|
||||
`);
|
||||
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS time_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
minutes INTEGER NOT NULL,
|
||||
comment TEXT,
|
||||
author TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(ticket_id) REFERENCES tickets(id)
|
||||
)
|
||||
`);
|
||||
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
message TEXT,
|
||||
author TEXT,
|
||||
role TEXT,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(ticket_id) REFERENCES tickets(id)
|
||||
)
|
||||
`);
|
||||
|
||||
db.run(`CREATE INDEX IF NOT EXISTS idx_time_logs_ticket ON time_logs(ticket_id)`);
|
||||
db.run(`CREATE INDEX IF NOT EXISTS idx_history_ticket ON history(ticket_id)`);
|
||||
});
|
||||
|
||||
// Log history
|
||||
function logHistory(ticketId, action, message, author = 'system', role = 'user') {
|
||||
const timestamp = moment().utc().format('YYYY-MM-DD HH:mm:ss');
|
||||
console.log(`Logging history for ticket: ${ticketId}, timestamp: ${timestamp}`); // Debugging line
|
||||
db.run(
|
||||
'INSERT INTO history (ticket_id, action, message, author, role, timestamp) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[ticketId, action, message, author, role, timestamp]
|
||||
);
|
||||
}
|
||||
|
||||
// GET webhook configuration for frontend
|
||||
app.get('/api/webhooks', (req, res) => {
|
||||
res.json({
|
||||
admin: ADMIN_WEBHOOK_URL || null,
|
||||
user: USER_WEBHOOK_URL || null,
|
||||
configured: {
|
||||
admin: !!ADMIN_WEBHOOK_URL,
|
||||
user: !!USER_WEBHOOK_URL
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// GET all tickets
|
||||
app.get('/api/tickets', (req, res) => {
|
||||
db.all(
|
||||
`SELECT id, title, requester, description, status, priority, category, total_time,
|
||||
strftime('%Y-%m-%dT%H:%M:%S', created_at) AS created_at,
|
||||
strftime('%Y-%m-%dT%H:%M:%S', updated_at) AS updated_at
|
||||
FROM tickets
|
||||
ORDER BY created_at DESC`,
|
||||
(err, rows) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// GET single ticket
|
||||
app.get('/api/tickets/:id', (req, res) => {
|
||||
db.get('SELECT * FROM tickets WHERE id = ?', [req.params.id], (err, row) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
if (!row) return res.status(404).json({ error: 'Ticket not found' });
|
||||
res.json(row);
|
||||
});
|
||||
});
|
||||
|
||||
// GET ticket history (ISO formatted date)
|
||||
app.get('/api/tickets/:id/history', (req, res) => {
|
||||
db.all(
|
||||
`SELECT id, ticket_id, action, message, author, role,
|
||||
strftime('%Y-%m-%dT%H:%M:%S', timestamp) AS date
|
||||
FROM history
|
||||
WHERE ticket_id = ?
|
||||
ORDER BY timestamp DESC`,
|
||||
[req.params.id],
|
||||
(err, rows) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// POST new ticket
|
||||
app.post('/api/tickets', (req, res) => {
|
||||
const { title, requester, description, status, priority, category } = req.body;
|
||||
const statusValue = status && status.trim() ? status : 'open';
|
||||
|
||||
// Get current Berlin time and convert it to UTC
|
||||
const berlinTime = moment.tz('Europe/Berlin');
|
||||
const utcTime = berlinTime.clone().utc().format('YYYY-MM-DD HH:mm:ss'); // Convert Berlin time to UTC
|
||||
|
||||
db.run(
|
||||
'INSERT INTO tickets (title, requester, description, status, priority, category, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[title, requester, description, statusValue, priority, category, utcTime, utcTime],
|
||||
function (err) {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
logHistory(this.lastID, 'Ticket Created', `Ticket "${title}" created`, requester);
|
||||
res.status(201).json({ id: this.lastID });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// PUT update ticket
|
||||
app.put('/api/tickets/:id', (req, res) => {
|
||||
const { title, requester, description, status, priority, category, author } = req.body;
|
||||
|
||||
// Get current Berlin time and convert it to UTC
|
||||
const berlinTime = moment.tz('Europe/Berlin');
|
||||
const utcTime = berlinTime.clone().utc().format('YYYY-MM-DD HH:mm:ss'); // Convert Berlin time to UTC
|
||||
|
||||
db.run(
|
||||
`UPDATE tickets SET
|
||||
title = ?,
|
||||
requester = ?,
|
||||
description = ?,
|
||||
status = ?,
|
||||
priority = ?,
|
||||
category = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?`,
|
||||
[title, requester, description, status, priority, category, utcTime, req.params.id],
|
||||
function (err) {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
if (this.changes === 0) return res.status(404).json({ error: 'Ticket not found' });
|
||||
logHistory(req.params.id, 'Ticket Updated', 'Ticket details updated', author || 'system');
|
||||
res.json({ message: 'Ticket updated' });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// DELETE ticket
|
||||
app.delete('/api/tickets/:id', (req, res) => {
|
||||
db.run('DELETE FROM tickets WHERE id = ?', [req.params.id], function (err) {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
if (this.changes === 0) return res.status(404).json({ error: 'Ticket not found' });
|
||||
logHistory(req.params.id, 'Ticket Deleted', 'Ticket deleted');
|
||||
res.json({ message: 'Ticket deleted' });
|
||||
});
|
||||
});
|
||||
|
||||
// POST comment with role
|
||||
app.post('/api/tickets/:id/comments', (req, res) => {
|
||||
const { author, role, content } = req.body;
|
||||
if (!author || !content) return res.status(400).json({ error: 'Author and content required' });
|
||||
|
||||
const timestamp = moment().utc().format('YYYY-MM-DD HH:mm:ss'); // Explicit UTC timestamp
|
||||
|
||||
db.run(
|
||||
'INSERT INTO history (ticket_id, action, message, author, role, timestamp) VALUES (?, "Comment", ?, ?, ?, ?)',
|
||||
[req.params.id, content, author, role || 'user', timestamp],
|
||||
function (err) {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
res.status(201).json({ id: this.lastID });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// GET comments (ISO formatted date)
|
||||
app.get('/api/tickets/:id/comments', (req, res) => {
|
||||
db.all(
|
||||
`SELECT id, ticket_id, message AS content, author, role,
|
||||
strftime('%Y-%m-%dT%H:%M:%S', timestamp) AS date
|
||||
FROM history
|
||||
WHERE ticket_id = ? AND action = "Comment"
|
||||
ORDER BY timestamp DESC`,
|
||||
[req.params.id],
|
||||
(err, rows) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// POST time log
|
||||
app.post('/api/tickets/:id/time', (req, res) => {
|
||||
const { minutes, comment, author } = req.body;
|
||||
if (typeof minutes !== 'number' || minutes <= 0)
|
||||
return res.status(400).json({ error: 'Minutes must be a positive number.' });
|
||||
if (!author)
|
||||
return res.status(400).json({ error: 'Author is required.' });
|
||||
|
||||
db.get('SELECT * FROM tickets WHERE id = ?', [req.params.id], (err, ticket) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
if (!ticket) return res.status(404).json({ error: 'Ticket not found' });
|
||||
|
||||
db.run(
|
||||
'INSERT INTO time_logs (ticket_id, minutes, comment, author) VALUES (?, ?, ?, ?)',
|
||||
[req.params.id, minutes, comment || '', author],
|
||||
function (err) {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
|
||||
db.get(
|
||||
'SELECT SUM(minutes) as total FROM time_logs WHERE ticket_id = ?',
|
||||
[req.params.id],
|
||||
(err, row) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
|
||||
const totalTime = row.total || 0;
|
||||
db.run(
|
||||
'UPDATE tickets SET total_time = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||
[totalTime, req.params.id],
|
||||
err => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
logHistory(
|
||||
req.params.id,
|
||||
'Time Logged',
|
||||
`Added ${minutes} minutes by ${author}. Comment: ${comment || 'none'}`,
|
||||
author
|
||||
);
|
||||
// Send back the updated total_time to the frontend
|
||||
res.status(201).json({ message: 'Time logged', total_time: totalTime });
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// GET time logs
|
||||
app.get('/api/tickets/:id/time', (req, res) => {
|
||||
db.all(
|
||||
'SELECT * FROM time_logs WHERE ticket_id = ? ORDER BY created_at DESC',
|
||||
[req.params.id],
|
||||
(err, rows) => {
|
||||
if (err) return res.status(500).json({ error: err.message });
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server läuft auf http://localhost:${port}`);
|
||||
console.log('Webhook URLs loaded:', {
|
||||
admin: ADMIN_WEBHOOK_URL ? 'Configured' : 'Not configured',
|
||||
user: USER_WEBHOOK_URL ? 'Configured' : 'Not configured'
|
||||
});
|
||||
});
|
||||
BIN
Binary file not shown.
Reference in new issue
Block a user