Pārlūkot izejas kodu

Route visualization

Khysnik 1 mēnesi atpakaļ
vecāks
revīzija
2d82e8242c
3 mainītis faili ar 150 papildinājumiem un 0 dzēšanām
  1. 133 0
      src/adapters/lsroute.js
  2. 10 0
      src/routes/auth.js
  3. 7 0
      src/server.js

+ 133 - 0
src/adapters/lsroute.js

@@ -0,0 +1,133 @@
+const HTTP_METHODS = [
+    'get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'all'
+];
+
+export function createRouteTracker(app) {
+    const routes = [];
+
+    /* ---- Hook app.METHOD() ---- */
+    for (const method of HTTP_METHODS) {
+        const original = app[method].bind(app);
+
+        app[method] = (path, ...handlers) => {
+            const meta = extractMeta(handlers);
+
+            routes.push({
+                path,
+                methods: method === 'all' ? ['ALL'] : [method.toUpperCase()],
+                input: meta.input,
+                output: meta.output,
+                cookies: meta.cookies
+            });
+
+            return original(path, ...handlers);
+        };
+    }
+
+    /* ---- Hook app.use() ---- */
+    const originalUse = app.use.bind(app);
+
+    app.use = (path, ...handlers) => {
+        if (typeof path === 'string') {
+            for (const h of handlers) {
+                if (h?.stack) {
+                    trackRouter(h, path);
+                }
+            }
+        }
+        return originalUse(path, ...handlers);
+    };
+
+    function trackRouter(router, prefix) {
+        for (const layer of router.stack) {
+            if (!layer.route) continue;
+
+            const methods = Object.keys(layer.route.methods)
+                .filter(Boolean)
+                .map(m => m.toUpperCase());
+
+            const meta = extractMeta(layer.route.stack.map(l => l.handle));
+
+            routes.push({
+                path: normalize(prefix + layer.route.path),
+                methods,
+                input: meta.input,
+                output: meta.output,
+                cookies: meta.cookies
+            });
+        }
+    }
+
+    return {
+        list: () => routes,
+        visualize
+    };
+}
+
+/* ---- Extract metadata from handler comments ---- */
+function extractMeta(handlers) {
+    let input = null;
+    let output = null;
+    let cookies = null;
+
+    for (const fn of handlers) {
+        if (typeof fn !== 'function') continue;
+
+        const src = fn.toString();
+
+        const inMatch = src.match(/\/\/\s*@input\s+\{([^}]+)\}/);
+        const outMatch = src.match(/\/\/\s*@output\s+\{([^}]+)\}/);
+        const cookieMatch = src.match(/\/\/\s*@cookies\s+\{([^}]+)\}/);
+
+        if (inMatch) input = inMatch[1].split(',').map(v => v.trim());
+        if (outMatch) output = outMatch[1].split(',').map(v => v.trim());
+        if (cookieMatch) cookies = cookieMatch[1].split(',').map(v => v.trim());
+    }
+
+    return { input, output, cookies };
+}
+
+/* ---- Normalize paths ---- */
+function normalize(path) {
+    return path.replace(/\/+/g, '/');
+}
+
+/* ---- Developer-friendly table ---- */
+function visualize() {
+    const rows = this.list().map(r => {
+        const formatArray = (arr) => {
+            if (!arr || arr.length === 0) return '-';
+            return arr.map(item => {
+                if (typeof item === 'string' && item.startsWith('in:')) return `\x1b[32m${item}\x1b[0m`;   // green
+                if (typeof item === 'string' && item.startsWith('out:')) return `\x1b[34m${item}\x1b[0m`;  // blue
+                return item;
+            }).join(', ');
+        };
+
+        return {
+            METHOD: r.methods.join(','),
+            PATH: r.path,
+            INPUT: formatArray(r.input),
+            OUTPUT: formatArray(r.output),
+            COOKIES: formatArray(r.cookies)
+        };
+    });
+
+    // compute column widths
+    const headers = ['METHOD','PATH','INPUT','OUTPUT','COOKIES'];
+    const widths = headers.map(h => Math.max(
+        h.length,
+        ...rows.map(r => r[h].length)
+    ));
+
+    // print header
+    let line = headers.map((h,i)=>h.padEnd(widths[i])).join(' | ');
+    console.log(line);
+    console.log(widths.map(w=>'─'.repeat(w)).join('-|-'));
+
+    // print rows
+    for (const r of rows) {
+        console.log(headers.map((h,i)=>r[h].padEnd(widths[i])).join(' | '));
+    }
+}
+

+ 10 - 0
src/routes/auth.js

@@ -16,6 +16,8 @@ export default function authRoutes(UserDB) {
     const auth = express.Router();
 
     auth.get('/user', async (req, res) => {
+        //@cookies { in:token }
+        //@output { username, email, _id }
         const token = req.cookies.token;
 
         if (!token) {
@@ -52,6 +54,8 @@ export default function authRoutes(UserDB) {
     });
 
     auth.get('/login/:data', async (req, res) => {
+        //@input { /:data }
+        //@output { redirect:http://localhost:5173/ }
         if (!req.params.data) {
             return res.status(401).send('Invalid token');
         }
@@ -96,6 +100,9 @@ export default function authRoutes(UserDB) {
     });
 
     auth.post('/login', async (req, res) => {
+        //@input { username, password }
+        //@output { success, user }
+        //@cookies { out:token }
         const {username, password} = req.body;
         if (!username || !password) {
             return res.status(400).json({error: 'Missing credentials'});
@@ -118,6 +125,9 @@ export default function authRoutes(UserDB) {
     });
 
     auth.post('/register', async (req, res) => {
+        //@input { email, password }
+        //@output { success, username }
+
         const {email, password} = req.body;
 
         const username = email.split("@")[0]

+ 7 - 0
src/server.js

@@ -6,11 +6,14 @@ import cookieParser from 'cookie-parser';
 
 import DB from './adapters/database.js';
 import authRoutes from './routes/auth.js';
+import {createRouteTracker} from './adapters/lsroute.js'
 
 const UserDB = new DB('users');
 
 const app = express();
 
+const tracker = createRouteTracker(app);
+
 app.use(
     cors({
         origin: ['http://localhost:5173', 'http://localhost:5174'],
@@ -24,8 +27,12 @@ app.use(express.urlencoded({ extended: true }));
 
 app.use('/api/auth', authRoutes(UserDB));
 
+tracker.visualize()
+
 app.listen(3000, () => console.log('Server on :3000'));
 
+
+
 // POST /api/auth/register - {email, password}
 // POST /api/auth/login - {username, password}
 // GET /api/auth/user