Lägg till autentisering och loggning, refaktorisera uppgifter till router
This commit is contained in:
@@ -9,6 +9,10 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /srv/swarm/org-todo-pwa/data:/data
|
- /srv/swarm/org-todo-pwa/data:/data
|
||||||
user: "1000:1000"
|
user: "1000:1000"
|
||||||
|
environment:
|
||||||
|
- DEBUG=app
|
||||||
|
- AUTH_USERNAME=fredrik
|
||||||
|
- AUTH_PASSWORD=apa
|
||||||
deploy:
|
deploy:
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
|
|||||||
98
logger.js
Normal file
98
logger.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
const { createLogger, format, transports } = require('winston');
|
||||||
|
|
||||||
|
const logger = createLogger({
|
||||||
|
level: 'info',
|
||||||
|
format: format.combine(
|
||||||
|
format.timestamp(),
|
||||||
|
format.json()
|
||||||
|
),
|
||||||
|
transports: [
|
||||||
|
new transports.Console(),
|
||||||
|
new transports.File({ filename: 'app.log' })
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = logger;
|
||||||
|
|
||||||
|
// filepath: /home/fredrik/dev/org-todo-pwa/routes/tasks.js
|
||||||
|
const express = require('express');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const logger = require('../logger');
|
||||||
|
const auth = require('../middleware/auth');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const dataDir = '/data';
|
||||||
|
|
||||||
|
// Ensure the /data directory exists
|
||||||
|
if (!fs.existsSync(dataDir)) {
|
||||||
|
fs.mkdirSync(dataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the tags.json file exists
|
||||||
|
const tagsFilePath = path.join(dataDir, 'tags.json');
|
||||||
|
if (!fs.existsSync(tagsFilePath)) {
|
||||||
|
fs.writeFileSync(tagsFilePath, JSON.stringify([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect the /add-task endpoint with authentication
|
||||||
|
router.post('/add-task', auth, async (req, res) => {
|
||||||
|
const { subject, description, scheduled } = req.body;
|
||||||
|
const currentDateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||||
|
|
||||||
|
let orgFormattedData = `
|
||||||
|
* TODO ${subject}
|
||||||
|
SCHEDULED: <${scheduled}>
|
||||||
|
:LOGBOOK:
|
||||||
|
- State "TODO" from "TODO" [${currentDateTime}]
|
||||||
|
:END:
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
orgFormattedData = `
|
||||||
|
* TODO ${subject}
|
||||||
|
${description}
|
||||||
|
SCHEDULED: <${scheduled}>
|
||||||
|
:LOGBOOK:
|
||||||
|
- State "TODO" from "TODO" [${currentDateTime}]
|
||||||
|
:END:
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(dataDir, 'tasks.org');
|
||||||
|
try {
|
||||||
|
await fs.promises.appendFile(filePath, orgFormattedData);
|
||||||
|
res.send({ message: 'Task added successfully!' });
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error writing to file:', err);
|
||||||
|
res.status(500).send('Error writing to file.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Endpoint to save tags
|
||||||
|
router.post('/save-tags', auth, async (req, res) => {
|
||||||
|
const { tags } = req.body;
|
||||||
|
const filePath = path.join(dataDir, 'tags.json');
|
||||||
|
try {
|
||||||
|
await fs.promises.writeFile(filePath, JSON.stringify(tags));
|
||||||
|
res.send({ message: 'Tags saved successfully!' });
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error saving tags:', err);
|
||||||
|
res.status(500).send('Error saving tags.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Endpoint to retrieve tags
|
||||||
|
router.get('/get-tags', auth, async (req, res) => {
|
||||||
|
const filePath = path.join(dataDir, 'tags.json');
|
||||||
|
try {
|
||||||
|
const data = await fs.promises.readFile(filePath);
|
||||||
|
const tags = JSON.parse(data);
|
||||||
|
res.send(tags);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error retrieving tags:', err);
|
||||||
|
res.status(500).send('Error retrieving tags.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
16
middleware/auth.js
Normal file
16
middleware/auth.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const basicAuth = require('basic-auth');
|
||||||
|
|
||||||
|
const auth = (req, res, next) => {
|
||||||
|
const user = basicAuth(req);
|
||||||
|
const username = process.env.AUTH_USERNAME; // Use environment variables
|
||||||
|
const password = process.env.AUTH_PASSWORD; // Use environment variables
|
||||||
|
|
||||||
|
if (user && user.name === username && user.pass === password) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
res.set('WWW-Authenticate', 'Basic realm="401"');
|
||||||
|
return res.status(401).send('Authentication required.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = auth;
|
||||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -12,6 +12,7 @@
|
|||||||
"basic-auth": "^2.0.1",
|
"basic-auth": "^2.0.1",
|
||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"fs": "^0.0.1-security"
|
"fs": "^0.0.1-security"
|
||||||
}
|
}
|
||||||
@@ -185,6 +186,17 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
|
||||||
|
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"basic-auth": "^2.0.1",
|
"basic-auth": "^2.0.1",
|
||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"fs": "^0.0.1-security"
|
"fs": "^0.0.1-security"
|
||||||
}
|
}
|
||||||
|
|||||||
79
routes/tasks.js
Normal file
79
routes/tasks.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const debug = require('debug')('app');
|
||||||
|
const auth = require('../middleware/auth');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const dataDir = '/data';
|
||||||
|
|
||||||
|
// Ensure the /data directory exists
|
||||||
|
if (!fs.existsSync(dataDir)) {
|
||||||
|
fs.mkdirSync(dataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the tags.json file exists
|
||||||
|
const tagsFilePath = path.join(dataDir, 'tags.json');
|
||||||
|
if (!fs.existsSync(tagsFilePath)) {
|
||||||
|
fs.writeFileSync(tagsFilePath, JSON.stringify([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect the /add-task endpoint with authentication
|
||||||
|
router.post('/add-task', auth, async (req, res) => {
|
||||||
|
const { subject, description, scheduled } = req.body;
|
||||||
|
const currentDateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||||
|
|
||||||
|
let orgFormattedData = `
|
||||||
|
* TODO ${subject}
|
||||||
|
SCHEDULED: <${scheduled}>
|
||||||
|
:LOGBOOK:
|
||||||
|
- State "TODO" from "TODO" [${currentDateTime}]
|
||||||
|
:END:`;
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
orgFormattedData = `
|
||||||
|
* TODO ${subject}
|
||||||
|
${description}
|
||||||
|
SCHEDULED: <${scheduled}>
|
||||||
|
:LOGBOOK:
|
||||||
|
- State "TODO" from "TODO" [${currentDateTime}]
|
||||||
|
:END:`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(dataDir, 'tasks.org');
|
||||||
|
try {
|
||||||
|
await fs.promises.appendFile(filePath, orgFormattedData);
|
||||||
|
res.send({ message: 'Task added successfully!' });
|
||||||
|
} catch (err) {
|
||||||
|
debug('Error writing to file:', err);
|
||||||
|
res.status(500).send('Error writing to file.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Endpoint to save tags
|
||||||
|
router.post('/save-tags', auth, async (req, res) => {
|
||||||
|
const { tags } = req.body;
|
||||||
|
const filePath = path.join(dataDir, 'tags.json');
|
||||||
|
try {
|
||||||
|
await fs.promises.writeFile(filePath, JSON.stringify(tags));
|
||||||
|
res.send({ message: 'Tags saved successfully!' });
|
||||||
|
} catch (err) {
|
||||||
|
debug('Error saving tags:', err);
|
||||||
|
res.status(500).send('Error saving tags.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Endpoint to retrieve tags
|
||||||
|
router.get('/get-tags', auth, async (req, res) => {
|
||||||
|
const filePath = path.join(dataDir, 'tags.json');
|
||||||
|
try {
|
||||||
|
const data = await fs.promises.readFile(filePath);
|
||||||
|
const tags = JSON.parse(data);
|
||||||
|
res.send(tags);
|
||||||
|
} catch (err) {
|
||||||
|
debug('Error retrieving tags:', err);
|
||||||
|
res.status(500).send('Error retrieving tags.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
97
server.js
97
server.js
@@ -1,106 +1,15 @@
|
|||||||
|
require('dotenv').config();
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const basicAuth = require('basic-auth');
|
|
||||||
const debug = require('debug')('app');
|
const debug = require('debug')('app');
|
||||||
|
const tasksRouter = require('./routes/tasks');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3044;
|
const port = 3044;
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use(express.static('public'));
|
app.use(express.static('public'));
|
||||||
|
app.use('/', tasksRouter);
|
||||||
// Ensure the /data directory exists
|
|
||||||
const dataDir = '/data';
|
|
||||||
if (!fs.existsSync(dataDir)) {
|
|
||||||
fs.mkdirSync(dataDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the tags.json file exists
|
|
||||||
const tagsFilePath = path.join(dataDir, 'tags.json');
|
|
||||||
if (!fs.existsSync(tagsFilePath)) {
|
|
||||||
fs.writeFileSync(tagsFilePath, JSON.stringify([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authentication middleware
|
|
||||||
const auth = (req, res, next) => {
|
|
||||||
const user = basicAuth(req);
|
|
||||||
const username = 'fredrik'; // Replace with your desired username
|
|
||||||
const password = 'apa'; // Replace with your desired password
|
|
||||||
|
|
||||||
if (user && user.name === username && user.pass === password) {
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
res.set('WWW-Authenticate', 'Basic realm="401"');
|
|
||||||
return res.status(401).send('Authentication required.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Protect the /add-task endpoint with authentication
|
|
||||||
app.post('/add-task', auth, (req, res) => {
|
|
||||||
const { subject, description, scheduled } = req.body;
|
|
||||||
const currentDateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
|
||||||
|
|
||||||
let orgFormattedData = `
|
|
||||||
* TODO ${subject}
|
|
||||||
SCHEDULED: <${scheduled}>
|
|
||||||
:LOGBOOK:
|
|
||||||
- State "TODO" from "TODO" [${currentDateTime}]
|
|
||||||
:END:
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (description) {
|
|
||||||
orgFormattedData = `
|
|
||||||
* TODO ${subject}
|
|
||||||
${description}
|
|
||||||
SCHEDULED: <${scheduled}>
|
|
||||||
:LOGBOOK:
|
|
||||||
- State "TODO" from "TODO" [${currentDateTime}]
|
|
||||||
:END:
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = path.join(dataDir, 'tasks.org');
|
|
||||||
fs.appendFile(filePath, orgFormattedData, (err) => {
|
|
||||||
if (err) {
|
|
||||||
debug('Error writing to file:', err);
|
|
||||||
return res.status(500).send('Error writing to file.');
|
|
||||||
}
|
|
||||||
res.send({ message: 'Task added successfully!' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Endpoint to save tags
|
|
||||||
app.post('/save-tags', auth, (req, res) => {
|
|
||||||
const { tags } = req.body;
|
|
||||||
const filePath = path.join(dataDir, 'tags.json');
|
|
||||||
fs.writeFile(filePath, JSON.stringify(tags), (err) => {
|
|
||||||
if (err) {
|
|
||||||
debug('Error saving tags:', err);
|
|
||||||
return res.status(500).send('Error saving tags.');
|
|
||||||
}
|
|
||||||
res.send({ message: 'Tags saved successfully!' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Endpoint to retrieve tags
|
|
||||||
app.get('/get-tags', auth, (req, res) => {
|
|
||||||
const filePath = path.join(dataDir, 'tags.json');
|
|
||||||
fs.readFile(filePath, (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
debug('Error retrieving tags:', err);
|
|
||||||
return res.status(500).send('Error retrieving tags.');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const tags = JSON.parse(data);
|
|
||||||
res.send(tags);
|
|
||||||
} catch (e) {
|
|
||||||
debug('Error parsing tags JSON:', e);
|
|
||||||
res.send([]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
debug(`Server running at http://localhost:${port}`);
|
debug(`Server running at http://localhost:${port}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user