Skip to content

Commit

Permalink
Modernize API: Update Node.js & deps, ESM, & more
Browse files Browse the repository at this point in the history
  • Loading branch information
jstayton committed Sep 14, 2024
1 parent fac7fb0 commit 0c1b664
Show file tree
Hide file tree
Showing 67 changed files with 9,197 additions and 7,762 deletions.
1 change: 0 additions & 1 deletion .env.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
NODE_ENV=development

BUGSNAG_API_KEY=
DATABASE_URL=postgres://postgres:password@localhost:5432/piratepx_development
PORT=3000
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

16 changes: 0 additions & 16 deletions .eslintrc.json

This file was deleted.

2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14
22
25 changes: 12 additions & 13 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
MIT License

Copyright (c) 2020 piratepx
Copyright (c) 2024 piratepx

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 changes: 10 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ there to make this possible, however, they're pretty much separate codebases.
### Backend

The backend is a JSON REST API built in [Node.js](https://nodejs.org/) using
[Fastify](https://www.fastify.io/) and
[Fastify](https://www.fastify.dev/) and
[Objection.js](https://vincit.github.io/objection.js/). It persists data to a
[PostgreSQL](https://www.postgresql.org/) database.

Expand All @@ -40,14 +40,13 @@ development, with a focus on backend-specific details. See

- [Node.js](https://nodejs.org/) (see `engines.node` in
[`package.json`](package.json))
- [PostgreSQL](https://www.postgresql.org/) >= v11
- [PostgreSQL](https://www.postgresql.org/) >= v16

[Docker Compose](https://docs.docker.com/compose/) is used to run PostgreSQL as
configured in [`docker-compose.yml`](docker-compose.yml). Once installed, simply
run:
configured in [`compose.yaml`](compose.yaml). Once installed, simply run:

```bash
$ docker-compose up
$ docker compose up
```

The app itself is not run in a Docker container in development, as it's easy
Expand Down Expand Up @@ -77,18 +76,10 @@ See [`.env.dist`](.env.dist) for an example.

### Database

#### Create

Ensure PostgreSQL is running, then:

```bash
$ npm run dev:db:create
```

#### Migrations

[Knex.js](https://knexjs.org/#Migrations) is used to manage database migrations,
which are located in [`api/db/migrations`](api/db/migrations).
[Knex.js](https://knexjs.org/guide/migrations.html) is used to manage database
migrations, which are located in [`api/db/migrations`](api/db/migrations).

To run the latest migrations:

Expand All @@ -106,15 +97,15 @@ $ npm run dev

### Code Style & Linting

[Prettier](https://prettier.com/) is setup to enforce a consistent code style.
[Prettier](https://prettier.io/) is setup to enforce a consistent code style.
It's highly recommended to
[add an integration to your editor](https://prettier.io/docs/en/editors.html)
that automatically formats on save.

[ESLint](https://eslint.org/) is setup with the
["recommended" rules](https://eslint.org/docs/rules/) to enforce a level of code
quality. It's also highly recommended to
[add an integration to your editor](https://eslint.org/docs/user-guide/integrations#editors)
["recommended" rules](https://eslint.org/docs/latest/rules/) to enforce a level
of code quality. It's also highly recommended to
[add an integration to your editor](https://eslint.org/docs/latest/use/integrations#editors)
that automatically formats on save.

To run via the command line:
Expand Down
39 changes: 22 additions & 17 deletions api/app.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
const path = require('path')
import { join } from 'node:path'

const AutoLoad = require('fastify-autoload')
import autoLoad from '@fastify/autoload'

const initializers = require('./initializers')
import initializers from '#api/initializers/index'

require('@/services/bugsnag')
import { apiDir } from '#api/lib/paths'
import cronJobScheduler from '#api/services/cron_job_scheduler'
import errorHandler, { errorSchema } from '#api/services/errors/error_handler'

const errorHandler = require('@/plugins/error_handler')
const scheduleCronJobs = require('@/services/schedule_cron_jobs')

module.exports = async (fastify, opts) => {
// eslint-disable-next-line no-unused-vars
async function app(fastify, options) {
await initializers()

fastify.register(AutoLoad, {
dir: path.join(__dirname, 'plugins', 'common'),
options: { ...opts },
})
fastify.addSchema(errorSchema)
fastify.setErrorHandler(errorHandler)

fastify.register(AutoLoad, {
dir: path.join(__dirname, 'routes'),
options: { ...opts },
await fastify.register(autoLoad, {
dir: join(apiDir, 'plugins'),
encapsulate: false,
})

fastify.setErrorHandler(errorHandler)
fastify.register(autoLoad, {
dir: join(apiDir, 'routes'),
autoHooks: true,
cascadeHooks: true,
routeParams: true,
})

scheduleCronJobs()
cronJobScheduler()
}

export default app
12 changes: 10 additions & 2 deletions api/db/config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { env } from 'node:process'

const defaults = {
client: 'pg',
connection: process.env.DATABASE_URL,
connection: env.DATABASE_URL,
pool: {
min: 0,
max: 10,
},
}

module.exports = {
const config = {
development: {
...defaults,
debug: true,
Expand All @@ -12,3 +18,5 @@ module.exports = {
...defaults,
},
}

export default config
9 changes: 6 additions & 3 deletions api/db/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const Knex = require('knex')
import Knex from 'knex'
import { env } from 'node:process'

const config = require('@/db/config')
import config from '#api/db/config'

module.exports = Knex(config[process.env.NODE_ENV])
const db = Knex(config[env.NODE_ENV])

export default db
10 changes: 8 additions & 2 deletions api/db/migrations/20200713072454_create_uuid_ossp_extension.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
exports.up = (knex) => knex.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
async function up(knex) {
return await knex.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
}

exports.down = (knex) => knex.raw('DROP EXTENSION IF EXISTS "uuid-ossp"')
async function down(knex) {
return await knex.raw('DROP EXTENSION IF EXISTS "uuid-ossp"')
}

export { up, down }
11 changes: 8 additions & 3 deletions api/db/migrations/20200713072746_create_users.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
exports.up = (knex) =>
knex.schema.createTable('users', (t) => {
async function up(knex) {
return await knex.schema.createTable('users', (t) => {
t.uuid('id')
.defaultTo(knex.raw('uuid_generate_v4()'))
.notNullable()
Expand All @@ -8,5 +8,10 @@ exports.up = (knex) =>
t.datetime('created_at').notNullable()
t.datetime('updated_at').notNullable()
})
}

exports.down = (knex) => knex.schema.dropTable('users')
async function down(knex) {
return await knex.schema.dropTable('users')
}

export { up, down }
11 changes: 8 additions & 3 deletions api/db/migrations/20200713073113_create_projects.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
exports.up = (knex) =>
knex.schema.createTable('projects', (t) => {
async function up(knex) {
return await knex.schema.createTable('projects', (t) => {
t.uuid('id')
.defaultTo(knex.raw('uuid_generate_v4()'))
.notNullable()
Expand All @@ -11,5 +11,10 @@ exports.up = (knex) =>
t.datetime('created_at').notNullable()
t.datetime('updated_at').notNullable()
})
}

exports.down = (knex) => knex.schema.dropTable('projects')
async function down(knex) {
return await knex.schema.dropTable('projects')
}

export { up, down }
11 changes: 8 additions & 3 deletions api/db/migrations/20200713073310_create_counts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
exports.up = (knex) =>
knex.schema.createTable('counts', (t) => {
async function up(knex) {
return await knex.schema.createTable('counts', (t) => {
t.uuid('id')
.defaultTo(knex.raw('uuid_generate_v4()'))
.notNullable()
Expand All @@ -13,5 +13,10 @@ exports.up = (knex) =>

t.unique(['project_id', 'identifier', 'date'])
})
}

exports.down = (knex) => knex.schema.dropTable('counts')
async function down(knex) {
return await knex.schema.dropTable('counts')
}

export { up, down }
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
exports.up = (knex) =>
knex.schema.table('projects', (t) => {
async function up(knex) {
return await knex.schema.table('projects', (t) => {
t.string('shared_secret').unique()
})
}

exports.down = (knex) =>
knex.schema.table('projects', (t) => {
async function down(knex) {
return await knex.schema.table('projects', (t) => {
t.dropColumn('shared_secret')
})
}

export { up, down }
15 changes: 8 additions & 7 deletions api/initializers/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
require('dotenv').config()
require('module-alias/register')
import 'dotenv/config'

const objection = require('@/initializers/objection')
const pg = require('@/initializers/pg')
import initializeObjection from '#api/initializers/objection'
import initializePG from '#api/initializers/pg'

module.exports = async () => {
pg()
await objection()
async function initializers() {
initializePG()
await initializeObjection()
}

export default initializers
10 changes: 6 additions & 4 deletions api/initializers/objection.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const { Model } = require('objection')
import { Model } from 'objection'

const db = require('@/db')
import db from '#api/db/index'

module.exports = () => {
async function initializeObjection() {
Model.knex(db)

return db.raw("SELECT 'Hello?'")
await db.raw("SELECT 'Hello?'")
}

export default initializeObjection
8 changes: 5 additions & 3 deletions api/initializers/pg.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const { types } = require('pg')
import pg from 'pg'

module.exports = () => {
function initializePG() {
// Parse date type as string instead of Date to prevent time zone conversion.
// See: https://github.com/brianc/node-postgres/issues/1844
types.setTypeParser(1082, (date) => date)
pg.types.setTypeParser(1082, (date) => date)
}

export default initializePG
5 changes: 0 additions & 5 deletions api/lib/errors/not_implemented_error.js

This file was deleted.

8 changes: 8 additions & 0 deletions api/lib/paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'

const rootDir = join(dirname(fileURLToPath(import.meta.url)), '..', '..')
const apiDir = join(rootDir, 'api')
const webDir = join(rootDir, 'web')

export { rootDir, apiDir, webDir }
4 changes: 3 additions & 1 deletion api/lib/pick.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = (obj, keys) => {
function pick(obj, keys) {
if (!obj) {
return null
}
Expand All @@ -13,3 +13,5 @@ module.exports = (obj, keys) => {

return picked
}

export default pick
8 changes: 6 additions & 2 deletions api/lib/px.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
module.exports = Buffer.from(
import { Buffer } from 'buffer'

const px = Buffer.from(
'R0lGODlhAQABAHAAACH5BAUAAAAALAAAAAABAAEAAAICRAEAOw==',
'base64'
'base64',
)

export default px
Loading

0 comments on commit 0c1b664

Please sign in to comment.