import { promises as fs } from "fs"; import db from '../../sql.mjs'; import lib from '../../lib.mjs'; import search from '../../routeinc/search.mjs'; const allowedMimes = [ "audio", "image", "video", "%" ]; export default router => { router.group(/^\/api\/v2/, group => { group.get(/$/, (req, res) => { res.end("api lol"); }); group.get(/\/random(\/user\/.+|\/image|\/video|\/audio)?$/, async (req, res) => { const user = req.url.split[3] === "user" ? req.url.split[4] : "%"; const mime = (allowedMimes.filter(n => req.url.split[3]?.startsWith(n))[0] ? req.url.split[3] : "") + "%"; const rows = await db` select * from "items" where mime ilike ${mime} and username ilike ${user} and active = 'true' order by random() limit 1 `; return res.json({ success: rows.length > 0, items: rows.length > 0 ? rows[0] : [] }); }); group.get(/\/items\/get/, async (req, res) => { let eps = 150; const opt = { older: req.url.qs.older ?? null, newer: req.url.qs.newer ?? null, mode: +req.url.qs.mode ?? 0 // 0 sfw, 1 nsfw, 2 untagged, 3 all }; const newest = (await db`select max(id) as id from "items"`)[0].id; const oldest = (await db`select min(id) as id from "items"`)[0].id; const modequery = lib.getMode(opt.mode); const rows = (await db` select "items".id, "items".mime, coalesce("tags_assign".tag_id, 0) as tag_id from "items" left join "tags_assign" on "tags_assign".item_id = "items".id and ("tags_assign".tag_id = 1 or "tags_assign".tag_id = 2) where ${db.unsafe(modequery)} and active = 'true' ${ opt.older ? db`and id <= ${opt.older}` : opt.newer ? db`and id >= ${opt.newer}` : db`` } order by id ${ opt.newer ? db`asc` : db`desc` } limit ${eps} `).sort((a, b) => b.id - a.id); return res.json({ atEnd: rows[0].id === newest, atStart: rows[rows.length - 1].id === oldest, success: true, items: rows }, 200); }); group.get(/\/item\/[0-9]+$/, async (req, res) => { const id = +req.url.split[3]; const item = await db` select * from "items" where id = ${+id} and active = 'true' limit 1 `; const next = await db` select id from "items" where id > ${+id} and active = 'true' order by id limit 1 `; const prev = await db` select id from "items" where id < ${+id} and active = 'true' order by id desc limit 1 `; if(item.length === 0) { return res.json({ success: false, msg: 'no items found' }); } const rows = { ...item[0], ...{ next: next[0]?.id ?? null, prev: prev[0]?.id ?? null } }; return res.json({ success: true, rows }); }); group.get(/\/user\/.*(\/\d+)?$/, async (req, res) => { const user = req.url.split[3]; const eps = +req.url.split[4] || 50; const rows = db` select id, mime, size, src, stamp, userchannel, username, usernetwork from "items" where username = ${user} and active = 'true' order by stamp desc limit ${+eps} `; return res.json({ success: rows.length > 0, items: rows.length > 0 ? rows : [] }); }); // tags lol group.put(/\/admin\/tags\/(?.*)/, lib.auth, async (req, res) => { if(!req.params.tagname || !req.post.newtag) { return res.json({ success: false, msg: 'missing tagname or newtag', debug: { params: req.params.tagname, post: req.post } }, 400); // bad request } const tagname = decodeURIComponent(req.params.tagname); const newtag = req.post.newtag; if(['sfw', 'nsfw'].includes(tagname) || ['sfw', 'nsfw'].includes(newtag)) { return res.json({ msg: 'f0ck you' }, 405); // method not allowed } const tmptag = (await db` select * from "tags" where tag = ${tagname} limit 1 `)[0]; if(!tmptag) { return res.json({ success: false, msg: 'no tag found' }, 404); // not found } const q = (await db` update "tags" set ${ db({ tag: newtag }, 'tag') } where tag = ${tagname} returning * `)?.[0]; return res.json(q, tagname === newtag ? 200 : 201); // created (modified) }); group.get(/\/admin\/tags\/suggest$/, lib.auth, async (req, res) => { const reply = { success: false, suggestions: {} }; const searchString = req.url.qs.q; if(searchString?.length <= 1) { reply.error = 'too short lol'; return res.json(reply); } try { const q = await db` select tag, count(tags_assign.tag_id) as tagged from "tags" left join "tags_assign" on "tags_assign".tag_id = "tags".id where normalized like '%' || slugify(${searchString}) || '%' group by "tags".id order by tagged desc limit 15 `; reply.success = true; reply.suggestions = search(q, searchString); } catch(err) { reply.error = err.msg; } return res.json(reply); }); group.post(/\/admin\/deletepost$/, lib.auth, async (req, res) => { if(!req.post.postid) { return res.json({ success: false, msg: 'no postid' }); } const id = +req.post.postid; if(id <= 1) { return res.json({ success: false }); } const f0ck = await db` select dest, mime from "items" where id = ${id} and active = 'true' limit 1 `; if(f0ck.length === 0) { return res.json({ success: false, msg: `f0ck ${id}: f0ck not found` }); } await db`update "items" set active = 'false' where id = ${id}`; await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{}); await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{}); await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{}); await fs.unlink(`./public/t/${id}.webp`).catch(_=>{}); if(f0ck[0].mime.startsWith('audio')) { await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{}); await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{}); } res.json({ success: true }); }); group.post(/\/admin\/togglefav$/, lib.auth, async (req, res) => { const postid = +req.post.postid; let favs = await db` select user_id from "favorites" where item_id = ${+postid} `; if(Object.values(favs).filter(u => u.user_id === req.session.id)[0]) { // del fav await db` delete from "favorites" where user_id = ${+req.session.id} and item_id = ${+postid} `; } else { // add fav await db` insert into "favorites" ${ db({ item_id: +postid, user_id: +req.session.id }, 'item_id', 'user_id') } `; } favs = await db` select "user".user, "user_options".avatar from "favorites" left join "user" on "user".id = "favorites".user_id left join "user_options" on "user_options".user_id = "favorites".user_id where "favorites".item_id = ${+postid} `; return res.json({ success: true, itemid: postid, favs }); }); }); return router; };