Files
calebburke.dev/api/src/router.ts
T
2026-07-17 04:39:18 -07:00

127 lines
3.8 KiB
TypeScript

import path from "path"
import fs from "fs"
import {
Router,
type Request,
type Response,
type ErrorRequestHandler,
type NextFunction,
} from "express"
import { UnauthorizedError } from "express-jwt"
import { template } from "lodash"
import { APPLICATION_NAME, GIT_COMMIT_HASH, NODE_ENV, RELEASE_TAG } from "@/config"
import { logger } from "@/utils/logger"
import { jwtMiddleware, authorizationMiddleware } from "@/middlewares"
import {
BlogPostsController,
CurrentUserController,
FlashcardDecksController,
FlashcardsController,
Public,
Users,
UsersController,
} from "@/controllers"
export const router = Router()
// non-api (no authentication is required) routes
router.route("/_status").get((_req: Request, res: Response) => {
return res.json({
RELEASE_TAG,
GIT_COMMIT_HASH,
})
})
// external (public) routes - no authentication required
router.route("/api/public/blog-posts").get(Public.BlogPostsController.index)
router.route("/api/public/blog-posts/:blogPostIdOrSlug").get(Public.BlogPostsController.show)
// api routes
router.use("/api", jwtMiddleware, authorizationMiddleware)
router.route("/api/current-user").get(CurrentUserController.show)
router.route("/api/users").get(UsersController.index).post(UsersController.create)
router
.route("/api/users/:userId")
.get(UsersController.show)
.patch(UsersController.update)
.delete(UsersController.destroy)
router.route("/api/users/:userId/preferences/:key").patch(Users.PreferencesController.update)
router
.route("/api/flashcard-decks")
.get(FlashcardDecksController.index)
.post(FlashcardDecksController.create)
router
.route("/api/flashcard-decks/:flashcardDeckId")
.get(FlashcardDecksController.show)
.patch(FlashcardDecksController.update)
.delete(FlashcardDecksController.destroy)
router.route("/api/flashcards").get(FlashcardsController.index).post(FlashcardsController.create)
router
.route("/api/flashcards/:flashcardId")
.get(FlashcardsController.show)
.patch(FlashcardsController.update)
.delete(FlashcardsController.destroy)
router.route("/api/blog-posts").get(BlogPostsController.index).post(BlogPostsController.create)
router
.route("/api/blog-posts/:blogPostIdOrSlug")
.get(BlogPostsController.show)
.patch(BlogPostsController.update)
.delete(BlogPostsController.destroy)
// if no other routes match, return a 404
router.use("/api", (req: Request, res: Response) => {
return res.status(404).json({ message: "Not Found", url: req.path })
})
// Special error handler for all api errors
// See https://expressjs.com/en/guide/error-handling.html#writing-error-handlers
router.use("/api", (err: ErrorRequestHandler, _req: Request, res: Response, next: NextFunction) => {
if (res.headersSent) {
return next(err)
}
if (err instanceof UnauthorizedError) {
logger.error(err)
return res.status(err.status).json({ message: err.inner.message })
}
/* if (err instanceof DatabaseError) {
logger.error(err)
return res.status(422).json({ message: "Invalid query against database." })
}
*/
logger.error(err)
return res.status(500).json({ message: "Internal Server Error" })
})
// if no other non-api routes match, send the pretty 404 page
if (NODE_ENV == "development") {
router.use("/", (_req: Request, res: Response) => {
const templatePath = path.resolve(__dirname, "web/404.html")
try {
const templateString = fs.readFileSync(templatePath, "utf8")
const compiledTemplate = template(templateString)
const result = compiledTemplate({
applicationName: APPLICATION_NAME,
releaseTag: RELEASE_TAG,
gitCommitHash: GIT_COMMIT_HASH,
})
return res.status(404).send(result)
} catch (error) {
logger.error(error)
return res.status(500).send(`Error building 404 page: ${error}`)
}
})
}
export default router