Switching to use SQLite instead of MySQL

This commit is contained in:
Collin Duncan 2023-01-28 10:51:46 +01:00
parent dd46f61ecc
commit 0c6c9df93f
No known key found for this signature in database
9 changed files with 1031 additions and 295 deletions

3
.gitignore vendored
View file

@ -7,3 +7,6 @@ dist/
# vscode # vscode
.vscode/settings.json .vscode/settings.json
# sqlite
autobaan_db

View file

@ -1,16 +1,4 @@
services: services:
database:
image: mysql:latest
restart: always
env_file: ./database/.env
command: --default-authentication-plugin=mysql_native_password
ports:
- 3306:3306
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
timeout: 5s
retries: 10
http: http:
build: build:
context: .. context: ..
@ -19,6 +7,5 @@ services:
env_file: ./server/.env env_file: ./server/.env
ports: ports:
- 3000:3000 - 3000:3000
depends_on: # volumes:
database: # - ./autobaan_db:/app/autobaan_db
condition: service_healthy

1129
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -29,14 +29,13 @@
"argon2": "^0.30.3", "argon2": "^0.30.3",
"axios": "^1.2.0", "axios": "^1.2.0",
"dayjs": "^1.11.6", "dayjs": "^1.11.6",
"mysql": "^2.18.1",
"node-cron": "^3.0.2", "node-cron": "^3.0.2",
"puppeteer": "^19.5.2", "puppeteer": "^19.5.2",
"sqlite3": "^5.1.4",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.2.3", "@types/jest": "^29.2.3",
"@types/mysql": "^2.15.21",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/node-cron": "^3.0.6", "@types/node-cron": "^3.0.6",
"@types/puppeteer": "^7.0.4", "@types/puppeteer": "^7.0.4",

View file

@ -1,81 +1,34 @@
import mysql, { Connection, ConnectionConfig, FieldInfo } from 'mysql' import { resolve } from 'path'
import { TABLE_reservations } from './sql' import sqlite from 'sqlite3'
import { CREATE_TABLE_reservations } from './sql'
const createConnectionConfig = async (): Promise<ConnectionConfig> => { export const run = async (sql: string, params?: unknown) => {
const host = process.env.MYSQL_HOST const db = new sqlite.Database(resolve('autobaan_db'))
const user = process.env.MYSQL_USER await new Promise<void>((res, rej) => {
const password = process.env.MYSQL_PASSWORD db.run(sql, params, (err) => {
const database = process.env.MYSQL_DATABASE if (err) rej(err)
if (!user || !password || !database) {
throw new DatabaseEnvironmentError(
'Required environment variables are missing'
)
}
return {
host,
user,
password,
database,
}
}
let connection: Connection
export const getConnection = async (): Promise<Connection> => {
if (!connection) {
const config = await createConnectionConfig()
connection = mysql.createConnection(config)
}
return connection
}
export type ResultSet<T> = T[]
export const connect = async () => {
return new Promise<void>((res, rej) =>
getConnection().then((cn) => {
cn.connect((err) => {
if (err) {
rej(err)
}
res() res()
}) })
}) })
) db.close()
} }
export const disconnect = async () => { export const all = async <T>(sql: string, params?: unknown) => {
return new Promise<void>((res, rej) => const db = new sqlite.Database(resolve('autobaan_db'))
getConnection().then((cn) => { const rows = await new Promise<T[]>((res, rej) =>
cn.end((err) => { db.all(sql, params, (err, rows) => {
if (err) { if (err) rej(err)
rej(err) res(rows)
}
res()
})
}) })
) )
} db.close()
return rows
export const query = async <T = unknown, V = unknown>(
sql: string,
values?: V
): Promise<{ results: ResultSet<T>; fields?: FieldInfo[] }> => {
return new Promise((res, rej) => {
connection.query({ sql, values }, (err, results, fields) => {
if (err) {
rej(err)
}
res({ results, fields })
})
})
} }
export const init = async () => { export const init = async () => {
try { try {
await connect() await run(CREATE_TABLE_reservations)
await query(TABLE_reservations) } catch (err: unknown) {
} catch (err: any) {
console.error(err) console.error(err)
} }
} }

View file

@ -1,4 +1,4 @@
export const TABLE_reservations = ` export const CREATE_TABLE_reservations = `
CREATE TABLE IF NOT EXISTS reservations ( CREATE TABLE IF NOT EXISTS reservations (
id VARCHAR(36) NOT NULL PRIMARY KEY, id VARCHAR(36) NOT NULL PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE, username VARCHAR(64) NOT NULL UNIQUE,

View file

@ -1,7 +1,7 @@
import { Dayjs } from 'dayjs' import { Dayjs } from 'dayjs'
import { v4 } from 'uuid' import { v4 } from 'uuid'
import dayjs from './dayjs' import dayjs from './dayjs'
import { query } from './database' import { all, run } from './database'
const RESERVATION_AVAILABLE_WITHIN_DAYS = 7 const RESERVATION_AVAILABLE_WITHIN_DAYS = 7
@ -127,7 +127,7 @@ export class Reservation {
} }
public static async save(res: Reservation) { public static async save(res: Reservation) {
await query( await run(
` `
INSERT INTO reservations INSERT INTO reservations
( (
@ -163,19 +163,17 @@ export class Reservation {
} }
public static async delete(res: Reservation) { public static async delete(res: Reservation) {
await query( await run(
` `
DELETE FROM reservations DELETE FROM reservations
WHERE id = $ WHERE id = $
`, `,
[ [res.id]
res.id,
]
) )
} }
public static async fetchById(id: string): Promise<Reservation | null> { public static async fetchById(id: string): Promise<Reservation | null> {
const response = await query<SqlReservation>( const response = await all<SqlReservation>(
` `
SELECT * SELECT *
FROM reservations FROM reservations
@ -184,8 +182,8 @@ export class Reservation {
[id] [id]
) )
if (response.results.length === 1) { if (response.length === 1) {
const sqlReservation = response.results[0] const sqlReservation = response[0]
const res = new Reservation( const res = new Reservation(
{ {
username: sqlReservation.username, username: sqlReservation.username,
@ -197,7 +195,7 @@ export class Reservation {
}, },
{ id: sqlReservation.opponent_id, name: sqlReservation.opponent_name }, { id: sqlReservation.opponent_id, name: sqlReservation.opponent_name },
undefined, undefined,
sqlReservation.id, sqlReservation.id
) )
return res return res
} }
@ -206,7 +204,7 @@ export class Reservation {
} }
public static async fetchFirst(): Promise<Reservation | null> { public static async fetchFirst(): Promise<Reservation | null> {
const response = await query<SqlReservation>( const response = await all<SqlReservation>(
` `
SELECT * SELECT *
FROM reservations FROM reservations
@ -215,8 +213,8 @@ export class Reservation {
` `
) )
if (response.results.length === 1) { if (response.length === 1) {
const sqlReservation = response.results[0] const sqlReservation = response[0]
const res = new Reservation( const res = new Reservation(
{ {
username: sqlReservation.username, username: sqlReservation.username,
@ -228,7 +226,7 @@ export class Reservation {
}, },
{ id: sqlReservation.opponent_id, name: sqlReservation.opponent_name }, { id: sqlReservation.opponent_id, name: sqlReservation.opponent_name },
undefined, undefined,
sqlReservation.id, sqlReservation.id
) )
return res return res
} }
@ -236,8 +234,11 @@ export class Reservation {
return null return null
} }
public static async fetchByDate(date: Dayjs, limit = 20): Promise<Reservation[]> { public static async fetchByDate(
const response = await query<SqlReservation>( date: Dayjs,
limit = 20
): Promise<Reservation[]> {
const response = await all<SqlReservation>(
` `
SELECT * SELECT *
FROM reservations FROM reservations
@ -245,14 +246,13 @@ export class Reservation {
ORDER BY date_range_start DESC ORDER BY date_range_start DESC
LIMIT ?; LIMIT ?;
`, `,
[ [date.format('YYYY-MM-DD'), limit]
date.format('YYYY-MM-DD'),
limit,
]
) )
if (response.results.length > 0) { if (response.length > 0) {
return response.results.map((sqlReservation) => new Reservation( return response.map(
(sqlReservation) =>
new Reservation(
{ {
username: sqlReservation.username, username: sqlReservation.username,
password: sqlReservation.password, password: sqlReservation.password,
@ -261,10 +261,14 @@ export class Reservation {
start: dayjs(sqlReservation.date_range_start), start: dayjs(sqlReservation.date_range_start),
end: dayjs(sqlReservation.date_range_end), end: dayjs(sqlReservation.date_range_end),
}, },
{ id: sqlReservation.opponent_id, name: sqlReservation.opponent_name }, {
id: sqlReservation.opponent_id,
name: sqlReservation.opponent_name,
},
undefined, undefined,
sqlReservation.id, sqlReservation.id
)) )
)
} }
return [] return []

View file

@ -1,4 +1,4 @@
import { disconnect, init } from '../common/database' import { init } from '../common/database'
import { Logger, LogLevel } from '../common/logger' import { Logger, LogLevel } from '../common/logger'
import server from './http' import server from './http'
import { startTasks, stopTasks } from './cron' import { startTasks, stopTasks } from './cron'
@ -14,7 +14,6 @@ process.on('uncaughtException', (error, origin) => {
}) })
process.on('beforeExit', async () => { process.on('beforeExit', async () => {
await disconnect()
stopTasks() stopTasks()
}) })

View file

@ -56,7 +56,7 @@ describe('scheduler', () => {
{ start, end }, { start, end },
{ id: '123', name: 'collin' }, { id: '123', name: 'collin' },
undefined, undefined,
'1234', '1234'
), ),
scheduledFor: start scheduledFor: start
.subtract(7, 'days') .subtract(7, 'days')