Refactor backend to import/export syntax and update deps
This commit is contained in:
parent
1463773480
commit
7d0f3898de
7
.github/workflows/deploy_backend.yml
vendored
7
.github/workflows/deploy_backend.yml
vendored
|
|
@ -19,6 +19,13 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 17
|
||||||
|
cache: yarn
|
||||||
|
cache-dependency-path: '**/yarn.lock'
|
||||||
|
- run: yarn install --immutable
|
||||||
|
- run: yarn build
|
||||||
- id: auth
|
- id: auth
|
||||||
uses: google-github-actions/auth@v0
|
uses: google-github-actions/auth@v0
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
50
crabfit-backend/.eslintrc.js
Normal file
50
crabfit-backend/.eslintrc.js
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
module.exports = {
|
||||||
|
'env': {
|
||||||
|
'es2021': true,
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'extends': 'eslint:recommended',
|
||||||
|
'overrides': [
|
||||||
|
],
|
||||||
|
'parserOptions': {
|
||||||
|
'ecmaVersion': 'latest',
|
||||||
|
'sourceType': 'module'
|
||||||
|
},
|
||||||
|
'rules': {
|
||||||
|
'indent': [
|
||||||
|
'error',
|
||||||
|
2
|
||||||
|
],
|
||||||
|
'linebreak-style': [
|
||||||
|
'error',
|
||||||
|
'unix'
|
||||||
|
],
|
||||||
|
'quotes': [
|
||||||
|
'error',
|
||||||
|
'single'
|
||||||
|
],
|
||||||
|
'semi': [
|
||||||
|
'error',
|
||||||
|
'never'
|
||||||
|
],
|
||||||
|
'eqeqeq': 2,
|
||||||
|
'no-return-await': 1,
|
||||||
|
'no-var': 2,
|
||||||
|
'prefer-const': 1,
|
||||||
|
'yoda': 2,
|
||||||
|
'no-trailing-spaces': 1,
|
||||||
|
'eol-last': [1, 'always'],
|
||||||
|
'no-unused-vars': [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
'args': 'all',
|
||||||
|
'argsIgnorePattern': '^_',
|
||||||
|
'ignoreRestSiblings': true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'arrow-parens': [
|
||||||
|
'error',
|
||||||
|
'as-needed'
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,3 +6,7 @@
|
||||||
.env
|
.env
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.parcel-cache
|
||||||
|
res
|
||||||
|
routes
|
||||||
|
swagger.yaml
|
||||||
|
|
|
||||||
10
crabfit-backend/.gitignore
vendored
10
crabfit-backend/.gitignore
vendored
|
|
@ -1,11 +1,7 @@
|
||||||
/node_modules
|
node_modules
|
||||||
|
dist
|
||||||
.DS_Store
|
.parcel-cache
|
||||||
.env
|
.env
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@ cron:
|
||||||
url: /tasks/cleanup
|
url: /tasks/cleanup
|
||||||
schedule: every monday 09:00
|
schedule: every monday 09:00
|
||||||
target: api
|
target: api
|
||||||
- description: "clean up old events without a visited date"
|
|
||||||
url: /tasks/legacyCleanup
|
|
||||||
schedule: every tuesday 09:00
|
|
||||||
target: api
|
|
||||||
- description: "remove people with an event id that no longer exists"
|
- description: "remove people with an event id that no longer exists"
|
||||||
url: /tasks/removeOrphans
|
url: /tasks/removeOrphans
|
||||||
schedule: 1st wednesday of month 09:00
|
schedule: 1st wednesday of month 09:00
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,61 @@
|
||||||
require('dotenv').config();
|
import { config } from 'dotenv'
|
||||||
|
import { Datastore } from '@google-cloud/datastore'
|
||||||
|
import express from 'express'
|
||||||
|
import cors from 'cors'
|
||||||
|
|
||||||
const { Datastore } = require('@google-cloud/datastore');
|
import packageJson from './package.json'
|
||||||
const express = require('express');
|
|
||||||
const cors = require('cors');
|
|
||||||
|
|
||||||
const package = require('./package.json');
|
import {
|
||||||
|
stats,
|
||||||
|
getEvent,
|
||||||
|
createEvent,
|
||||||
|
getPeople,
|
||||||
|
createPerson,
|
||||||
|
login,
|
||||||
|
updatePerson,
|
||||||
|
taskCleanup,
|
||||||
|
taskRemoveOrphans,
|
||||||
|
} from './routes'
|
||||||
|
|
||||||
const stats = require('./routes/stats');
|
config()
|
||||||
const getEvent = require('./routes/getEvent');
|
|
||||||
const createEvent = require('./routes/createEvent');
|
|
||||||
const getPeople = require('./routes/getPeople');
|
|
||||||
const createPerson = require('./routes/createPerson');
|
|
||||||
const login = require('./routes/login');
|
|
||||||
const updatePerson = require('./routes/updatePerson');
|
|
||||||
|
|
||||||
const taskCleanup = require('./routes/taskCleanup');
|
const app = express()
|
||||||
const taskLegacyCleanup = require('./routes/taskLegacyCleanup');
|
const port = 8080
|
||||||
const taskRemoveOrphans = require('./routes/taskRemoveOrphans');
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
const port = 8080;
|
|
||||||
const corsOptions = {
|
const corsOptions = {
|
||||||
origin: process.env.NODE_ENV === 'production' ? 'https://crab.fit' : 'http://localhost:5173',
|
origin: process.env.NODE_ENV === 'production' ? 'https://crab.fit' : 'http://localhost:5173',
|
||||||
};
|
}
|
||||||
|
|
||||||
const datastore = new Datastore({
|
const datastore = new Datastore({
|
||||||
keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
|
keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
|
||||||
});
|
})
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json())
|
||||||
app.use((req, res, next) => {
|
app.use((req, _res, next) => {
|
||||||
req.datastore = datastore;
|
req.datastore = datastore
|
||||||
req.types = {
|
req.types = {
|
||||||
event: process.env.NODE_ENV === 'production' ? 'Event' : 'DevEvent',
|
event: process.env.NODE_ENV === 'production' ? 'Event' : 'DevEvent',
|
||||||
person: process.env.NODE_ENV === 'production' ? 'Person' : 'DevPerson',
|
person: process.env.NODE_ENV === 'production' ? 'Person' : 'DevPerson',
|
||||||
stats: process.env.NODE_ENV === 'production' ? 'Stats' : 'DevStats',
|
stats: process.env.NODE_ENV === 'production' ? 'Stats' : 'DevStats',
|
||||||
};
|
}
|
||||||
next();
|
next()
|
||||||
});
|
})
|
||||||
app.options('*', cors(corsOptions));
|
app.options('*', cors(corsOptions))
|
||||||
app.use(cors(corsOptions));
|
app.use(cors(corsOptions))
|
||||||
|
|
||||||
app.get('/', (req, res) => res.send(`Crabfit API v${package.version}`));
|
app.get('/', (_req, res) => res.send(`Crabfit API v${packageJson.version}`))
|
||||||
|
|
||||||
app.get('/stats', stats);
|
app.get('/stats', stats)
|
||||||
app.get('/event/:eventId', getEvent);
|
app.get('/event/:eventId', getEvent)
|
||||||
app.post('/event', createEvent);
|
app.post('/event', createEvent)
|
||||||
app.get('/event/:eventId/people', getPeople);
|
app.get('/event/:eventId/people', getPeople)
|
||||||
app.post('/event/:eventId/people', createPerson);
|
app.post('/event/:eventId/people', createPerson)
|
||||||
app.post('/event/:eventId/people/:personName', login);
|
app.post('/event/:eventId/people/:personName', login)
|
||||||
app.patch('/event/:eventId/people/:personName', updatePerson);
|
app.patch('/event/:eventId/people/:personName', updatePerson)
|
||||||
|
|
||||||
// Tasks
|
// Tasks
|
||||||
app.get('/tasks/cleanup', taskCleanup);
|
app.get('/tasks/cleanup', taskCleanup)
|
||||||
app.get('/tasks/legacyCleanup', taskLegacyCleanup);
|
app.get('/tasks/removeOrphans', taskRemoveOrphans)
|
||||||
app.get('/tasks/removeOrphans', taskRemoveOrphans);
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Crabfit API listening at http://localhost:${port} in ${process.env.NODE_ENV === 'production' ? 'prod' : 'dev'} mode`)
|
console.log(`Crabfit API listening at http://localhost:${port} in ${process.env.NODE_ENV === 'production' ? 'prod' : 'dev'} mode`)
|
||||||
});
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,33 @@
|
||||||
{
|
{
|
||||||
"name": "crabfit-backend",
|
"name": "crabfit-backend",
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"description": "API for Crabfit",
|
"description": "API for Crabfit",
|
||||||
"main": "index.js",
|
|
||||||
"author": "Ben Grant",
|
"author": "Ben Grant",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"source": "index.js",
|
||||||
|
"main": "dist/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=12.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js"
|
"build:dev": "NODE_ENV=development parcel build --no-cache",
|
||||||
|
"dev": "rm -rf .parcel-cache dist && NODE_ENV=development nodemon --exec \"yarn build:dev && yarn start\" --watch routes --watch res --watch index.js",
|
||||||
|
"build": "parcel build",
|
||||||
|
"start": "node ./dist/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google-cloud/datastore": "^6.3.1",
|
"@google-cloud/datastore": "^7.0.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.11.5",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^16.0.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.18.1",
|
||||||
"punycode": "^2.1.1"
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^8.22.0",
|
||||||
|
"nodemon": "^2.0.19",
|
||||||
|
"parcel": "^2.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,84 @@
|
||||||
const dayjs = require('dayjs');
|
import dayjs from 'dayjs'
|
||||||
const punycode = require('punycode/');
|
import punycode from 'punycode/'
|
||||||
|
|
||||||
const adjectives = require('../res/adjectives.json');
|
import adjectives from '../res/adjectives.json'
|
||||||
const crabs = require('../res/crabs.json');
|
import crabs from '../res/crabs.json'
|
||||||
|
|
||||||
const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1);
|
const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1)
|
||||||
|
|
||||||
// Generate a random name based on an adjective and a crab species
|
// Generate a random name based on an adjective and a crab species
|
||||||
const generateName = () => {
|
const generateName = () =>
|
||||||
return `${capitalize(adjectives[Math.floor(Math.random() * adjectives.length)])} ${crabs[Math.floor(Math.random() * crabs.length)]} Crab`;
|
`${capitalize(adjectives[Math.floor(Math.random() * adjectives.length)])} ${crabs[Math.floor(Math.random() * crabs.length)]} Crab`
|
||||||
};
|
|
||||||
|
|
||||||
// Generate a slug for the crab fit
|
// Generate a slug for the crab fit
|
||||||
const generateId = name => {
|
const generateId = name => {
|
||||||
let id = punycode.encode(name.trim().toLowerCase()).trim().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-');
|
let id = punycode.encode(name.trim().toLowerCase()).trim().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-')
|
||||||
if (id.replace(/-/g, '') === '') {
|
if (id.replace(/-/g, '') === '') {
|
||||||
id = generateName().trim().toLowerCase().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-');
|
id = generateName().trim().toLowerCase().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-')
|
||||||
}
|
}
|
||||||
const number = Math.floor(100000 + Math.random() * 900000);
|
const number = Math.floor(100000 + Math.random() * 900000)
|
||||||
return `${id}-${number}`;
|
return `${id}-${number}`
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const createEvent = async (req, res) => {
|
||||||
const { event } = req.body;
|
const { event } = req.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const name = event.name.trim() === '' ? generateName() : event.name.trim();
|
const name = event.name.trim() === '' ? generateName() : event.name.trim()
|
||||||
let eventId = generateId(name);
|
let eventId = generateId(name)
|
||||||
const currentTime = dayjs().unix();
|
const currentTime = dayjs().unix()
|
||||||
|
|
||||||
// Check if the event ID already exists, and if so generate a new one
|
// Check if the event ID already exists, and if so generate a new one
|
||||||
let eventResult;
|
let eventResult
|
||||||
do {
|
do {
|
||||||
const query = req.datastore.createQuery(req.types.event)
|
const query = req.datastore.createQuery(req.types.event)
|
||||||
.select('__key__')
|
.select('__key__')
|
||||||
.filter('__key__', req.datastore.key([req.types.event, eventId]));
|
.filter('__key__', req.datastore.key([req.types.event, eventId]))
|
||||||
|
|
||||||
eventResult = (await req.datastore.runQuery(query))[0][0];
|
eventResult = (await req.datastore.runQuery(query))[0][0]
|
||||||
|
|
||||||
if (eventResult !== undefined) {
|
if (eventResult !== undefined) {
|
||||||
eventId = generateId(name);
|
eventId = generateId(name)
|
||||||
}
|
}
|
||||||
} while (eventResult !== undefined);
|
} while (eventResult !== undefined)
|
||||||
|
|
||||||
const entity = {
|
const entity = {
|
||||||
key: req.datastore.key([req.types.event, eventId]),
|
key: req.datastore.key([req.types.event, eventId]),
|
||||||
data: {
|
data: {
|
||||||
name: name,
|
name: name,
|
||||||
created: currentTime,
|
created: currentTime,
|
||||||
times: event.times,
|
times: event.times,
|
||||||
timezone: event.timezone,
|
timezone: event.timezone,
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
await req.datastore.insert(entity);
|
await req.datastore.insert(entity)
|
||||||
|
|
||||||
res.status(201).send({
|
res.status(201).send({
|
||||||
id: eventId,
|
id: eventId,
|
||||||
name: name,
|
name: name,
|
||||||
created: currentTime,
|
created: currentTime,
|
||||||
times: event.times,
|
times: event.times,
|
||||||
timezone: event.timezone,
|
timezone: event.timezone,
|
||||||
});
|
})
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
let eventCountResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'eventCount'])))[0] || null;
|
const eventCountResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'eventCount'])))[0] || null
|
||||||
if (eventCountResult) {
|
if (eventCountResult) {
|
||||||
eventCountResult.value++;
|
await req.datastore.upsert({
|
||||||
await req.datastore.upsert(eventCountResult);
|
...eventCountResult,
|
||||||
|
value: eventCountResult.value + 1,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
await req.datastore.insert({
|
await req.datastore.insert({
|
||||||
key: req.datastore.key([req.types.stats, 'eventCount']),
|
key: req.datastore.key([req.types.stats, 'eventCount']),
|
||||||
data: { value: 1 },
|
data: { value: 1 },
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
res.sendStatus(400);
|
res.sendStatus(400)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default createEvent
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,65 @@
|
||||||
const dayjs = require('dayjs');
|
import dayjs from 'dayjs'
|
||||||
const bcrypt = require('bcrypt');
|
import bcrypt from 'bcrypt'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const createPerson = async (req, res) => {
|
||||||
const { eventId } = req.params;
|
const { eventId } = req.params
|
||||||
const { person } = req.body;
|
const { person } = req.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const event = (await req.datastore.get(req.datastore.key([req.types.event, eventId])))[0];
|
const event = (await req.datastore.get(req.datastore.key([req.types.event, eventId])))[0]
|
||||||
const query = req.datastore.createQuery(req.types.person)
|
const query = req.datastore.createQuery(req.types.person)
|
||||||
.filter('eventId', eventId)
|
.filter('eventId', eventId)
|
||||||
.filter('name', person.name);
|
.filter('name', person.name)
|
||||||
let personResult = (await req.datastore.runQuery(query))[0][0];
|
const personResult = (await req.datastore.runQuery(query))[0][0]
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
if (person && personResult === undefined) {
|
if (person && personResult === undefined) {
|
||||||
const currentTime = dayjs().unix();
|
const currentTime = dayjs().unix()
|
||||||
|
|
||||||
// If password
|
// If password
|
||||||
let hash = null;
|
let hash = null
|
||||||
if (person.password) {
|
if (person.password) {
|
||||||
hash = await bcrypt.hash(person.password, 10);
|
hash = await bcrypt.hash(person.password, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
const entity = {
|
const entity = {
|
||||||
key: req.datastore.key(req.types.person),
|
key: req.datastore.key(req.types.person),
|
||||||
data: {
|
data: {
|
||||||
name: person.name.trim(),
|
name: person.name.trim(),
|
||||||
password: hash,
|
password: hash,
|
||||||
eventId: eventId,
|
eventId: eventId,
|
||||||
created: currentTime,
|
created: currentTime,
|
||||||
availability: [],
|
availability: [],
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
await req.datastore.insert(entity);
|
await req.datastore.insert(entity)
|
||||||
|
|
||||||
res.sendStatus(201);
|
res.sendStatus(201)
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
let personCountResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'personCount'])))[0] || null;
|
const personCountResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'personCount'])))[0] || null
|
||||||
if (personCountResult) {
|
if (personCountResult) {
|
||||||
personCountResult.value++;
|
await req.datastore.upsert({
|
||||||
await req.datastore.upsert(personCountResult);
|
...personCountResult,
|
||||||
|
value: personCountResult.value + 1,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
await req.datastore.insert({
|
await req.datastore.insert({
|
||||||
key: req.datastore.key([req.types.stats, 'personCount']),
|
key: req.datastore.key([req.types.stats, 'personCount']),
|
||||||
data: { value: 1 },
|
data: { value: 1 },
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(400);
|
res.sendStatus(400)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
res.sendStatus(400);
|
res.sendStatus(400)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default createPerson
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,29 @@
|
||||||
const dayjs = require('dayjs');
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const getEvent = async (req, res) => {
|
||||||
const { eventId } = req.params;
|
const { eventId } = req.params
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let event = (await req.datastore.get(req.datastore.key([req.types.event, eventId])))[0];
|
const event = (await req.datastore.get(req.datastore.key([req.types.event, eventId])))[0]
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
res.send({
|
res.send({
|
||||||
id: eventId,
|
id: eventId,
|
||||||
...event,
|
...event,
|
||||||
});
|
})
|
||||||
|
|
||||||
// Update last visited time
|
// Update last visited time
|
||||||
event.visited = dayjs().unix();
|
await req.datastore.upsert({
|
||||||
await req.datastore.upsert(event);
|
...event,
|
||||||
} else {
|
visited: dayjs().unix()
|
||||||
res.sendStatus(404);
|
})
|
||||||
}
|
} else {
|
||||||
} catch (e) {
|
res.sendStatus(404)
|
||||||
console.error(e);
|
}
|
||||||
res.sendStatus(404);
|
} catch (e) {
|
||||||
}
|
console.error(e)
|
||||||
};
|
res.sendStatus(404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getEvent
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
module.exports = async (req, res) => {
|
const getPeople = async (req, res) => {
|
||||||
const { eventId } = req.params;
|
const { eventId } = req.params
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query = req.datastore.createQuery(req.types.person).filter('eventId', eventId);
|
const query = req.datastore.createQuery(req.types.person).filter('eventId', eventId)
|
||||||
let people = (await req.datastore.runQuery(query))[0];
|
let people = (await req.datastore.runQuery(query))[0]
|
||||||
people = people.map(person => ({
|
people = people.map(person => ({
|
||||||
name: person.name,
|
name: person.name,
|
||||||
availability: person.availability,
|
availability: person.availability,
|
||||||
created: person.created,
|
created: person.created,
|
||||||
}));
|
}))
|
||||||
|
|
||||||
res.send({
|
res.send({ people })
|
||||||
people,
|
} catch (e) {
|
||||||
});
|
console.error(e)
|
||||||
} catch (e) {
|
res.sendStatus(404)
|
||||||
console.error(e);
|
}
|
||||||
res.sendStatus(404);
|
}
|
||||||
}
|
|
||||||
};
|
export default getPeople
|
||||||
|
|
|
||||||
10
crabfit-backend/routes/index.js
Normal file
10
crabfit-backend/routes/index.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
export { default as stats } from './stats'
|
||||||
|
export { default as getEvent } from './getEvent'
|
||||||
|
export { default as createEvent } from './createEvent'
|
||||||
|
export { default as getPeople } from './getPeople'
|
||||||
|
export { default as createPerson } from './createPerson'
|
||||||
|
export { default as login } from './login'
|
||||||
|
export { default as updatePerson } from './updatePerson'
|
||||||
|
|
||||||
|
export { default as taskCleanup } from './taskCleanup'
|
||||||
|
export { default as taskRemoveOrphans } from './taskRemoveOrphans'
|
||||||
|
|
@ -1,33 +1,35 @@
|
||||||
const bcrypt = require('bcrypt');
|
import bcrypt from 'bcrypt'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const login = async (req, res) => {
|
||||||
const { eventId, personName } = req.params;
|
const { eventId, personName } = req.params
|
||||||
const { person } = req.body;
|
const { person } = req.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query = req.datastore.createQuery(req.types.person)
|
const query = req.datastore.createQuery(req.types.person)
|
||||||
.filter('eventId', eventId)
|
.filter('eventId', eventId)
|
||||||
.filter('name', personName);
|
.filter('name', personName)
|
||||||
let personResult = (await req.datastore.runQuery(query))[0][0];
|
const personResult = (await req.datastore.runQuery(query))[0][0]
|
||||||
|
|
||||||
if (personResult) {
|
if (personResult) {
|
||||||
if (personResult.password) {
|
if (personResult.password) {
|
||||||
const passwordsMatch = person && person.password && await bcrypt.compare(person.password, personResult.password);
|
const passwordsMatch = person && person.password && await bcrypt.compare(person.password, personResult.password)
|
||||||
if (!passwordsMatch) {
|
if (!passwordsMatch) {
|
||||||
return res.status(401).send('Incorrect password');
|
return res.status(401).send('Incorrect password')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
name: personName,
|
name: personName,
|
||||||
availability: personResult.availability,
|
availability: personResult.availability,
|
||||||
created: personResult.created,
|
created: personResult.created,
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default login
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,29 @@
|
||||||
const package = require('../package.json');
|
import packageJson from '../package.json'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const stats = async (req, res) => {
|
||||||
let eventCount = null;
|
let eventCount = null
|
||||||
let personCount = null;
|
let personCount = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const eventResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'eventCount'])))[0] || null;
|
const eventResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'eventCount'])))[0] || null
|
||||||
const personResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'personCount'])))[0] || null;
|
const personResult = (await req.datastore.get(req.datastore.key([req.types.stats, 'personCount'])))[0] || null
|
||||||
|
|
||||||
if (eventResult) {
|
if (eventResult) {
|
||||||
eventCount = eventResult.value;
|
eventCount = eventResult.value
|
||||||
}
|
}
|
||||||
if (personResult) {
|
if (personResult) {
|
||||||
personCount = personResult.value;
|
personCount = personResult.value
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
eventCount,
|
eventCount,
|
||||||
personCount,
|
personCount,
|
||||||
version: package.version,
|
version: packageJson.version,
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default stats
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,48 @@
|
||||||
const dayjs = require('dayjs');
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const taskCleanup = async (req, res) => {
|
||||||
if (req.header('X-Appengine-Cron') === undefined) {
|
if (req.header('X-Appengine-Cron') === undefined) {
|
||||||
return res.status(400).send('This task can only be run from a cron job');
|
return res.status(400).send('This task can only be run from a cron job')
|
||||||
}
|
}
|
||||||
|
|
||||||
const threeMonthsAgo = dayjs().subtract(3, 'month').unix();
|
|
||||||
|
|
||||||
console.log(`Running cleanup task at ${dayjs().format('h:mma D MMM YYYY')}`);
|
const threeMonthsAgo = dayjs().subtract(3, 'month').unix()
|
||||||
|
|
||||||
try {
|
console.log(`Running cleanup task at ${dayjs().format('h:mma D MMM YYYY')}`)
|
||||||
|
|
||||||
|
try {
|
||||||
// Fetch events that haven't been visited in over 3 months
|
// Fetch events that haven't been visited in over 3 months
|
||||||
const eventQuery = req.datastore.createQuery(req.types.event).filter('visited', '<', threeMonthsAgo);
|
const eventQuery = req.datastore.createQuery(req.types.event).filter('visited', '<', threeMonthsAgo)
|
||||||
let oldEvents = (await req.datastore.runQuery(eventQuery))[0];
|
const oldEvents = (await req.datastore.runQuery(eventQuery))[0]
|
||||||
|
|
||||||
if (oldEvents && oldEvents.length > 0) {
|
if (oldEvents && oldEvents.length > 0) {
|
||||||
let oldEventIds = oldEvents.map(e => e[req.datastore.KEY].name);
|
const oldEventIds = oldEvents.map(e => e[req.datastore.KEY].name)
|
||||||
console.log(`Found ${oldEventIds.length} events to remove`);
|
console.log(`Found ${oldEventIds.length} events to remove`)
|
||||||
|
|
||||||
// Fetch availabilities linked to the events discovered
|
// Fetch availabilities linked to the events discovered
|
||||||
let peopleDiscovered = 0;
|
let peopleDiscovered = 0
|
||||||
await Promise.all(oldEventIds.map(async (eventId) => {
|
await Promise.all(oldEventIds.map(async eventId => {
|
||||||
const peopleQuery = req.datastore.createQuery(req.types.person).filter('eventId', eventId);
|
const peopleQuery = req.datastore.createQuery(req.types.person).filter('eventId', eventId)
|
||||||
let oldPeople = (await req.datastore.runQuery(peopleQuery))[0];
|
const oldPeople = (await req.datastore.runQuery(peopleQuery))[0]
|
||||||
|
|
||||||
if (oldPeople && oldPeople.length > 0) {
|
if (oldPeople && oldPeople.length > 0) {
|
||||||
peopleDiscovered += oldPeople.length;
|
peopleDiscovered += oldPeople.length
|
||||||
await req.datastore.delete(oldPeople.map(person => person[req.datastore.KEY]));
|
await req.datastore.delete(oldPeople.map(person => person[req.datastore.KEY]))
|
||||||
}
|
}
|
||||||
}));
|
}))
|
||||||
|
|
||||||
await req.datastore.delete(oldEvents.map(event => event[req.datastore.KEY]));
|
await req.datastore.delete(oldEvents.map(event => event[req.datastore.KEY]))
|
||||||
|
|
||||||
console.log(`Cleanup successful: ${oldEventIds.length} events and ${peopleDiscovered} people removed`);
|
console.log(`Cleanup successful: ${oldEventIds.length} events and ${peopleDiscovered} people removed`)
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.sendStatus(200)
|
||||||
} else {
|
} else {
|
||||||
console.log(`Found 0 events to remove, ending cleanup`);
|
console.log('Found 0 events to remove, ending cleanup')
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default taskCleanup
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
const dayjs = require('dayjs');
|
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
|
||||||
if (req.header('X-Appengine-Cron') === undefined) {
|
|
||||||
return res.status(400).send('This task can only be run from a cron job');
|
|
||||||
}
|
|
||||||
|
|
||||||
const threeMonthsAgo = dayjs().subtract(3, 'month').unix();
|
|
||||||
|
|
||||||
console.log(`Running LEGACY cleanup task at ${dayjs().format('h:mma D MMM YYYY')}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Fetch events that haven't been visited in over 3 months
|
|
||||||
const eventQuery = req.datastore.createQuery(req.types.event).order('created');
|
|
||||||
let oldEvents = (await req.datastore.runQuery(eventQuery))[0];
|
|
||||||
|
|
||||||
oldEvents = oldEvents.filter(event => !event.hasOwnProperty('visited'));
|
|
||||||
|
|
||||||
if (oldEvents && oldEvents.length > 0) {
|
|
||||||
console.log(`Found ${oldEvents.length} events that were missing a visited date`);
|
|
||||||
|
|
||||||
// Filter events that are older than 3 months and missing a visited date
|
|
||||||
oldEvents = oldEvents.filter(event => event.created < threeMonthsAgo);
|
|
||||||
|
|
||||||
if (oldEvents && oldEvents.length > 0) {
|
|
||||||
let oldEventIds = oldEvents.map(e => e[req.datastore.KEY].name);
|
|
||||||
|
|
||||||
// Fetch availabilities linked to the events discovered
|
|
||||||
let eventsRemoved = 0;
|
|
||||||
let peopleRemoved = 0;
|
|
||||||
await Promise.all(oldEventIds.map(async (eventId) => {
|
|
||||||
const peopleQuery = req.datastore.createQuery(req.types.person).filter('eventId', eventId);
|
|
||||||
let oldPeople = (await req.datastore.runQuery(peopleQuery))[0];
|
|
||||||
|
|
||||||
let deleteEvent = true;
|
|
||||||
if (oldPeople && oldPeople.length > 0) {
|
|
||||||
oldPeople.forEach(person => {
|
|
||||||
if (person.created >= threeMonthsAgo) {
|
|
||||||
deleteEvent = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (deleteEvent) {
|
|
||||||
if (oldPeople && oldPeople.length > 0) {
|
|
||||||
peopleRemoved += oldPeople.length;
|
|
||||||
await req.datastore.delete(oldPeople.map(person => person[req.datastore.KEY]));
|
|
||||||
}
|
|
||||||
eventsRemoved++;
|
|
||||||
await req.datastore.delete(req.datastore.key([req.types.event, eventId]));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
console.log(`Legacy cleanup successful: ${eventsRemoved} events and ${peopleRemoved} people removed`);
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
} else {
|
|
||||||
console.log('Found 0 events that are older than 3 months and missing a visited date, ending LEGACY cleanup');
|
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('Found no events that are missing a visited date, ending LEGACY cleanup [DISABLE ME!]');
|
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,46 +1,48 @@
|
||||||
const dayjs = require('dayjs');
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const taskRemoveOrphans = async (req, res) => {
|
||||||
if (req.header('X-Appengine-Cron') === undefined) {
|
if (req.header('X-Appengine-Cron') === undefined) {
|
||||||
return res.status(400).send('This task can only be run from a cron job');
|
return res.status(400).send('This task can only be run from a cron job')
|
||||||
}
|
}
|
||||||
|
|
||||||
const threeMonthsAgo = dayjs().subtract(3, 'month').unix();
|
const threeMonthsAgo = dayjs().subtract(3, 'month').unix()
|
||||||
|
|
||||||
console.log(`Running orphan removal task at ${dayjs().format('h:mma D MMM YYYY')}`);
|
console.log(`Running orphan removal task at ${dayjs().format('h:mma D MMM YYYY')}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch people that are older than 3 months
|
// Fetch people that are older than 3 months
|
||||||
const peopleQuery = req.datastore.createQuery(req.types.person).filter('created', '<', threeMonthsAgo);
|
const peopleQuery = req.datastore.createQuery(req.types.person).filter('created', '<', threeMonthsAgo)
|
||||||
let oldPeople = (await req.datastore.runQuery(peopleQuery))[0];
|
const oldPeople = (await req.datastore.runQuery(peopleQuery))[0]
|
||||||
|
|
||||||
if (oldPeople && oldPeople.length > 0) {
|
if (oldPeople && oldPeople.length > 0) {
|
||||||
console.log(`Found ${oldPeople.length} people older than 3 months, checking for events`);
|
console.log(`Found ${oldPeople.length} people older than 3 months, checking for events`)
|
||||||
|
|
||||||
// Fetch events linked to the people discovered
|
// Fetch events linked to the people discovered
|
||||||
let peopleWithoutEvents = 0;
|
let peopleWithoutEvents = 0
|
||||||
await Promise.all(oldPeople.map(async (person) => {
|
await Promise.all(oldPeople.map(async person => {
|
||||||
let event = (await req.datastore.get(req.datastore.key([req.types.event, person.eventId])))[0];
|
const event = (await req.datastore.get(req.datastore.key([req.types.event, person.eventId])))[0]
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
peopleWithoutEvents++;
|
peopleWithoutEvents++
|
||||||
await req.datastore.delete(person[req.datastore.KEY]);
|
await req.datastore.delete(person[req.datastore.KEY])
|
||||||
}
|
}
|
||||||
}));
|
}))
|
||||||
|
|
||||||
if (peopleWithoutEvents > 0) {
|
if (peopleWithoutEvents > 0) {
|
||||||
console.log(`Orphan removal successful: ${oldEventIds.length} events and ${peopleDiscovered} people removed`);
|
console.log(`Orphan removal successful: ${peopleWithoutEvents} people removed`)
|
||||||
res.sendStatus(200);
|
res.sendStatus(200)
|
||||||
} else {
|
} else {
|
||||||
console.log(`Found 0 people without events, ending orphan removal`);
|
console.log('Found 0 people without events, ending orphan removal')
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`Found 0 people older than 3 months, ending orphan removal`);
|
console.log('Found 0 people older than 3 months, ending orphan removal')
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
res.sendStatus(404);
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default taskRemoveOrphans
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,40 @@
|
||||||
const bcrypt = require('bcrypt');
|
import bcrypt from 'bcrypt'
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
const updatePerson = async (req, res) => {
|
||||||
const { eventId, personName } = req.params;
|
const { eventId, personName } = req.params
|
||||||
const { person } = req.body;
|
const { person } = req.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query = req.datastore.createQuery(req.types.person)
|
const query = req.datastore.createQuery(req.types.person)
|
||||||
.filter('eventId', eventId)
|
.filter('eventId', eventId)
|
||||||
.filter('name', personName);
|
.filter('name', personName)
|
||||||
let personResult = (await req.datastore.runQuery(query))[0][0];
|
const personResult = (await req.datastore.runQuery(query))[0][0]
|
||||||
|
|
||||||
if (personResult) {
|
if (personResult) {
|
||||||
if (person && person.availability) {
|
if (person && person.availability) {
|
||||||
if (personResult.password) {
|
if (personResult.password) {
|
||||||
const passwordsMatch = person.password && await bcrypt.compare(person.password, personResult.password);
|
const passwordsMatch = person.password && await bcrypt.compare(person.password, personResult.password)
|
||||||
if (!passwordsMatch) {
|
if (!passwordsMatch) {
|
||||||
return res.status(401).send('Incorrect password');
|
return res.status(401).send('Incorrect password')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
personResult.availability = person.availability;
|
await req.datastore.upsert({
|
||||||
|
...personResult,
|
||||||
|
availability: person.availability,
|
||||||
|
})
|
||||||
|
|
||||||
await req.datastore.upsert(personResult);
|
res.sendStatus(200)
|
||||||
|
} else {
|
||||||
|
res.sendStatus(400)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
res.sendStatus(400)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.sendStatus(200);
|
export default updatePerson
|
||||||
} else {
|
|
||||||
res.sendStatus(400);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
res.sendStatus(400);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -230,19 +230,6 @@ paths:
|
||||||
description: "Not found"
|
description: "Not found"
|
||||||
400:
|
400:
|
||||||
description: "Not called from a cron job"
|
description: "Not called from a cron job"
|
||||||
"/tasks/legacyCleanup":
|
|
||||||
get:
|
|
||||||
summary: "Delete events inactive for more than 3 months that don't have a visited date"
|
|
||||||
operationId: "taskLegacyCleanup"
|
|
||||||
tags:
|
|
||||||
- tasks
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: "OK"
|
|
||||||
404:
|
|
||||||
description: "Not found"
|
|
||||||
400:
|
|
||||||
description: "Not called from a cron job"
|
|
||||||
"/tasks/removeOrphans":
|
"/tasks/removeOrphans":
|
||||||
get:
|
get:
|
||||||
summary: "Deletes people if the event they were created under no longer exists"
|
summary: "Deletes people if the event they were created under no longer exists"
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue