Compare commits

..

10 Commits

Author SHA1 Message Date
Ratstail91 22735a9ea6 Fully tested the remote database
Updated the base docker image
2024-05-03 09:28:39 +10:00
Ratstail91 9367999ef2 UNTESTED: Updated all dependencies 2024-05-03 07:08:29 +10:00
Ratstail91 d42824d41a UNTESTED: Added database port as a configurable option
Also updated license field in package.json
2024-04-15 21:03:08 +10:00
Ratstail91 7d2186860c Updated dependencies 2024-04-15 17:12:18 +10:00
Ratstail91 99dfaddf04 Updated libraries, docker engine version, docker distro version
This also officially publishes the changes from @eels.
Sorry it took so long.
2023-12-23 23:36:18 +11:00
Kayne Ruse 45b9ac1281 Merge pull request #9 from eels/feature/additional-query-features
feat: resolve long-standing issues + fix minor bugs + update docs
2023-08-30 23:40:52 +10:00
Liam Howell 8d0d804923 docs: fix spelling 2023-08-30 13:37:31 +01:00
Liam Howell fe91ce6ed3 feat: add ability to paginate results, add ability to retrieve certain fields, fix docker-compose output, update docs 2023-08-30 13:19:45 +01:00
Kayne Ruse a6c627a178 Docker behaviour changed, fixed 2023-06-26 23:18:42 +10:00
Kayne Ruse 763ddd8bd7 Updated dependencies, bumped patch version 2023-06-26 23:04:23 +10:00
13 changed files with 696 additions and 409 deletions
+4 -2
View File
@@ -2,7 +2,9 @@ WEB_PORT=3100
WEB_ORIGIN=http://localhost:3001
DB_HOSTNAME=database
DB_HOSTNAME=localhost
DB_PORTNAME=3306
DB_DATABASE=news
DB_USERNAME=news
DB_PASSWORD=venusaur
@@ -17,4 +19,4 @@ DB_LOGGING=
SECRET_ACCESS=access
# Select the default number of articles returned by a GET request
QUERY_LIMIT=10
PAGE_SIZE=10
+2 -2
View File
@@ -1,7 +1,7 @@
FROM node:18-bullseye-slim
FROM node:22-bookworm-slim
WORKDIR "/app"
COPY package*.json ./
COPY package*.json /app
RUN npm install --production
COPY . /app
EXPOSE 3100
+230 -94
View File
@@ -6,7 +6,7 @@ This server is available via docker hub at krgamestudios/news-server.
# Setup
There are multiple ways to run this app - it can run on it's own via `npm start` (for production) or `npm run dev` (for development). it can also run inside docker using `docker-compose up --build` - run `node configure-script.js` to generate docker-compose.yml and startup.sql.
There are multiple ways to run this app - it can run on it's own via `npm start` (for production) or `npm run dev` (for development). it can also run inside docker using `docker compose up --build` - run `node configure-script.js` to generate docker-compose.yml and startup.sql.
To generate an authorization token, use [auth-server](https://github.com/krgamestudios/auth-server). A public-facing development auth-server is available here (tokens are valid for 10 minutes):
@@ -22,112 +22,248 @@ Content-Type: application/json
# API
### `GET /news/:id?`
Get either an array of articles (newest first), or a specified article if the optional "id" parameter is given.
#### Response Body
```jsonc
[{
// [Number] index of the article
"index": index,
// [String] author of the article
"author": author,
// [String] raw body of the article
"body": body,
// [Number] number of times this article has been edited
"edits": edits,
// [String] body of the article rendered as HTML
"rendered": rendered,
// [String] title of the article
"title": title,
// [Date] time article was created
"createdAt": createdAt,
// [Date] time article was updated
"updatedAt": updatedAt,
}]
```
//NOTE: GET will return an empty array if a specific article can't be found
//NOTE: you can add a "limit" query parameter to change the default limit
GET /news?limit=10
#### Available Query Parameters
###
- `fields`
- TYPE: `string`
A comma separated list of the field names you want returning, (index will always be returned)
- `page`
- TYPE: `number`
The current page you want returning
- `page_size`
- TYPE: `number`
The number of results to return. This superseeds the `PAGE_SIZE` environment variable for the query
> **NOTE**
> If a specific article is requested, then just that article is returned rather than an array
//DOCS: get latest news, up to a default limit, or specify the index "id"
GET /news/:id
### `GET /news/archive/:id?`
Get either an array of articles (oldest first), or a specified article if the optional "id" parameter is given.
###
#### Response Body
```jsonc
[{
// [Number] index of the article
"index": index,
//DOCS: get the news starting from the beginning, up to a default limit, or specify the index "id"
GET /news/archive/:id
// [String] author of the article
"author": author,
//DOCS: result (if only a single article is specified, returns just that article rather than an array):
[
{
"index": index, //absolute index of the result
"title": title, //title of the article
"author": author, //author of the aricle
"body": body, //body of the article
"rendered": rendered //body rendered as HTML
"edits": edits //number of times this article has been edited
"createdAt": createdAt //time created
"updatedAt": updatedAt //time updated
},
...
]
// [String] raw body of the article
"body": body,
// [Number] number of times this article has been edited
"edits": edits,
###
// [String] body of the article rendered as HTML
"rendered": rendered,
// [String] title of the article
"title": title,
//DOCS: get the latest metadata, up to a default limit, or specify the index "id"
GET /news/metadata/:id
// [Date] time article was created
"createdAt": createdAt,
###
//DOCS: get the metadata starting from the beginning, up to a default limit, or specify the index "id"
GET /news/archive/metadata/:id
//DOCS: result (if only a single article is specified, returns just that article rather than an array):
[
{
"index": index, //absolute index of the result
"title": title, //title of the article
"author": author //author of the article
"edits": edits //number of times this article has been edited
"createdAt": createdAt //time created
"updatedAt": updatedAt //time updated
},
...
]
###
//DOCS: send a formatted JSON object, returns new index on success, or error on failure
POST /news
Authorization: Bearer XXX
{
"title": title //title of the article
"author": author //author of the article
"body": body //body of the article
}
//DOCS: result (status 200 on success, otherwise an error status):
{
"index": index //new index of the article
}
###
//DOCS: similar to `POST /news`, but allows overwriting an existing article
PATCH /news/:id
Authorization: Bearer XXX
{
"title": title //title of the article, optional
"author": author //author of the article, optional
"body": body //body of the article, optional
}
//DOCS: result: status 200 on success, otherwise an error status
###
//DOCS: remove an article from the news feed
DELETE /news/:id
Authorization: Bearer XXX
//DOCS: result: status 200 on success, otherwise an error status
###
// [Date] time article was updated
"updatedAt": updatedAt,
}]
```
#### Available Query Parameters
- `fields`
- TYPE: `string`
A comma separated list of the field names you want returning, (index will always be returned)
- `page`
- TYPE: `number`
The current page you want returning
- `page_size`
- TYPE: `number`
The number of results to return. This superseeds the `PAGE_SIZE` environment variable for the query
> **NOTE**
> If a specific article is requested, then just that article is returned rather than an array
### `GET /news/metadata/:id?`
Get either an array of metadata (newest first), or a specified article's metadata if the optional "id" parameter is given.
#### Response Body
```jsonc
[{
// [Number] index of the article
"index": index,
// [String] author of the article
"author": author,
// [Number] number of times this article has been edited
"edits": edits,
// [String] title of the article
"title": title,
// [Date] time article was created
"createdAt": createdAt,
// [Date] time article was updated
"updatedAt": updatedAt,
}]
```
#### Available Query Parameters
- `fields`
- TYPE: `string`
A comma separated list of the field names you want returning, (index will always be returned)
- `page`
- TYPE: `number`
The current page you want returning
- `page_size`
- TYPE: `number`
The number of results to return. This superseeds the `PAGE_SIZE` environment variable for the query
> **NOTE**
> If a specific article is requested, then just that article is returned rather than an array
### `GET /news/archive/metadata/:id?`
Get either an array of metadata (oldest first), or a specified article's metadata if the optional "id" parameter is given.
#### Response Body
```jsonc
[{
// [Number] index of the article
"index": index,
// [String] author of the article
"author": author,
// [Number] number of times this article has been edited
"edits": edits,
// [String] title of the article
"title": title,
// [Date] time article was created
"createdAt": createdAt,
// [Date] time article was updated
"updatedAt": updatedAt,
}]
```
#### Available Query Parameters
- `fields`
- TYPE: `string`
A comma separated list of the field names you want returning, (index will always be returned)
- `page`
- TYPE: `number`
The current page you want returning
- `page_size`
- TYPE: `number`
The number of results to return. This supersedes the `PAGE_SIZE` environment variable for the query
> **NOTE**
> If a specific article is requested, then just that article is returned rather than an array
---
### `POST /news`
> **IMPORTANT**
> Requires valid JWT Authorization header (Authorization: Bearer XXX)
Create a new article resource, returns either the new article's index on success, or an error on failure.
#### Request Body
```jsonc
{
// [String] OPTIONAL: title of the article
"title": title,
// [String] OPTIONAL: author of the article
"author": author,
// [String] OPTIONAL: body of the article
"body": body,
}
```
#### Response Body
```jsonc
{
// [Number]: new index of the article
"index": index,
}
```
### `PATCH /news/:id`
> **IMPORTANT**
> Requires valid JWT Authorization header (Authorization: Bearer XXX)
Update an existing article resource, returns either status code 200 on success, or an error status on failure.
#### Request Body
```jsonc
{
// [String] OPTIONAL: title of the article
"title": title,
// [String] OPTIONAL: author of the article
"author": author,
// [String] OPTIONAL: body of the article
"body": body,
}
```
### `DELETE /news/:id`
> **IMPORTANT**
> Requires valid JWT Authorization header (Authorization: Bearer XXX)
Remove an existing article resource from the news feed, returns either status code 200 on success, or an error status on failure.
+54 -29
View File
@@ -33,6 +33,25 @@ const question = (prompt, def = null) => {
const appWebOrigin = await question('Web Origin', `https://example.com`); //TODO: clean these up properly
const appPort = await question('App Port', '3100');
//configure the database address
let dbLocation = '';
while (typeof dbLocation != 'string' || /^[le]/i.test(dbLocation[0]) == false) {
dbLocation = await question('[l]ocal or [e]xternal database?');
}
let appDBHost = '';
let appDBPort = '';
if (/^[l]/i.test(dbLocation[0])) {
appDBHost = 'database';
appDBPort = '3306';
}
else {
appDBHost = await question('DB Host');
appDBPort = await question('DB Port', '3306');
}
//configure the database account
const appDBUser = await question('DB User', appName);
const appDBPass = await question('DB Pass', 'venusaur');
const dbRootPass = await question('DB Root Pass');
@@ -43,39 +62,43 @@ const question = (prompt, def = null) => {
//generate the files
const ymlfile = `
version: '3'
services:
${appName}:
build:
context: .
ports:
- "${appPort}"
- ${appPort}
labels:
- "traefik.enable=true"
- "traefik.http.routers.${appName}router.rule=Host(\`${appWebAddress}\`)"
- "traefik.http.routers.${appName}router.entrypoints=websecure"
- "traefik.http.routers.${appName}router.tls.certresolver=myresolver"
- "traefik.http.routers.${appName}router.service=${appName}service@docker"
- "traefik.http.services.${appName}service.loadbalancer.server.port=${appPort}"
- traefik.enable=true
- traefik.http.routers.${appName}router.rule=Host(\`${appWebAddress}\`)
- traefik.http.routers.${appName}router.entrypoints=websecure
- traefik.http.routers.${appName}router.tls.certresolver=myresolver
- traefik.http.routers.${appName}router.service=${appName}service@docker
- traefik.http.services.${appName}service.loadbalancer.server.port=${appPort}
environment:
- WEB_PORT=${appPort}
- WEB_ORIGIN=${appWebOrigin}
- DB_HOSTNAME=database
- DB_HOSTNAME=${appDBHost}
- DB_PORTNAME=${appDBPort}
- DB_DATABASE=${appName}
- DB_USERNAME=${appDBUser}
- DB_PASSWORD=${appDBPass}
- DB_TIMEZONE=Australia/Sydney
- QUERY_LIMIT=10
- PAGE_SIZE=10
- SECRET_ACCESS=${appSecretAccess}
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- app-network
- app-network${ appDBHost != 'database' ? '' : `
depends_on:
- database
database:
image: mariadb:latest
environment:
MYSQL_DATABASE: ${appName}
MYSQL_TCP_PORT: ${appDBPort}
MYSQL_USER: ${appDBUser}
MYSQL_PASSWORD: ${appDBPass}
MYSQL_ROOT_PASSWORD: ${dbRootPass}
@@ -84,36 +107,38 @@ services:
volumes:
- ./mysql:/var/lib/mysql
- ./startup.sql:/docker-entrypoint-initdb.d/startup.sql:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro`}
traefik_${appName}:
container_name: ${appName}_traefik
image: "traefik:v2.4"
container_name: "traefik"
image: traefik:latest
command:
- "--log.level=ERROR"
- "--api.insecure=false"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=${supportEmail}"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- --log.level=ERROR
- --api.insecure=false
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.websecure.address=:443
- --certificatesresolvers.myresolver.acme.tlschallenge=true
- --certificatesresolvers.myresolver.acme.email=${supportEmail}
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
ports:
- "80:80"
- "443:443"
- 80:80
- 443:443
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- ./letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- app-network
networks:
app-network:
driver: bridge
`;
const dockerfile = `
FROM node:18-bullseye-slim
FROM node:22-bookworm-slim
WORKDIR "/app"
COPY package*.json ./
COPY package*.json /app
RUN npm install --production
COPY . /app
EXPOSE ${appPort}
+348 -195
View File
@@ -1,53 +1,56 @@
{
"name": "news-server",
"version": "1.6.2",
"version": "1.7.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "news-server",
"version": "1.6.2",
"license": "ISC",
"version": "1.7.1",
"license": "Zlib",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0",
"mariadb": "^3.1.1",
"markdown-it": "^13.0.1",
"sequelize": "^6.31.1"
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mariadb": "^3.3.0",
"markdown-it": "^14.1.0",
"sequelize": "^6.37.3"
},
"devDependencies": {
"nodemon": "^2.0.22"
"nodemon": "^3.1.0"
}
},
"node_modules/@types/debug": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
"integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dependencies": {
"@types/ms": "*"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
"version": "7946.0.14",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
"integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
},
"node_modules/@types/ms": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
"version": "0.7.34",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
},
"node_modules/@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
"version": "20.12.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
"integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/validator": {
"version": "13.7.15",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.15.tgz",
"integrity": "sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ=="
"version": "13.11.9",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz",
"integrity": "sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw=="
},
"node_modules/abbrev": {
"version": "1.1.1",
@@ -97,21 +100,24 @@
"dev": true
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@@ -119,7 +125,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@@ -164,28 +170,28 @@
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -198,6 +204,9 @@
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
@@ -228,9 +237,9 @@
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
@@ -260,6 +269,22 @@
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
@@ -286,17 +311,20 @@
}
},
"node_modules/dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dottie": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.3.tgz",
"integrity": "sha512-4liA0PuRkZWQFQjwBypdxPfZaRWiv5tkhMXY2hzsa2pNf5s7U3m9cwUchfNKe8wZQxdGPQQzO6Rm2uGe0rvohQ=="
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz",
"integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA=="
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
@@ -320,9 +348,9 @@
}
},
"node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
@@ -330,6 +358,25 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -344,16 +391,16 @@
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@@ -430,9 +477,9 @@
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
@@ -444,18 +491,26 @@
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.3"
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -473,15 +528,15 @@
"node": ">= 6"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"function-bind": "^1.1.1"
"get-intrinsic": "^1.1.3"
},
"engines": {
"node": ">= 0.4.0"
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-flag": {
@@ -493,6 +548,28 @@
"node": ">=4"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
@@ -504,6 +581,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -600,14 +688,20 @@
}
},
"node_modules/jsonwebtoken": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
"dependencies": {
"jws": "^3.2.2",
"lodash": "^4.17.21",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.3.8"
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
@@ -639,11 +733,11 @@
}
},
"node_modules/linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"dependencies": {
"uc.micro": "^1.0.1"
"uc.micro": "^2.0.0"
}
},
"node_modules/lodash": {
@@ -651,27 +745,62 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
},
"node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
"integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
"engines": {
"node": ">=12"
"node": "14 || >=16.14"
}
},
"node_modules/mariadb": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.1.1.tgz",
"integrity": "sha512-Y5tu9pQr8uZs63FATr7ldODXn8N2aIFlAg/rp6kRTohgQiJfdl9DNGu9PXRTYdY4JgF5mT2ASD81Jdo5kfGYzg==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.3.0.tgz",
"integrity": "sha512-sAL4bJgbfCAtXcE8bXI+NAMzVaPNkIU8hRZUXYfgNFoWB9U57G3XQiMeCx/A6IrS6y7kGwBLylrwgsZQ8kUYlw==",
"dependencies": {
"@types/geojson": "^7946.0.10",
"@types/node": "^17.0.45",
"@types/geojson": "^7946.0.14",
"@types/node": "^20.11.17",
"denque": "^2.1.0",
"iconv-lite": "^0.6.3",
"lru-cache": "^7.14.0"
"lru-cache": "^10.2.0"
},
"engines": {
"node": ">= 12"
"node": ">= 14"
}
},
"node_modules/mariadb/node_modules/iconv-lite": {
@@ -686,24 +815,25 @@
}
},
"node_modules/markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"dependencies": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.js"
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="
},
"node_modules/media-typer": {
"version": "0.3.0",
@@ -769,17 +899,17 @@
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/moment-timezone": {
"version": "0.5.43",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz",
"integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==",
"version": "0.5.45",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz",
"integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==",
"dependencies": {
"moment": "^2.29.4"
},
@@ -801,18 +931,18 @@
}
},
"node_modules/nodemon": {
"version": "2.0.22",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",
"integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
"integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
"debug": "^3.2.7",
"debug": "^4",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.1.2",
"pstree.remy": "^1.1.8",
"semver": "^5.7.1",
"simple-update-notifier": "^1.0.7",
"semver": "^7.5.3",
"simple-update-notifier": "^2.0.0",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
@@ -821,7 +951,7 @@
"nodemon": "bin/nodemon.js"
},
"engines": {
"node": ">=8.10.0"
"node": ">=10"
},
"funding": {
"type": "opencollective",
@@ -829,29 +959,28 @@
}
},
"node_modules/nodemon/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/nodemon/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/nodemon/node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
@@ -885,9 +1014,9 @@
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -917,9 +1046,9 @@
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -951,6 +1080,14 @@
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -974,9 +1111,9 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@@ -1029,9 +1166,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
"integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -1082,9 +1219,9 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/sequelize": {
"version": "6.31.1",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.31.1.tgz",
"integrity": "sha512-cahWtRrYLjqoZP/aurGBoaxn29qQCF4bxkAUPEQ/ozjJjt6mtL4Q113S3N39mQRmX5fgxRbli+bzZARP/N51eg==",
"version": "6.37.3",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.3.tgz",
"integrity": "sha512-V2FTqYpdZjPy3VQrZvjTPnOoLm0KudCRXfGWp48QwhyPPp2yW8z0p0sCYZd/em847Tl2dVxJJ1DR+hF+O77T7A==",
"funding": [
{
"type": "opencollective",
@@ -1092,21 +1229,21 @@
}
],
"dependencies": {
"@types/debug": "^4.1.7",
"@types/validator": "^13.7.1",
"debug": "^4.3.3",
"dottie": "^2.0.2",
"inflection": "^1.13.2",
"@types/debug": "^4.1.8",
"@types/validator": "^13.7.17",
"debug": "^4.3.4",
"dottie": "^2.0.6",
"inflection": "^1.13.4",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"moment-timezone": "^0.5.35",
"pg-connection-string": "^2.5.0",
"retry-as-promised": "^7.0.3",
"semver": "^7.3.5",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"pg-connection-string": "^2.6.1",
"retry-as-promised": "^7.0.4",
"semver": "^7.5.4",
"sequelize-pool": "^7.1.0",
"toposort-class": "^1.0.1",
"uuid": "^8.3.2",
"validator": "^13.7.0",
"validator": "^13.9.0",
"wkx": "^0.5.0"
},
"engines": {
@@ -1185,43 +1322,54 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/simple-update-notifier": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
"integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dev": true,
"dependencies": {
"semver": "~7.0.0"
"semver": "^7.5.3"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
"node": ">=10"
}
},
"node_modules/statuses": {
@@ -1294,9 +1442,9 @@
}
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="
},
"node_modules/undefsafe": {
"version": "2.0.5",
@@ -1304,6 +1452,11 @@
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -1329,9 +1482,9 @@
}
},
"node_modules/validator": {
"version": "13.9.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz",
"integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==",
"version": "13.11.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz",
"integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==",
"engines": {
"node": ">= 0.10"
}
+9 -9
View File
@@ -1,6 +1,6 @@
{
"name": "news-server",
"version": "1.6.3",
"version": "1.7.1",
"description": "An API centric news server. Uses Sequelize and mariaDB by default.",
"main": "server/server.js",
"scripts": {
@@ -13,21 +13,21 @@
"url": "git+https://github.com/krgamestudios/news-server.git"
},
"author": "Kayne Ruse",
"license": "ISC",
"license": "Zlib",
"bugs": {
"url": "https://github.com/krgamestudios/news-server/issues"
},
"homepage": "https://github.com/krgamestudios/news-server#readme",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0",
"mariadb": "^3.1.1",
"markdown-it": "^13.0.1",
"sequelize": "^6.31.1"
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mariadb": "^3.3.0",
"markdown-it": "^14.1.0",
"sequelize": "^6.37.3"
},
"devDependencies": {
"nodemon": "^2.0.22"
"nodemon": "^3.1.0"
}
}
+1
View File
@@ -2,6 +2,7 @@ const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DB_DATABASE, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOSTNAME,
port: process.env.DB_PORTNAME,
dialect: 'mariadb',
timezone: process.env.DB_TIMEZONE,
logging: process.env.DB_LOGGING ? console.log : false
+4 -8
View File
@@ -12,14 +12,10 @@ const edit = require('./edit');
const remove = require('./remove');
//basic route management (all query possibilities)
router.get('/', cors(), query(false, false));
router.get('/:id(\\d+)', cors(), query(false, false));
router.get('/archive', cors(), query(true, false));
router.get('/archive/:id(\\d+)', cors(), query(true, false));
router.get('/metadata', cors(), query(false, true));
router.get('/metadata/:id(\\d+)', cors(), query(false, true));
router.get('/archive/metadata', cors(), query(true, true));
router.get('/archive/metadata/:id(\\d+)', cors(), query(true, true));
router.get('/:id(\\d+)?', cors(), query(false, false));
router.get('/archive/:id(\\d+)?', cors(), query(true, false));
router.get('/metadata/:id(\\d+)?', cors(), query(false, true));
router.get('/archive/metadata/:id(\\d+)?', cors(), query(true, true));
//use middleware to authenticate the rest of the routes
router.use(cors({
+41 -14
View File
@@ -1,18 +1,45 @@
const { Op } = require('sequelize');
const { articles } = require('../database/models');
//the query function that can be reused
const query = (ascending, metadataOnly) => async (req, res) => {
if (process.env.QUERY_LIMIT) {
process.env.PAGE_SIZE = process.env.QUERY_LIMIT;
console.warn('The use of QUERY_LIMIT is deprecated. Please use PAGE_SIZE instead.');
}
if (req.query.limit) {
req.query.page_size = req.query.limit;
console.warn('The use of the limit parameter is deprecated. Please use page_size instead.');
}
const PAGE_SIZE = parseInt(req.query.page_size) || parseInt(process.env.PAGE_SIZE) || 999;
const PAGE = parseInt(req.query.page) || 1;
const ARTICLE_ID = req.params.id ? parseInt(req.params.id) : undefined;
const FIELDS = req.query.fields ? req.query.fields.split(',') : undefined;
const attributes = [
'index',
'author',
'createdAt',
'edits',
'title',
'updatedAt',
].concat(metadataOnly ? [] : [
'body',
'rendered'
]);
//filter out attributes that aren't requested
const attributesToFetch = FIELDS ? attributes.filter((attr) => {
return FIELDS.includes(attr) || attr === 'index';
}) : attributes;
//specific search (id is defined)
if (req.params.id && typeof(parseInt(req.params.id)) === 'number') {
if (typeof(ARTICLE_ID) === 'number' && !isNaN(ARTICLE_ID)) {
const result = await articles.findOne({
attributes: [
'index', 'title', 'author', 'edits', 'createdAt', 'updatedAt', ...(!metadataOnly ? ['body', 'rendered'] : [])
],
attributes: attributesToFetch,
where: {
index: {
[Op.eq]: ascending ? parseInt(req.params.id) : (await articles.max('index')) - parseInt(req.params.id) + 1
}
index: ascending ? ARTICLE_ID : (await articles.max('index') - ARTICLE_ID) + 1,
}
});
@@ -23,16 +50,16 @@ const query = (ascending, metadataOnly) => async (req, res) => {
//default search
else {
const result = await articles.findAndCountAll({
attributes: [
'index', 'title', 'author', 'edits', 'createdAt', 'updatedAt', ...(!metadataOnly ? ['body', 'rendered'] : [])
],
attributes: attributesToFetch,
limit: PAGE_SIZE,
offset: Math.max((PAGE - 1) * PAGE_SIZE, 0),
order: [
['index', ascending ? 'ASC' : 'DESC']
],
limit: parseInt(req.query.limit) || parseInt(process.env.QUERY_LIMIT) || 999
]
});
return res.status(200).json(result.rows || result);
//result is empty array if failed to find
return res.status(200).json(result.rows || result || []);
}
};
+1 -44
View File
@@ -24,48 +24,5 @@ app.get('*', (req, res) => {
server.listen(process.env.WEB_PORT || 3100, async (err) => {
await database.sync();
console.log(`listening to localhost:${process.env.WEB_PORT || 3100}`);
//COMPATABILITY: parse the unrendered data from the database
const markdownIt = require('markdown-it')();
const { articles, revisions } = require('./database/models');
const missingArticles = await articles.findAll({
where: {
rendered: ''
}
});
const missingRevisions = await revisions.findAll({
where: {
rendered: ''
}
});
await Promise.all(
missingArticles.map(async ma => {
ma.update({
rendered: markdownIt.render(ma.body)
}, {
where: {
index: ma.index
}
});
})
)
.then(result => {if (result.length > 0) console.log('Rendered articles in HTML'); })
;
await Promise.all(
missingRevisions.map(async mr => {
mr.update({
rendered: markdownIt.render(mr.body)
}, {
where: {
index: mr.index
}
});
})
)
.then(result => {if (result.length > 0) console.log('Rendered revisions in HTML'); })
;
console.log(`database located at ${process.env.DB_HOSTNAME || '<default>'}:${process.env.DB_PORTNAME || '<default>'}`);
});
+2 -2
View File
@@ -1,4 +1,4 @@
#use this while debugging
CREATE DATABASE IF NOT EXISTS news;
CREATE USER IF NOT EXISTS 'news'@'%' IDENTIFIED BY 'venusaur';
CREATE DATABASE news;
CREATE USER 'news'@'%' IDENTIFIED BY 'venusaur';
GRANT ALL PRIVILEGES ON news.* TO 'news'@'%';
-6
View File
@@ -1,6 +0,0 @@
use news;
ALTER TABLE revisions CHANGE COLUMN id `index` INTEGER(11) UNIQUE NOT NULL AUTO_INCREMENT;
ALTER TABLE revisions DROP FOREIGN KEY revisions_ibfk_1;
ALTER TABLE revisions CHANGE COLUMN originalIndex originalIndex INTEGER(11);
-4
View File
@@ -1,4 +0,0 @@
ALTER TABLE articles ADD COLUMN rendered TEXT DEFAULT "" AFTER body;
ALTER TABLE revisions ADD COLUMN rendered TEXT DEFAULT "" AFTER body;