lil/app/routes.js

1import svgCaptcha from "svg-captcha"; 2import { Router } from "express"; 3 4import db from "./db.js"; 5import config from "./config.js"; 6import { ifLoggedInGoHome, userSessionRequired } from "./middlewares.js"; 7 8import { pageHtml } from "./pages.js"; 9import { makeId, makeToken } from "./utils.js"; 10 11const router = Router(); 12 13// all pages 14router.get("/", ifLoggedInGoHome, (req, res) => { 15 return res.send( 16 pageHtml.home({ 17 query: req.query, 18 }) 19 ); 20}); 21 22router.get("/new", userSessionRequired, (req, res) => { 23 return res.send( 24 pageHtml.newUrl({ 25 query: req.query, 26 user: res.locals.user, 27 }) 28 ); 29}); 30 31router.get("/me", userSessionRequired, (req, res) => { 32 return res.send( 33 pageHtml.me({ 34 query: req.query, 35 user: res.locals.user, 36 }) 37 ); 38}); 39 40router.get("/list", userSessionRequired, (req, res) => { 41 let limit = 20; 42 let offset = 0; 43 44 const page = req.query.p || 1; // 1 45 offset = page * limit - limit; 46 47 const count = db.get(`SELECT count(id) as count FROM urls WHERE user=@user`, { 48 user: res.locals.user.id, 49 }); 50 51 const results = db.query( 52 `SELECT 53 id, destination, short 54 FROM 55 urls WHERE user=@user 56 ORDER BY created_at DESC 57 LIMIT ${limit} 58 OFFSET ${offset}`, 59 { 60 user: res.locals.user.id, 61 } 62 ); 63 const totalPages = Math.ceil(count.count / limit); 64 let nextPageLink = ""; 65 let prevPageLink = ""; 66 if (page < totalPages) { 67 nextPageLink = `/list?p=${Number(page) + 1}`; 68 } 69 if (page > 1) { 70 prevPageLink = `/list?p=${Number(page) - 1}`; 71 } 72 73 return res.send( 74 pageHtml.listUrls({ 75 query: req.query, 76 user: res.locals.user, 77 results, 78 totalPages, 79 offset, 80 nextPageLink, 81 prevPageLink, 82 page, 83 }) 84 ); 85}); 86 87router.get("/logout", (req, res) => { 88 req.session.destroy(() => { 89 return res.redirect("/"); 90 }); 91}); 92 93router.get("/captcha.svg", (req, res) => { 94 const captcha = svgCaptcha.create(); 95 req.session.captchaText = captcha.text; 96 res.set("Content-Type", "image/svg+xml"); 97 return res.send(captcha.data); 98}); 99 100// all submissions 101router.post("/login", (req, res, next) => { 102 const { body } = req; 103 const backUrl = req.header("Referer") || "/"; 104 105 if (!body.username || !body.captcha) { 106 return res.redirect(backUrl + "?message=Invalid+payload"); 107 } 108 109 if (!req.session.captchaText || req.session.captchaText !== body.captcha) { 110 return res.redirect(backUrl + "?message=Invalid+captcha"); 111 } 112 113 let user = { 114 id: makeId(), 115 username: body.username, 116 api_token: makeToken(), 117 status: "active", 118 }; 119 const userResults = db.query( 120 `SELECT id, username, api_token, created_at, status FROM users where username=@username LIMIT 1`, 121 { 122 username: body.username, 123 } 124 ); 125 126 if (userResults.length == 0) { 127 // create user 128 db.run( 129 `INSERT INTO users (id, username, api_token, status) VALUES (@id, @username, @api_token, @status);`, 130 { ...user } 131 ); 132 } 133 134 if (userResults[0]) { 135 user = { 136 ...userResults[0], 137 }; 138 } 139 140 if (user.status !== "active") { 141 return res.redirect(backUrl + "?message=User+cannot+login"); 142 } 143 144 req.session.user = user; 145 return res.redirect("/list"); 146}); 147 148// where all urls goto shorten :) 149router.post("/", userSessionRequired, (req, res, next) => { 150 const { body } = req; 151 const backUrl = req.header("Referer") || "/new"; 152 153 if (!body || !body.full_url) { 154 if (res.locals.not_browser) { 155 return res.status(400).send("Invalid payload"); 156 } 157 return res.redirect(backUrl + "?message=Invalid+payload"); 158 } 159 160 if (body.short) { 161 const shortResults = db.query(`SELECT id FROM urls WHERE short=@short`, { 162 short: body.short, 163 }); 164 165 if (shortResults.length) { 166 if (res.locals.not_browser) { 167 return res.status(400).send("Short code already in use"); 168 } 169 return res.redirect(backUrl + "?message=Short+code+already+in+use"); 170 } 171 } 172 173 const url = { 174 id: makeId(), 175 destination: body.full_url, 176 short: body.short || makeId(), 177 user: res.locals.user.id, 178 }; 179 180 // create url 181 db.run( 182 `INSERT INTO urls (id, destination, short, user) VALUES (@id, @destination, @short, @user);`, 183 { ...url } 184 ); 185 186 if (res.locals.not_browser) { 187 return res.send(`${config.hostname}/${url.short}`); 188 } 189 190 return res.redirect("/list"); 191}); 192 193// last route to get full url for a short 194router.get("/:short", (req, res, next) => { 195 const { short } = req.params; 196 197 const data = db.get(`SELECT destination FROM urls WHERE short=@short`, { 198 short, 199 }); 200 201 if (data) { 202 return res.status(301).redirect(data.destination); 203 } 204 205 next(); 206}); 207 208export default router;