kopia lustrzana https://github.com/manuelkasper/sotlas-api
Porównaj commity
5 Commity
5e764a7ade
...
deb80e6f6f
Autor | SHA1 | Data |
---|---|---|
Manuel Kasper | deb80e6f6f | |
Manuel Kasper | 2c8cd5d4d7 | |
Manuel Kasper | f56fe05622 | |
Manuel Kasper | cdba5c193a | |
Manuel Kasper | 157eb20bca |
Plik diff jest za duży
Load Diff
11
package.json
11
package.json
|
@ -9,8 +9,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@turf/simplify": "^5.1.5",
|
"axios": "^1.6.3",
|
||||||
"axios": "^0.21.1",
|
|
||||||
"carrier": "^0.3.0",
|
"carrier": "^0.3.0",
|
||||||
"csv-parse": "^5.3.0",
|
"csv-parse": "^5.3.0",
|
||||||
"diacritics": "^1.3.0",
|
"diacritics": "^1.3.0",
|
||||||
|
@ -19,13 +18,13 @@
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-bearer-token": "^2.4.0",
|
"express-bearer-token": "^2.4.0",
|
||||||
"express-cache-controller": "^1.1.0",
|
"express-cache-controller": "^1.1.0",
|
||||||
"express-jwt": "^5.3.1",
|
"express-jwt": "^8.4.1",
|
||||||
"express-validator": "^6.10.0",
|
"express-validator": "^6.10.0",
|
||||||
"express-ws": "^4.0.0",
|
"express-ws": "^4.0.0",
|
||||||
"geolite2-redist": "^3.0.2",
|
"geolite2-redist": "^3.0.2",
|
||||||
"hasha": "^5.1.0",
|
"hasha": "^5.1.0",
|
||||||
"htmlparser2": "^3.10.1",
|
"htmlparser2": "^3.10.1",
|
||||||
"jwks-rsa": "^1.6.0",
|
"jwks-rsa": "^3.1.0",
|
||||||
"maxmind": "^4.3.6",
|
"maxmind": "^4.3.6",
|
||||||
"minio": "^7.0.29",
|
"minio": "^7.0.29",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
|
@ -34,9 +33,7 @@
|
||||||
"node-cron": "^3.0.1",
|
"node-cron": "^3.0.1",
|
||||||
"promise-retry": "^2.0.1",
|
"promise-retry": "^2.0.1",
|
||||||
"reconnect-net": "^1.1.1",
|
"reconnect-net": "^1.1.1",
|
||||||
"sharp": "^0.30.7",
|
"sharp": "^0.32.6",
|
||||||
"togeojson": "^0.16.0",
|
|
||||||
"togpx": "^0.5.4",
|
|
||||||
"treemap-js": "^1.2.1"
|
"treemap-js": "^1.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ const express = require('express')
|
||||||
const multer = require('multer')
|
const multer = require('multer')
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
const photos = require('./photos')
|
const photos = require('./photos')
|
||||||
const jwt = require('express-jwt')
|
const { expressjwt: jwt } = require('express-jwt')
|
||||||
const jwksRsa = require('jwks-rsa')
|
const { expressJwtSecret } = require('jwks-rsa')
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
|
|
||||||
let upload = multer({dest: config.photos.uploadPath})
|
let upload = multer({dest: config.photos.uploadPath})
|
||||||
|
@ -12,12 +12,13 @@ let router = express.Router()
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
||||||
let jwtCallback = jwt({
|
let jwtCallback = jwt({
|
||||||
secret: jwksRsa.expressJwtSecret({
|
secret: expressJwtSecret({
|
||||||
cache: true,
|
cache: true,
|
||||||
rateLimit: true,
|
rateLimit: true,
|
||||||
jwksRequestsPerMinute: 5,
|
jwksRequestsPerMinute: 5,
|
||||||
jwksUri: config.sso.jwksUri
|
jwksUri: config.sso.jwksUri
|
||||||
})
|
}),
|
||||||
|
algorithms: ['RS256']
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/summits/:association/:code/upload', jwtCallback, upload.array('photo'), async (req, res) => {
|
router.post('/summits/:association/:code/upload', jwtCallback, upload.array('photo'), async (req, res) => {
|
||||||
|
@ -26,7 +27,7 @@ router.post('/summits/:association/:code/upload', jwtCallback, upload.array('pho
|
||||||
noCache: true
|
noCache: true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.user.callsign) {
|
if (!req.auth.callsign) {
|
||||||
res.status(401).send('Missing callsign in SSO token').end()
|
res.status(401).send('Missing callsign in SSO token').end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -41,7 +42,7 @@ router.post('/summits/:association/:code/upload', jwtCallback, upload.array('pho
|
||||||
if (req.files) {
|
if (req.files) {
|
||||||
let dbPhotos = []
|
let dbPhotos = []
|
||||||
for (let file of req.files) {
|
for (let file of req.files) {
|
||||||
let photo = await photos.importPhoto(file.path, req.user.callsign)
|
let photo = await photos.importPhoto(file.path, req.auth.callsign)
|
||||||
dbPhotos.push(photo)
|
dbPhotos.push(photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ router.delete('/summits/:association/:code/:filename', jwtCallback, async (req,
|
||||||
noCache: true
|
noCache: true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.user.callsign) {
|
if (!req.auth.callsign) {
|
||||||
res.status(401).send('Missing callsign in SSO token').end()
|
res.status(401).send('Missing callsign in SSO token').end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,7 +84,7 @@ router.delete('/summits/:association/:code/:filename', jwtCallback, async (req,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that uploader is currently logged in user
|
// Check that uploader is currently logged in user
|
||||||
if (photo.author !== req.user.callsign) {
|
if (photo.author !== req.auth.callsign) {
|
||||||
res.status(401).send('Cannot delete another user\'s photos').end()
|
res.status(401).send('Cannot delete another user\'s photos').end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -98,7 +99,7 @@ router.post('/summits/:association/:code/reorder', jwtCallback, async (req, res)
|
||||||
noCache: true
|
noCache: true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.user.callsign) {
|
if (!req.auth.callsign) {
|
||||||
res.status(401).send('Missing callsign in SSO token').end()
|
res.status(401).send('Missing callsign in SSO token').end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ router.post('/summits/:association/:code/reorder', jwtCallback, async (req, res)
|
||||||
// Assign new sortOrder index to photos of this user, in the order given by req.body.filenames
|
// Assign new sortOrder index to photos of this user, in the order given by req.body.filenames
|
||||||
let updates = req.body.filenames.map((filename, index) => {
|
let updates = req.body.filenames.map((filename, index) => {
|
||||||
return db.getDb().collection('summits').updateOne(
|
return db.getDb().collection('summits').updateOne(
|
||||||
{ code: summitCode, 'photos.author': req.user.callsign, 'photos.filename': filename },
|
{ code: summitCode, 'photos.author': req.auth.callsign, 'photos.filename': filename },
|
||||||
{ $set: { 'photos.$.sortOrder': index + 1 } }
|
{ $set: { 'photos.$.sortOrder': index + 1 } }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -123,7 +124,7 @@ router.post('/summits/:association/:code/:filename', jwtCallback, async (req, re
|
||||||
noCache: true
|
noCache: true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.user.callsign) {
|
if (!req.auth.callsign) {
|
||||||
res.status(401).send('Missing callsign in SSO token').end()
|
res.status(401).send('Missing callsign in SSO token').end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -137,7 +138,7 @@ router.post('/summits/:association/:code/:filename', jwtCallback, async (req, re
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that editor is the currently logged in user
|
// Check that editor is the currently logged in user
|
||||||
if (photo.author !== req.user.callsign) {
|
if (photo.author !== req.auth.callsign) {
|
||||||
res.status(401).send('Cannot delete another user\'s photos').end()
|
res.status(401).send('Cannot delete another user\'s photos').end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ const users = require('./users');
|
||||||
const activations = require('./activations');
|
const activations = require('./activations');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const photos_router = require('./photos_router');
|
const photos_router = require('./photos_router');
|
||||||
const tracks_router = require('./tracks_router');
|
|
||||||
const solardata = require('./solardata');
|
const solardata = require('./solardata');
|
||||||
const maxmind = require('maxmind');
|
const maxmind = require('maxmind');
|
||||||
const cronjobs = require('./cronjobs');
|
const cronjobs = require('./cronjobs');
|
||||||
|
@ -51,7 +50,6 @@ app.use('/geoexport', geoexport);
|
||||||
app.use('/activations', activations);
|
app.use('/activations', activations);
|
||||||
app.use('/users', users);
|
app.use('/users', users);
|
||||||
app.use('/photos', photos_router);
|
app.use('/photos', photos_router);
|
||||||
app.use('/tracks', tracks_router);
|
|
||||||
app.use('/solardata', solardata);
|
app.use('/solardata', solardata);
|
||||||
|
|
||||||
db.waitDb(() => {
|
db.waitDb(() => {
|
||||||
|
|
73
tracks.js
73
tracks.js
|
@ -1,73 +0,0 @@
|
||||||
const togeojson = require('togeojson')
|
|
||||||
const fsPromises = require('fs').promises
|
|
||||||
const DOMParser = require('xmldom').DOMParser
|
|
||||||
const simplify = require('@turf/simplify')
|
|
||||||
const togpx = require('togpx')
|
|
||||||
const hasha = require('hasha')
|
|
||||||
const path = require('path')
|
|
||||||
const config = require('./config')
|
|
||||||
const db = require('./db')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
importTrack: async function(filename, author) {
|
|
||||||
// Hash input file to determine filename
|
|
||||||
let hash = await hasha.fromFile(filename, {algorithm: 'sha256'})
|
|
||||||
let hashFilename = hash.substr(0, 32)
|
|
||||||
let originalPath = config.tracks.paths.original + '/' + hashFilename.substr(0, 2) + '/' + hashFilename
|
|
||||||
await fsPromises.mkdir(path.dirname(originalPath), {recursive: true})
|
|
||||||
|
|
||||||
// Parse first to check if it's valid GPX/KML
|
|
||||||
let gpxData = await fsPromises.readFile(filename, 'utf-8')
|
|
||||||
let dom = new DOMParser().parseFromString(gpxData, 'text/xml')
|
|
||||||
if (!dom) {
|
|
||||||
throw new Error('Bad XML document')
|
|
||||||
}
|
|
||||||
let geojson
|
|
||||||
if (dom.documentElement.tagName === 'kml') {
|
|
||||||
geojson = togeojson.kml(dom)
|
|
||||||
originalPath += '.kml'
|
|
||||||
} else {
|
|
||||||
geojson = togeojson.gpx(dom)
|
|
||||||
originalPath += '.gpx'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (geojson.type !== 'FeatureCollection') {
|
|
||||||
throw new Error('Expected feature collection')
|
|
||||||
}
|
|
||||||
if (geojson.features.length === 0) {
|
|
||||||
throw new Error('No features found')
|
|
||||||
}
|
|
||||||
|
|
||||||
await fsPromises.copyFile(filename, originalPath)
|
|
||||||
|
|
||||||
// Remove times, if present
|
|
||||||
geojson.features.forEach(feature => {
|
|
||||||
if (feature.type !== 'Feature') {
|
|
||||||
throw new Error('Expected feature')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feature.properties.coordTimes) {
|
|
||||||
delete feature.properties.coordTimes
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let simplified = simplify(geojson, {tolerance: config.tracks.tolerance, highQuality: true})
|
|
||||||
let simpleGpx = togpx(simplified)
|
|
||||||
|
|
||||||
let outPath = config.tracks.paths.simple + '/' + hashFilename.substr(0, 2) + '/' + hashFilename + '.gpx'
|
|
||||||
await fsPromises.mkdir(path.dirname(outPath), {recursive: true})
|
|
||||||
await fsPromises.writeFile(outPath, simpleGpx)
|
|
||||||
|
|
||||||
db.getDb().collection('uploads').insertOne({
|
|
||||||
uploadDate: new Date(),
|
|
||||||
type: 'track',
|
|
||||||
filename: hashFilename + '.gpx',
|
|
||||||
author
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
filename: hashFilename + '.gpx',
|
|
||||||
author
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
const express = require('express')
|
|
||||||
const multer = require('multer')
|
|
||||||
const config = require('./config')
|
|
||||||
const tracks = require('./tracks')
|
|
||||||
const jwt = require('express-jwt')
|
|
||||||
const jwksRsa = require('jwks-rsa')
|
|
||||||
|
|
||||||
let upload = multer({dest: config.tracks.uploadPath})
|
|
||||||
|
|
||||||
let router = express.Router()
|
|
||||||
module.exports = router
|
|
||||||
|
|
||||||
let jwtCallback = jwt({
|
|
||||||
secret: jwksRsa.expressJwtSecret({
|
|
||||||
cache: true,
|
|
||||||
rateLimit: true,
|
|
||||||
jwksRequestsPerMinute: 5,
|
|
||||||
jwksUri: config.sso.jwksUri
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.post('/upload', jwtCallback, upload.single('track'), (req, res) => {
|
|
||||||
res.cacheControl = {
|
|
||||||
noCache: true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!req.user.callsign) {
|
|
||||||
res.status(401).send('Missing callsign in SSO token').end()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.file) {
|
|
||||||
tracks.importTrack(req.file.path, req.user.callsign)
|
|
||||||
.then(track => {
|
|
||||||
res.json(track)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err)
|
|
||||||
res.status(500).end()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
21
users.js
21
users.js
|
@ -1,8 +1,8 @@
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const {body, validationResult} = require('express-validator');
|
const {body, validationResult} = require('express-validator');
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
const jwt = require("express-jwt");
|
const { expressjwt: jwt } = require('express-jwt')
|
||||||
const jwksRsa = require("jwks-rsa");
|
const { expressJwtSecret } = require('jwks-rsa')
|
||||||
const db = require("./db");
|
const db = require("./db");
|
||||||
const summitUtils = require('./summits');
|
const summitUtils = require('./summits');
|
||||||
|
|
||||||
|
@ -10,18 +10,19 @@ let router = express.Router();
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
let jwtCallback = jwt({
|
let jwtCallback = jwt({
|
||||||
secret: jwksRsa.expressJwtSecret({
|
secret: expressJwtSecret({
|
||||||
cache: true,
|
cache: true,
|
||||||
rateLimit: true,
|
rateLimit: true,
|
||||||
jwksRequestsPerMinute: 5,
|
jwksRequestsPerMinute: 5,
|
||||||
jwksUri: config.sso.jwksUri
|
jwksUri: config.sso.jwksUri
|
||||||
})
|
}),
|
||||||
|
algorithms: ['RS256']
|
||||||
});
|
});
|
||||||
|
|
||||||
const DB_COLLECTION_USERS = "users";
|
const DB_COLLECTION_USERS = "users";
|
||||||
|
|
||||||
router.get("/me", jwtCallback, (req, res) => {
|
router.get("/me", jwtCallback, (req, res) => {
|
||||||
const reqUserId = req.user.userid;
|
const reqUserId = req.auth.userid;
|
||||||
if (!reqUserId) {
|
if (!reqUserId) {
|
||||||
return res.status(401).send("Missing userid in SSO token").end();
|
return res.status(401).send("Missing userid in SSO token").end();
|
||||||
}
|
}
|
||||||
|
@ -52,7 +53,7 @@ router.post("/me/settings",
|
||||||
jwtCallback,
|
jwtCallback,
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
|
|
||||||
const reqUserId = req.user.userid;
|
const reqUserId = req.auth.userid;
|
||||||
if (!reqUserId) {
|
if (!reqUserId) {
|
||||||
return res.status(401).send("Missing userid in SSO token").end();
|
return res.status(401).send("Missing userid in SSO token").end();
|
||||||
}
|
}
|
||||||
|
@ -69,7 +70,7 @@ router.post("/me/settings",
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/me/tags", jwtCallback, (req, res) => {
|
router.get("/me/tags", jwtCallback, (req, res) => {
|
||||||
const reqUserId = req.user.userid;
|
const reqUserId = req.auth.userid;
|
||||||
if (!reqUserId) {
|
if (!reqUserId) {
|
||||||
return res.status(401).send("Missing userid in SSO token").end();
|
return res.status(401).send("Missing userid in SSO token").end();
|
||||||
}
|
}
|
||||||
|
@ -94,7 +95,7 @@ router.get("/me/tags", jwtCallback, (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/me/summits/tags", jwtCallback, (req, res) => {
|
router.get("/me/summits/tags", jwtCallback, (req, res) => {
|
||||||
const reqUserId = req.user.userid;
|
const reqUserId = req.auth.userid;
|
||||||
if (!reqUserId) {
|
if (!reqUserId) {
|
||||||
return res.status(401).send("Missing userid in SSO token").end();
|
return res.status(401).send("Missing userid in SSO token").end();
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ router.get("/me/summits/tags", jwtCallback, (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/me/summit/:association/:code", jwtCallback, (req, res) => {
|
router.get("/me/summit/:association/:code", jwtCallback, (req, res) => {
|
||||||
const reqUserId = req.user.userid;
|
const reqUserId = req.auth.userid;
|
||||||
if (!reqUserId) {
|
if (!reqUserId) {
|
||||||
return res.status(401).send("Missing userid in SSO token").end();
|
return res.status(401).send("Missing userid in SSO token").end();
|
||||||
}
|
}
|
||||||
|
@ -174,7 +175,7 @@ router.post("/me/summit/:association/:code",
|
||||||
body("tags.*").isString(),
|
body("tags.*").isString(),
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
|
|
||||||
const reqUserId = req.user.userid;
|
const reqUserId = req.auth.userid;
|
||||||
if (!reqUserId) {
|
if (!reqUserId) {
|
||||||
return res.status(401).send("Missing userid in SSO token").end();
|
return res.status(401).send("Missing userid in SSO token").end();
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue