selfhostable, read-only reddit client

add search page, support spoiler/nsfw imgs

+9
src/assets/missing.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> 2 + <rect x="2" y="2" width="196" height="196" style="fill:#999;stroke:#000000"/> 3 + <text x="50%" y="50%" font-size="36" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#000000"> 4 + missing 5 + </text> 6 + <style/> 7 + </svg> 8 + 9 +
+7
src/assets/nsfw.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> 2 + <rect x="2" y="2" width="196" height="196" style="fill:#ff123a;stroke:#000000"/> 3 + <text x="50%" y="50%" font-size="36" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#000000"> 4 + nsfw 5 + </text> 6 + <style/> 7 + </svg>
+8
src/assets/spoiler.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> 2 + <rect x="2" y="2" width="196" height="196" style="fill:#999;stroke:#000000"/> 3 + <text x="50%" y="50%" font-size="36" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#000000"> 4 + spoiler 5 + </text> 6 + <style/> 7 + </svg> 8 +
+3 -1
src/auth.js
··· 27 27 28 28 function authenticateAdmin(req, res, next) { 29 29 if (!req.cookies || !req.cookies.auth_token) { 30 - return res.redirect("/login"); 30 + return res.redirect( 31 + `/login?redirect=${encodeURIComponent(req.originalUrl)}`, 32 + ); 31 33 } 32 34 33 35 const token = req.cookies.auth_token;
+1
src/index.js
··· 16 16 app.use(express.json()); 17 17 app.use(express.urlencoded({ extended: true })); 18 18 app.use(express.static(path.join(__dirname, "public"))); 19 + app.use(express.static(path.join(__dirname, "assets"))); 19 20 app.use(cookieParser()); 20 21 app.use( 21 22 rateLimit({
+1 -1
src/mixins/post.pug
··· 26 26 img(src=item.url onclick=`toggleDetails('${p.id}')`) 27 27 else if isPostImage(p) 28 28 - var url = postThumbnail(p) 29 - img(src=url onclick=`toggleDetails('${p.id}')`) 29 + img(src=url onclick=`toggleDetails('${p.id}')`) 30 30 else if isPostVideo(p) 31 31 - var url = p.secure_media.reddit_video.scrubber_media_url 32 32 video(src=url data-dashjs-player width='100px' height='100px' onclick=`toggleDetails('${p.id}')`)
+9 -3
src/mixins/postUtils.pug
··· 4 4 } 5 5 - 6 6 function isPostImage(p) { 7 - return (p.post_hint == "image" && p.thumbnail && p.thumbnail != "self" && p.thumbnail != "default"); 7 + const imgRe = /\.(png|jpg|jpeg|gif|webp|bmp|tiff|svg)$/i; 8 + return (p.post_hint == "image" && p.thumbnail && p.thumbnail != "self" && p.thumbnail != "default") 9 + || imgRe.test(p.url); 8 10 } 9 11 - 10 12 function postThumbnail(p) { 11 - if (p.thumbnail == "image") { 12 - return p.url 13 + if (p.thumbnail == "image" || p.thumbnail == "") { 14 + return p.url; 15 + } else if (p.over_18) { 16 + return "/nsfw.svg"; 17 + } else if (p.thumbnail == "spoiler") { 18 + return "/spoiler.svg"; 13 19 } else { 14 20 return p.thumbnail; 15 21 }
+5 -5
src/public/styles.css
··· 158 158 .info-item, .header-item, .footer-item { 159 159 margin-right: 14px; 160 160 } 161 - 162 - .media-preview img, 163 - .media-preview video { 164 - object-fit: cover; 165 - } 166 161 167 162 .media-preview img, 168 163 .media-preview video { 164 + object-fit: cover; 169 165 width: 4rem; 170 166 height: 4rem; 171 167 } ··· 565 561 flex-direction: column; 566 562 gap: 20px; 567 563 } 564 + 565 + .about { 566 + padding-bottom: 20px; 567 + }
+8 -1
src/routes/index.js
··· 131 131 items.length === 0 132 132 ? "no results found" 133 133 : `showing ${items.length} results`; 134 - res.render("sub-search", { items, subs, after, message, user: req.user }); 134 + res.render("sub-search", { 135 + items, 136 + subs, 137 + after, 138 + message, 139 + user: req.user, 140 + original_query: req.query.q, 141 + }); 135 142 } 136 143 }); 137 144
+2 -1
src/views/comments.pug
··· 43 43 a(href=`/media/${item.url}`) 44 44 img(src=item.url loading="lazy") 45 45 else if isPostImage(post) 46 - img(src=post.url).post-media 46 + a(href=`/media/${post.url}`) 47 + img(src=post.url).post-media 47 48 else if isPostVideo(post) 48 49 - var url = post.secure_media.reddit_video.dash_url 49 50 video(controls data-dashjs-player src=`${url}`).post-media
+2 -2
src/views/index.pug
··· 25 25 else 26 26 button(onclick=`toggleSub('${subreddit}')` id=`thinger_${subreddit}`) subscribe 27 27 if about && !isMulti 28 - p #{about.public_description} 28 + div.about #{about.public_description} 29 29 else 30 - p 30 + div.about 31 31 | consider donating to&nbsp; 32 32 a(href="https://donate.stripe.com/dR62bTaZH1295Da4gg") oppiliappan 33 33 |, author of readit
+2 -1
src/views/sub-search.pug
··· 11 11 div.hero 12 12 h1 search subreddits 13 13 form(action="/search" method="get").search-bar 14 - input(type="text" name="q" placeholder="search subreddits (add +nsfw to include over-18 results)" required).search-input 14 + - var prefill = original_query ?? ""; 15 + input(type="text" name="q" placeholder="search subreddits (add +nsfw to include over-18 results)" value=prefill required).search-input 15 16 button(type="submit").search-button go 16 17 if message 17 18 div.search-message