-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
130 lines (111 loc) · 3.45 KB
/
Copy pathserver.js
File metadata and controls
130 lines (111 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
const express = require('express');
const cors = require('cors');
const session = require('express-session');
const bcrypt = require('bcryptjs');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const fs = require('fs').promises;
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
const DATA_FILE = path.join(__dirname, 'data', 'services.json');
// Trust proxy when behind reverse proxy (nginx, Traefik, etc.)
app.set('trust proxy', true);
const AUTH_ENABLED = process.env.AUTH_ENABLED === 'true';
const AUTH_USERNAME = process.env.AUTH_USERNAME || 'admin';
const AUTH_PASSWORD_HASH = process.env.AUTH_PASSWORD_HASH || bcrypt.hashSync('admin', 10);
const SESSION_SECRET = process.env.SESSION_SECRET || 'dasher-secret-change-this';
app.use(cors());
app.use(express.json());
app.use(cookieParser());
app.use(session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
secure: false,
sameSite: 'lax',
maxAge: 24 * 60 * 60 * 1000,
httpOnly: true
}
}));
// CSRF protection
const csrfProtection = csrf({ cookie: true });
app.use(express.static('public'));
function requireAuth(req, res, next) {
if (!AUTH_ENABLED || req.session.authenticated) {
return next();
}
return res.status(401).json({ error: 'Unauthorized' });
}
// Ensure data directory exists
async function ensureDataDir() {
try {
await fs.mkdir(path.join(__dirname, 'data'), { recursive: true });
try {
await fs.access(DATA_FILE);
} catch {
// Create initial data file if it doesn't exist
await fs.writeFile(DATA_FILE, JSON.stringify({ services: [], categories: [] }, null, 2));
}
} catch (error) {
console.error('Error creating data directory:', error);
}
}
// Get CSRF token for login
app.get('/api/csrf-token', csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// Login
app.post('/api/login', csrfProtection, async (req, res) => {
const { username, password } = req.body || {};
if (!AUTH_ENABLED) {
return res.json({ success: true });
}
if (username === AUTH_USERNAME && await bcrypt.compare(password || '', AUTH_PASSWORD_HASH)) {
req.session.authenticated = true;
req.session.save((err) => {
if (err) return res.status(500).json({ error: 'Failed to save session' });
res.json({ success: true });
});
return;
}
return res.status(401).json({ error: 'Invalid credentials' });
});
// Logout
app.post('/api/logout', (req, res) => {
req.session.destroy(() => {
res.json({ success: true });
});
});
// Auth status
app.get('/api/auth-status', (req, res) => {
res.json({
authEnabled: AUTH_ENABLED,
authenticated: !AUTH_ENABLED || req.session.authenticated || false
});
});
// Get all services
app.get('/api/services', requireAuth, async (req, res) => {
try {
const data = await fs.readFile(DATA_FILE, 'utf8');
res.json(JSON.parse(data));
} catch (error) {
res.status(500).json({ error: 'Failed to read services' });
}
});
// Save services
app.post('/api/services', csrfProtection, requireAuth, async (req, res) => {
try {
await fs.writeFile(DATA_FILE, JSON.stringify(req.body, null, 2));
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: 'Failed to save services' });
}
});
// Start server
ensureDataDir().then(() => {
app.listen(PORT, '0.0.0.0', () => {
console.log(`Dasher running on http://0.0.0.0:${PORT}`);
});
});