diff --git a/http-server/http-server.js b/http-server/http-server.js index 3f161fd..e7f7520 100644 --- a/http-server/http-server.js +++ b/http-server/http-server.js @@ -51,7 +51,10 @@ export function createServer(options) { return { listen(port = 3000, cb) { server.listen(port, cb); - return { router }; + }, + router: { + prefix: router.prefix, + define: router.define } }; } diff --git a/http-server/route.js b/http-server/route.js index d13c43b..5c959f2 100644 --- a/http-server/route.js +++ b/http-server/route.js @@ -1,18 +1,23 @@ import { serializeError } from "serialize-error"; import formidable from "formidable"; import { BadRequestError, HttpError, MethodNotAllowedError, NotFoundError } from "./errors.js"; +import * as utils from "./utils/index.js"; /** * @returns {import("./types/http-server.js").HttpRouter} */ export function createRouter() { + /** @type {import("./types/http-server.js").HttpRoute[]} */ + const routes = []; + + let prefixNameGroup = ""; + return { - routes: [], async handle(ctx) { const { req, res } = ctx; try { - for (const route of this.routes) { + for (const route of routes) { const patternResult = route.pattern.exec(req.url.pathname); if (!patternResult) continue; @@ -69,9 +74,18 @@ export function createRouter() { ); } }, + prefix(name, cb) { + const prevPrefix = prefixNameGroup; + prefixNameGroup += name; + cb(this); + prefixNameGroup = prevPrefix; + }, define(method, pathname, handler, options = {}) { + if (!utils.matchRoutePathname(pathname)) throw new Error(`Incorrect pathname "${pathname}"`); + if (prefixNameGroup) pathname = utils.formatRoutePathname(prefixNameGroup + pathname); /** @type {import("./types/http-server.js").HttpRoute} */ - this.routes.push({ method, pattern: new URLPattern({ pathname }), handler, options }); + routes.push({ method, pattern: new URLPattern({ pathname }), handler, options }); + console.log("defined:", pathname); } }; } diff --git a/http-server/types/http-server.d.ts b/http-server/types/http-server.d.ts index 0b01ae3..5f08b02 100644 --- a/http-server/types/http-server.d.ts +++ b/http-server/types/http-server.d.ts @@ -3,7 +3,8 @@ import type IncomingForm from "formidable/Formidable.js"; import formidable from "formidable"; export interface HttpServer { - listen: (port: number, cb: () => void) => { router: HttpRouter }; + listen: (port: number, cb: () => void) => void; + router: Pick; } export interface HttpServerOptions { @@ -26,9 +27,10 @@ export interface HttpRoute { handler: HttpRouteHandler; options: HttpRouteOptions; } + export interface HttpRouter { - routes: Array; handle: (ctx: HttpContext) => Promise; + prefix: (name: string, cb: (router: HttpRouter) => void) => void; define: ( method: HttpMethod, pathname: string, diff --git a/http-server/utils/index.js b/http-server/utils/index.js new file mode 100644 index 0000000..1bd2c71 --- /dev/null +++ b/http-server/utils/index.js @@ -0,0 +1 @@ +export * from "./string.js"; diff --git a/http-server/utils/string.js b/http-server/utils/string.js new file mode 100644 index 0000000..1d94f14 --- /dev/null +++ b/http-server/utils/string.js @@ -0,0 +1,14 @@ +/** + * @param {string} pathname + */ +export function matchRoutePathname(pathname) { + return pathname.match(/(^\/[^/].*$|^\/$)/g); +} + +/** + * @param {string} pathname + */ +export function formatRoutePathname(pathname) { + if (pathname[0] !== "/") pathname = "/" + pathname; + return pathname.replaceAll(/\/{2,}/g, "/"); +} diff --git a/index.js b/index.js index 4900c0e..729e233 100644 --- a/index.js +++ b/index.js @@ -17,22 +17,27 @@ const cors = { headers: "Content-Type,Accept" }; -const server = createServer({ cors }).listen(SERVER_PORT, async () => { +const server = createServer({ cors }); + +server.listen(SERVER_PORT, async () => { console.log(`Service ${packageJson.name} is running at port ${SERVER_PORT}.`); await fsp.mkdir(path.join(process.cwd(), "/uploads"), { recursive: true }); await db.connect().then(() => console.log("Database connected!")); }); -server.router.define("GET", "/", ({ res }) => res.sendText("Сервис инструментов таблицы АСУ.")); -server.router.define("GET", "/api/reports_status_parse", reportsHandlers.statusParseHandler); -server.router.define("POST", "/api/reports_excel_file_parse", reportsHandlers.parseExcelHandler, { - incomingForm: { - maxFields: 1, - maxFiles: 1, - filename: (_, ext) => `gpp-reports${ext}`, - keepExtensions: true, - uploadDir: path.join(process.cwd(), "./uploads"), - filter: ({ mimetype }) => !!mimetype && mimetype.includes("application/vnd.ms-excel") - } +server.router.prefix("/api", (router) => { + router.define("GET", "/reports_status_parse", reportsHandlers.statusParseHandler); + router.define("POST", "/reports_excel_file_parse", reportsHandlers.parseExcelHandler, { + incomingForm: { + maxFields: 1, + maxFiles: 1, + filename: (_, ext) => `gpp-reports${ext}`, + keepExtensions: true, + uploadDir: path.join(process.cwd(), "./uploads"), + filter: ({ mimetype }) => !!mimetype && mimetype.includes("application/vnd.ms-excel") + } + }); + router.define("POST", "/reports_sync", reportsHandlers.syncParsedHandler); }); -server.router.define("POST", "/api/reports_sync", reportsHandlers.syncParsedHandler); + +server.router.define("GET", "/", ({ res }) => res.sendText("Сервис инструментов таблицы АСУ."));