« get me outta code hell

Do cleaner, slightly less regex-y URL handling - scratchrlol - Simple HTML-based Scratch client
summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2018-11-14 22:29:06 -0400
committerFlorrie <towerofnix@gmail.com>2018-11-14 22:40:29 -0400
commitc5840770145bf53201b08c6017faa737f5cb16cf (patch)
treee3a4edb6495525d6de51da315216dc6e9ac0bda2
parent9c77f7caeb3098152b98e5e3b8a958c2e92fe920 (diff)
Do cleaner, slightly less regex-y URL handling
-rwxr-xr-xindex.js203
1 files changed, 101 insertions, 102 deletions
diff --git a/index.js b/index.js
index 1ad3e1f..2e5202a 100755
--- a/index.js
+++ b/index.js
@@ -75,6 +75,7 @@ const getUser = function(username) {
 }
 
 const last = arr => arr[arr.length - 1]
+const compareArr = (a1, a2) => a1.length === a2.length && a1.every((x, i) => typeof a2[i] === 'function' ? a2[i](x) : a2[i] === x)
 const filterHTML = text => text.replace(/</g, '&lt;').replace(/>/g, '&gt;')
 const pluralize = (word, n) => n === 1 ? word : word + 's'
 
@@ -225,6 +226,8 @@ const handleRequest = async (request, response) => {
   const queryData = qs.parse(query)
   const cookie = parseCookies(request)
 
+  const urlParts = pathname.split('/').filter(Boolean)
+
   // Not used by nearly all paths, but available to all for convenience.
   let { page: pageNumber = 1 } = queryData
   pageNumber = parseInt(pageNumber)
@@ -234,69 +237,68 @@ const handleRequest = async (request, response) => {
     pageNumber = Math.max(1, pageNumber)
   }
 
+  if (compareArr(urlParts, ['login'])) {
+    if (request.method === 'GET') {
+      await page(request, response, fixWS`
+        <h1>Login</h1>
+        <form action="/login" method="post">
+          <p><label>Username: <input name="username"></label></p>
+          <p><label>Token: <input type="password" name="token"></label></p>
+          <p><label>Session ID: <input type="password" name="sessionid"></label></p>
+          <p><label>CSRF Token: <input type="password" name="csrftoken"></label></p>
+          <p><button>Login</button></p>
+        </form>
+      `)
 
-  if (request.url === '/login') {
-    await page(request, response, fixWS`
-      <h1>Login</h1>
-      <form action="/post-login" method="post">
-        <p><label>Username: <input name="username"></label></p>
-        <p><label>Token: <input type="password" name="token"></label></p>
-        <p><label>Session ID: <input type="password" name="sessionid"></label></p>
-        <p><label>CSRF Token: <input type="password" name="csrftoken"></label></p>
-        <p><button>Login</button></p>
-      </form>
-    `)
-
-    return
-  }
-
-  if (request.url === '/post-login') {
-    const { username, token, sessionid, csrftoken } = await getData(request)
+      return
+    } else if (request.method === 'POST') {
+      const { username, token, sessionid, csrftoken } = await getData(request)
 
-    response.setHeader('Set-Cookie', [
-      `username=${username}; HttpOnly`,
-      `token=${token}; HttpOnly`,
-      `sessionid=${sessionid}; HttpOnly`,
-      `csrftoken=${csrftoken}; HttpOnly`
-    ])
+      response.setHeader('Set-Cookie', [
+        `username=${username}; HttpOnly`,
+        `token=${token}; HttpOnly`,
+        `sessionid=${sessionid}; HttpOnly`,
+        `csrftoken=${csrftoken}; HttpOnly`
+      ])
 
-    request.headers.cookie = response.getHeader('Set-Cookie').join(';') // Cheating! To make the header show right.
+      request.headers.cookie = response.getHeader('Set-Cookie').join(';') // Cheating! To make the header show right.
 
-    await page(request, response, fixWS`
-      <p>Okay, you're logged in.</p>
-    `)
+      await page(request, response, fixWS`
+        <p>Okay, you're logged in.</p>
+      `)
 
-    return
+      return
+    }
   }
 
-  if (request.url === '/logout') {
-    await page(request, response, fixWS`
-      <h1>Log Out</h1>
-      <form action="/post-logout" method="post">
-        <p>Are you sure you want to log out?</p>
-        <p><button>Log out</button></p>
-      </form>
-    `)
-
-    return
-  }
+  if (compareArr(urlParts, ['logout'])) {
+    if (request.method === 'GET') {
+      await page(request, response, fixWS`
+        <h1>Log Out</h1>
+        <form action="/logout" method="post">
+          <p>Are you sure you want to log out?</p>
+          <p><button>Log out</button></p>
+        </form>
+      `)
 
-  if (request.url === '/post-logout') {
-    const { username, token, sessionid, csrftoken } = await getData(request)
+      return
+    } else if (request.method === 'POST') {
+      const { username, token, sessionid, csrftoken } = await getData(request)
 
-    const clearCookie = name => `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
+      const clearCookie = name => `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
 
-    request.headers.cookie = '' // Cheating, kind of, but to make the header print right :)
-    response.setHeader('Set-Cookie', ['username', 'token', 'sessionid', 'csrftoken'].map(clearCookie))
+      request.headers.cookie = '' // Cheating, kind of, but to make the header print right :)
+      response.setHeader('Set-Cookie', ['username', 'token', 'sessionid', 'csrftoken'].map(clearCookie))
 
-    await page(request, response, fixWS`
-      <p>Okay, you've been logged out.</p>
-    `)
+      await page(request, response, fixWS`
+        <p>Okay, you've been logged out.</p>
+      `)
 
-    return
+      return
+    }
   }
 
-  if (pathname === '/notifications') {
+  if (compareArr(urlParts, ['notifications'])) {
     if (!cookie.username) {
       await page(request, response, fixWS`
         <p>Sorry, you can't view your notifications until you've <a href="/login">logged in</a>.</p>
@@ -318,7 +320,7 @@ const handleRequest = async (request, response) => {
         <ul>
           ${nText(notifications.slice(0, currentPageNewCount))}
         </ul>
-        <form action="/mark-notifications-as-read" method="post">
+        <form action="/notifications/mark-as-read" method="post">
           <p><button>Mark as read</button></p>
         </form>
       `
@@ -342,21 +344,22 @@ const handleRequest = async (request, response) => {
     return
   }
 
-  if (pathname === '/mark-notifications-as-read') {
-    await markNotificationsAsRead(cookie.sessionid, cookie.csrftoken)
+  if (compareArr(urlParts, ['notifications', 'mark-as-read'])) {
+    if (request.method === 'POST') {
+      await markNotificationsAsRead(cookie.sessionid, cookie.csrftoken)
 
-    response.writeHead(302, {
-      'Location': '/notifications'
-    })
+      response.writeHead(302, {
+        'Location': '/notifications'
+      })
 
-    response.end()
+      response.end()
 
-    return
+      return
+    }
   }
 
-  const projectMatch = pathname.match(/^\/projects\/([0-9]*)\/?/)
-  if (projectMatch) {
-    const projectID = projectMatch[1]
+  if (compareArr(urlParts, ['projects', id => /^[0-9]*$/.test(id)])) {
+    const projectID = urlParts[1]
 
     const project = await getProject(projectID, cookie.token)
     if (project.code === 'NotFound') {
@@ -398,13 +401,44 @@ const handleRequest = async (request, response) => {
     return
   }
 
-  const userMatch = pathname.match(/^\/users\/([a-zA-Z0-9\-_]*)(\/[^/]*$)?/)
-  if (userMatch) buildUserPage: {
-    const username = userMatch[1]
+  if (compareArr(urlParts.slice(0, 2), ['users', name => /^[a-zA-Z0-9\-_]*$/.test(name)])) {
+    const username = urlParts[1]
     const user = await getUser(username)
 
-    const path = userMatch[2]
-    if (path === '/projects') {
+    if (user.code === 'NotFound') {
+      await page(request, response, fixWS`
+        404. Sorry, that user doesn't exist.
+      `)
+      return
+    }
+
+    if (urlParts.length === 2) {
+      const projects = await fetch(`https://api.scratch.mit.edu/users/${username}/projects?limit=5`).then(res => res.json())
+      const projectsText = projects.map(templates.projectThumbnail).join('\n')
+
+      await page(request, response, fixWS`
+        <h1><img src="${user.profile.images['60x60']}" height="60px" alt="This user's profile picture"> ${user.username}</h1>
+        ${user.profile.bio ? fixWS`
+          <h2>About Me</h2>
+          ${templates.longField(user.profile.bio)}
+        ` : fixWS`
+          <p>(No "about me".)</p>
+        `}
+        ${user.profile.status ? fixWS`
+          <h2>What I'm Working On</h2>
+          ${templates.longField(user.profile.status)}
+        ` : fixWS`
+          <p>(No "what I'm working on".)</p>
+        `}
+        <h2>Projects</h2>
+        <ul class="thumb-list">
+          ${projectsText}
+        </ul>
+        <p><a href="/users/${username}/projects">See all!</a></p>
+      `)
+
+      return
+    } else if (compareArr(urlParts.slice(2), ['projects'])) {
       const offset = (pageNumber - 1) * limit
       const projects = await fetch(`https://api.scratch.mit.edu/users/${username}/projects?limit=${limit}&offset=${offset}`).then(res => res.json())
       const projectsText = projects.map(templates.projectThumbnail).join('\n')
@@ -420,45 +454,10 @@ const handleRequest = async (request, response) => {
       `)
 
       return
-    } else if (path && path !== '/') {
-      break buildUserPage // 404
     }
-
-    if (username.code === 'NotFound') {
-      await page(request, response, fixWS`
-        Sorry, that user doesn't exist.
-      `)
-      return
-    }
-
-    const projects = await fetch(`https://api.scratch.mit.edu/users/${username}/projects?limit=5`).then(res => res.json())
-    const projectsText = projects.map(templates.projectThumbnail).join('\n')
-
-    await page(request, response, fixWS`
-      <h1><img src="${user.profile.images['60x60']}" height="60px" alt="This user's profile picture"> ${user.username}</h1>
-      ${user.profile.bio ? fixWS`
-        <h2>About Me</h2>
-        ${templates.longField(user.profile.bio)}
-      ` : fixWS`
-        <p>(No "about me".)</p>
-      `}
-      ${user.profile.status ? fixWS`
-        <h2>What I'm Working On</h2>
-        ${templates.longField(user.profile.status)}
-      ` : fixWS`
-        <p>(No "what I'm working on".)</p>
-      `}
-      <h2>Projects</h2>
-      <ul class="thumb-list">
-        ${projectsText}
-      </ul>
-      <p><a href="/users/${username}/projects">See all!</a></p>
-    `)
-
-    return
   }
 
-  if (pathname === '/style.css') {
+  if (compareArr(urlParts, ['style.css'])) {
     response.writeHead(200, {
       'Content-Type': 'text/css'
     })
@@ -468,7 +467,7 @@ const handleRequest = async (request, response) => {
     return
   }
 
-  if (pathname === '/') {
+  if (urlParts.length === 0) {
     await page(request, response, fixWS`
       You are at the homepage. Sorry, I haven't implmented any content for it yet.
     `)