diff options
-rwxr-xr-x | index.js | 203 |
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, '<').replace(/>/g, '>') 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. `) |