diff --git a/.gitignore b/.gitignore index 653ba4e..bbeff78 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ public/ .env .DS_Store +discs.json diff --git a/src/endpoints/disc.ts b/src/endpoints/disc.ts index 79d5943..83e9613 100644 --- a/src/endpoints/disc.ts +++ b/src/endpoints/disc.ts @@ -1,14 +1,7 @@ import { Router } from "express"; import { z } from "zod"; -import { - assertRequestIsAuthorized, - buildDiscFilter, - discByIdSchema, - discQuerySchema, - discSchema, - registerEndpoint -} from "@helpers"; +import { assertRequestIsAuthorized, discByIdSchema, discQuerySchema, discSchema, registerEndpoint } from "@helpers"; import { Disc } from "@models"; export const registerDiscEndpoints = () => { @@ -20,8 +13,7 @@ export const registerDiscEndpoints = () => { method: "GET", route: "/", handler: async ({ req, res }) => { - const filter = buildDiscFilter(req.query); - const discs = await Disc.getDiscs(filter); + const discs = await Disc.getDiscs(req.query); res.json(discs); }, schema: { @@ -53,8 +45,7 @@ export const registerDiscEndpoints = () => { route: "/", handler: async ({ req, res }) => { assertRequestIsAuthorized(req.headers.authorization); - await Disc.insertDiscs(req.body); - const discs = await Disc.getDiscs(); + const discs = await Disc.insertDiscs(req.body); res.json(discs); }, schema: { diff --git a/src/helpers/util.ts b/src/helpers/util.ts index bd62542..872d0e6 100644 --- a/src/helpers/util.ts +++ b/src/helpers/util.ts @@ -1,6 +1,6 @@ import { Schema } from "mongoose"; -import type { TBase, TDiscFilter, TDiscQuery } from "@types"; +import type { TBase, TDisc, TDiscQuery } from "@types"; export const BaseSchema = new Schema({ created_at: { @@ -13,23 +13,27 @@ export const BaseSchema = new Schema({ default: () => Date.now() } }); + export const newId = () => crypto.randomUUID(); export const projection = { _id: 0, created_at: 0, updated_at: 0, __v: 0 }; export const regexify = (field: string) => ({ $regex: field, $options: "i" }); -export const buildDiscFilter = (query: TDiscQuery) => { +export const filterDiscsByQuery = (discs: TDisc[], query: TDiscQuery) => { const { id, name, brand, category, stability, speed, glide, turn, fade } = query; - const filter: TDiscFilter = {}; - if (id) filter.id = id; - if (name) filter.name_slug = regexify(name); - if (brand) filter.brand_slug = regexify(brand); - if (category) filter.category_slug = regexify(category); - if (stability) filter.stability_slug = stability.toLowerCase(); - if (speed) filter.speed = speed; - if (glide) filter.glide = glide; - if (turn) filter.turn = turn; - if (fade) filter.fade = fade; - return filter; + return discs.filter(disc => { + return ( + (!id || disc.id === id) && + (!name || + (disc.name_slug.includes(name) && + (!brand || disc.brand_slug.includes(brand)) && + (!category || disc.category_slug.includes(category)) && + (!stability || disc.stability_slug === stability.toLowerCase()) && + (!speed || disc.speed === speed) && + (!glide || disc.glide === glide) && + (!turn || disc.turn === turn) && + (!fade || disc.fade === fade))) + ); + }); }; diff --git a/src/index.ts b/src/index.ts index 9671ef6..9b13e6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import { Env } from "@constants"; import { initEndpoints } from "@endpoints"; import { Config } from "@helpers"; import { errorHandler, initMiddleware, notFound } from "@middleware"; +import { Disc } from "@models"; import { connectToDatabase, log } from "@services"; const { IS_PROD, PORT } = Config; @@ -12,6 +13,8 @@ const app = express(); await connectToDatabase(); +await Disc.writeDiscsToFile(); + initMiddleware(app); initEndpoints(app); diff --git a/src/middleware/index.ts b/src/middleware/index.ts index 4a646fe..0ba2ea5 100644 --- a/src/middleware/index.ts +++ b/src/middleware/index.ts @@ -40,7 +40,5 @@ export const initMiddleware = (app: Express) => { }) ); - app.set("json spaces", 2); - app.disable("x-powered-by"); }; diff --git a/src/models/disc.ts b/src/models/disc.ts index 27dd77a..bf402e8 100644 --- a/src/models/disc.ts +++ b/src/models/disc.ts @@ -1,6 +1,8 @@ +import fs from "fs"; import { model, Schema } from "mongoose"; -import { BaseSchema, CustomError, projection } from "@helpers"; +import { DISCS_FILENAME } from "@constants"; +import { BaseSchema, CustomError, filterDiscsByQuery, projection } from "@helpers"; import type { TDisc, TDiscQuery } from "@types"; const DiscModel = model( @@ -86,15 +88,37 @@ const DiscModel = model( }).add(BaseSchema) ); -const getDiscs = (filter?: TDiscQuery) => DiscModel.find(filter ?? {}, projection).sort({ name: 1 }); +const getDiscsFromFile = async () => { + if (!fs.existsSync(DISCS_FILENAME)) return []; + const text = await Bun.file(DISCS_FILENAME).text(); + return JSON.parse(text) as TDisc[]; +}; + +const writeDiscsToFile = async () => { + const discs = await DiscModel.find({}, projection); + await Bun.write(DISCS_FILENAME, JSON.stringify(discs)); + return discs; +}; + +const getDiscs = async (query?: TDiscQuery) => { + const discs = await getDiscsFromFile(); + if (query) return filterDiscsByQuery(discs, query); + return discs; +}; const assertDiscExists = async (id: string) => { - const disc = await DiscModel.findOne({ id }, projection); - if (!disc) throw new CustomError(`Disc with id '${id}' not found`, 404); - return disc; + const discs = await getDiscs(); + if (discs?.length > 0) { + const disc = discs.find(d => d.id === id); + if (disc) return disc; + } + throw new CustomError(`Disc with id '${id}' not found`, 404); }; -const insertDiscs = (discs: TDisc[]) => DiscModel.insertMany(discs); +const insertDiscs = async (discs: TDisc[]) => { + await DiscModel.insertMany(discs); + return writeDiscsToFile(); +}; const deleteAllDiscs = () => DiscModel.deleteMany(); @@ -104,6 +128,8 @@ const deleteDiscById = async (id: string) => { }; export const Disc = { + getDiscsFromFile, + writeDiscsToFile, getDiscs, assertDiscExists, insertDiscs, diff --git a/src/types/constants/index.ts b/src/types/constants/index.ts index 3abfa50..0563608 100644 --- a/src/types/constants/index.ts +++ b/src/types/constants/index.ts @@ -15,3 +15,5 @@ export enum RequestMethod { TRACE = "trace", ALL = "all" } + +export const DISCS_FILENAME = "discs.json";