Compare commits
227 Commits
Author | SHA1 | Date | |
---|---|---|---|
5cdb86d710 | |||
02a29bb056 | |||
0ea54f111d | |||
294c59d30b | |||
b9a42f9e8b | |||
ce1b1770d2 | |||
de66d95207 | |||
6376366a0c | |||
cd1f99af48 | |||
dcc6eac07a | |||
da5bfd5734 | |||
fe714f0556 | |||
0c96c258ab | |||
4260a7429a | |||
61f9422f3f | |||
b876e793b1 | |||
a8b1d156e8 | |||
941b99c71b | |||
efe37d3a30 | |||
f437ec8e80 | |||
809a4e7f60 | |||
f3dccb9747 | |||
7b8d486083 | |||
eb16d3b120 | |||
4ef66e173f | |||
675608c3fd | |||
52b83dee3a | |||
e18270a1ce | |||
a3b66567c9 | |||
c46fcd0d9b | |||
e818e902f4 | |||
daf8faeb8f | |||
e822f118c6 | |||
b964a71c7a | |||
23a62d3803 | |||
ed264661a2 | |||
3e2aa4dca4 | |||
891129ec2e | |||
2863065939 | |||
cd88cfe3b1 | |||
161cb76709 | |||
8991218271 | |||
0b81e584a1 | |||
0aae17f871 | |||
3ca5b5d598 | |||
c4dfd6b5c6 | |||
e24f3f9e35 | |||
ba7ea955c5 | |||
1d5225c9fe | |||
356b8eebf1 | |||
f5e2bd4a02 | |||
9da30ac7d2 | |||
69ca852379 | |||
b29537d6d8 | |||
cfddf8a274 | |||
7a8362bca6 | |||
817c9c2966 | |||
7078378c05 | |||
76f1a3ddbb | |||
a905b8817e | |||
ab2ea14891 | |||
d33168ca92 | |||
e3cda9faf4 | |||
7d8baa1da3 | |||
dacd72ffb3 | |||
5e735f072d | |||
32712cf70c | |||
d02b304728 | |||
ad6027ab20 | |||
4416867ffe | |||
1a033723c8 | |||
a504865a6e | |||
b6f28fdb58 | |||
7028d92d5e | |||
c1f06de7c5 | |||
ce7f854dd6 | |||
3b29f5f9d5 | |||
f5225a71c5 | |||
7098c335bc | |||
38c4abae3c | |||
6852ac40d1 | |||
4e2cfb8637 | |||
65818c2f40 | |||
e20e9899f9 | |||
befc8e8e75 | |||
058fe94fd1 | |||
c79cca18cf | |||
2ff1842d09 | |||
486580b21c | |||
36fc3326d3 | |||
5d91ce7d2b | |||
0c6e806525 | |||
77d041bb19 | |||
7844bc8b63 | |||
705e46f838 | |||
c2e9922ae9 | |||
d0336d8cfb | |||
95902023e9 | |||
2b2fe45be8 | |||
6f71170da9 | |||
00db7c6113 | |||
78457fd644 | |||
050c9e66c8 | |||
9f1052320e | |||
d134e4d6be | |||
ed2a3a4bc5 | |||
d4ac155c68 | |||
74fc3a9134 | |||
afb38f9af5 | |||
05518fa495 | |||
604e54f238 | |||
84c58479eb | |||
d69f9b8427 | |||
6c6f739110 | |||
f674fe2a40 | |||
f32d712dff | |||
64943719da | |||
4b78b70ede | |||
177e184621 | |||
1f42acb726 | |||
6e561071ee | |||
564f1bf530 | |||
d9e8b63004 | |||
c7329292ca | |||
8e9de0252e | |||
1a4afd22ab | |||
c13ca165c0 | |||
cba5fc0410 | |||
7a42e6ffce | |||
5e39c67147 | |||
226c368945 | |||
8dc448fe13 | |||
b1c9b7de72 | |||
89a93cc50c | |||
a0c5f94921 | |||
95a40bd1c2 | |||
56dcabfa94 | |||
5d79243fe1 | |||
267a1427c3 | |||
4ec8edfb37 | |||
8b9677bb76 | |||
68724890fd | |||
0dff028982 | |||
d9ced4a896 | |||
0e793ecde5 | |||
1b350781f2 | |||
84937c88a1 | |||
1d84180603 | |||
a942fc0b94 | |||
abf4ca92b1 | |||
cc73ee2162 | |||
6590f72b88 | |||
0b2f174ee8 | |||
f9e10abb51 | |||
78a2524ce9 | |||
81e7d50a6b | |||
1380e45794 | |||
9ca17e6fd4 | |||
48596ad17a | |||
85212051d0 | |||
046a7460ca | |||
f2757f08af | |||
e2f15ce862 | |||
aa3a4d0217 | |||
3074b0294a | |||
fb48cccdbb | |||
8dd54811ee | |||
460d4a84aa | |||
a9a5be9fb4 | |||
b2c43e18bb | |||
84104b58bc | |||
a753a3f27d | |||
05c59717fd | |||
51cfc34f75 | |||
a49ea84ac3 | |||
5fcad639dc | |||
b12170382a | |||
afba818021 | |||
5eeaa28612 | |||
2301d15abd | |||
fbf03bfbb4 | |||
63bd1104b7 | |||
e47dfb38fb | |||
cea4a13b4c | |||
8d32dfef24 | |||
f39a136d5d | |||
7dc03936ab | |||
223c8893df | |||
8e02892df0 | |||
85d683ca31 | |||
61e02953c7 | |||
9ccf17ac54 | |||
4e6b337d4f | |||
9a55781a64 | |||
b95ce804ab | |||
e0ac02e742 | |||
d2bf6f7afd | |||
d4218ad1e8 | |||
bab2a03382 | |||
4ca44ee8b9 | |||
9157868699 | |||
dc986edfe4 | |||
ac5c6ea0b5 | |||
8ec51789dd | |||
8b6f68f2e9 | |||
44df4deea3 | |||
b52b1bc19d | |||
7899f3bbd1 | |||
955f844e16 | |||
3f0b502b00 | |||
5da67f2a6a | |||
bbe31594e5 | |||
e16197f8e4 | |||
2186fc85d4 | |||
ca516ded7c | |||
c50048d8e6 | |||
49849a4a12 | |||
5e0ffda6c5 | |||
fdc8b836b3 | |||
d37a55aa1e | |||
cb0a5f7ee8 | |||
0f6164a825 | |||
41e4bb8fae | |||
534fb1d92a | |||
6b6e44eed7 | |||
c806410be6 | |||
9239666291 |
43
.gitea/workflows/build.yaml
Normal file
@ -0,0 +1,43 @@
|
||||
name: fetch npm modules
|
||||
run-name: fetch npm modules
|
||||
on: [push]
|
||||
jobs:
|
||||
f0ck the f0cker:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15.2
|
||||
env:
|
||||
POSTGRES_USER: f0ck
|
||||
POSTGRES_PASSWORD: f0ck
|
||||
POSTGRES_DB: f0ck
|
||||
POSTGRES_PORT: 5432
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 19
|
||||
- run: npm ci
|
||||
|
||||
- name: Install PostgreSQL client
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install --yes postgresql-client
|
||||
|
||||
- name: import f0ck database
|
||||
run: psql -h postgres -d f0ck -U f0ck < f0ck.sql
|
||||
env:
|
||||
PGPASSWORD: f0ck
|
||||
|
||||
- name: create directories
|
||||
run: mkdir -p tmp public/ca deleted/{ca,b,t}
|
||||
|
||||
- name: copy config
|
||||
run: cp config_example.json config.json
|
@ -1,7 +1,7 @@
|
||||
# how to install:
|
||||
## dependencies
|
||||
```bash
|
||||
sudo pacman -S nodejs npm ffmpeg yt-dlp ffmpegthumbnailer postgresql python python-pip imagemagick git mime-types
|
||||
sudo pacman -S nodejs npm ffmpeg yt-dlp ffmpegthumbnailer postgresql imagemagick git mime-types
|
||||
```
|
||||
## postgres
|
||||
```bash
|
||||
@ -26,7 +26,5 @@ mkdir -p public/ca deleted/{ca,b,t}
|
||||
cp config_example.json config.json
|
||||
- edit config.json
|
||||
- set up clients, as described here: https://git.lat/keinBot/cuffeo
|
||||
pip install nsfw_detector
|
||||
#if this fails make sure to have enough dedicated ram or swap space, alternatively try with pip install nsfw_detector --no-cache-dir
|
||||
npm start
|
||||
```
|
||||
|
@ -22,20 +22,33 @@
|
||||
"thumbnails": "/t",
|
||||
"coverarts": "/ca"
|
||||
},
|
||||
"themes": [ "f0ck", "p1nk", "orange", "atmos", "amoled", "paper", "term", "iced" ],
|
||||
"eps": 294,
|
||||
"themes": [ "f0ck", "p1nk", "orange", "atmos", "amoled", "paper", "term", "iced", "f0ck95", "f0ck95d" ],
|
||||
"eps": 300,
|
||||
"cache": false,
|
||||
"phrases": [
|
||||
"<img src=\"/s/img/crap/nyancat.gif\" style=\"margin-top: 5px\" />"
|
||||
]
|
||||
},
|
||||
"clients": [
|
||||
|
||||
],
|
||||
"clients": [{
|
||||
"type": "irc",
|
||||
"enabled": true,
|
||||
"network": "n0xy",
|
||||
"host": "irc.n0xy.net",
|
||||
"port": 6697,
|
||||
"ssl": true,
|
||||
"selfSigned": false,
|
||||
"sasl": false,
|
||||
"nickname": "f0ckci",
|
||||
"username": "f0ckci",
|
||||
"realname": "f0ckci",
|
||||
"channels": [
|
||||
"#f0ck-dev"
|
||||
]
|
||||
}],
|
||||
"sql": {
|
||||
"host": "localhost",
|
||||
"user": "f0ck",
|
||||
"password": "",
|
||||
"password": "f0ck",
|
||||
"database": "f0ck",
|
||||
"schema": "public",
|
||||
"multipleStatements": true
|
||||
@ -54,8 +67,10 @@
|
||||
"audio/mpeg": "mpg",
|
||||
"audio/mp3": "mp3",
|
||||
"audio/ogg": "ogg",
|
||||
"audio/opus": "opus",
|
||||
"audio/flac": "flac",
|
||||
"audio/x-flac": "flac",
|
||||
"video/x-m4v": "mp4"
|
||||
"video/x-m4v": "mp4",
|
||||
"video/x-matroska": "mkv"
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
import db from '../src/inc/sql.mjs';
|
||||
import lib from '../src/inc/lib.mjs';
|
||||
|
||||
import readline from 'node:readline/promises';
|
||||
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
|
||||
const newuser = process.argv[2]?.length ? process.argv[2] : await rl.question('username: ');
|
||||
const password = await rl.question('password: ');
|
||||
const level = +(await rl.question('level (0-100): '));
|
||||
|
||||
rl.close();
|
||||
|
||||
if(!newuser.length || !password.length) {
|
||||
console.log('nope lol');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const id = (await db`
|
||||
insert into "user" ${
|
||||
db({
|
||||
login: newuser.toLowerCase(),
|
||||
user: newuser,
|
||||
password: await lib.hash(password),
|
||||
level: level >= 0 && level <= 100 ? level : 0
|
||||
})
|
||||
}
|
||||
returning id
|
||||
`)[0]?.id;
|
||||
|
||||
console.log(`created new user ${newuser} with ID ${id}`);
|
||||
process.exit();
|
110
debug/clean.mjs
@ -1,33 +1,95 @@
|
||||
import sql from "../src/inc/sql.mjs";
|
||||
import { promises as fs } from "fs";
|
||||
import cfg from "../src/inc/config.mjs";
|
||||
import db from "../src/inc/sql.mjs";
|
||||
import fs from "node:fs";
|
||||
import readline from 'node:readline/promises';
|
||||
|
||||
const opts = {
|
||||
b: "public/b",
|
||||
t: "public/t",
|
||||
tmp: "tmp"
|
||||
};
|
||||
const count = {
|
||||
b: 0, t: 0, tmp: 0
|
||||
};
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
|
||||
const rows = await sql('items').select('id', 'dest');
|
||||
const ids = rows.map(r => r.id.toString() + ".webp");
|
||||
const dests = rows.map(r => r.dest);
|
||||
// npm run clean -- --dry-run
|
||||
const dry = !!process.argv.filter(a => a == '--dry-run').length;
|
||||
console.log(`dry run? ${dry}`);
|
||||
|
||||
const dirs = {
|
||||
b: "./public/b",
|
||||
t: "./public/t",
|
||||
tmp: "./tmp"
|
||||
};
|
||||
const files = {
|
||||
b: (await fs.readdir(opts.b)).filter(f => f !== '.empty'),
|
||||
t: await fs.readdir(opts.t)
|
||||
b: (await fs.promises.readdir(dirs.b)) .filter(f => f !== '.empty'),
|
||||
t: (await fs.promises.readdir(dirs.t)) .filter(f => f !== '.empty'),
|
||||
tmp: (await fs.promises.readdir(dirs.tmp)).filter(f => f !== '.empty')
|
||||
};
|
||||
const extensions = [ ...Object.values(cfg.mimes), 'mov' ];
|
||||
const count = {
|
||||
missing: { b: [], t: [] },
|
||||
invalid: { b: [], t: [] },
|
||||
spare: { b: [], t: [] },
|
||||
tmp: files.tmp
|
||||
};
|
||||
const rows = await db`select id, dest from items where active = true`;
|
||||
const f0cks = {
|
||||
b: rows.flatMap(f => f.dest),
|
||||
t: rows.flatMap(f => `${f.id}.webp`)
|
||||
};
|
||||
|
||||
const unused = {
|
||||
b: files.b.filter(f => !dests.includes(f)),
|
||||
t: files.t.filter(f => !ids.includes(f))
|
||||
// missing
|
||||
for(const row of rows) {
|
||||
if(!fs.existsSync(`${dirs.b}/${row.dest}`))
|
||||
count.missing.b.push(row.id);
|
||||
if(!fs.existsSync(`${dirs.t}/${row.id}.webp`))
|
||||
count.missing.t.push(row.id);
|
||||
}
|
||||
|
||||
count.b = (await Promise.all(unused.b.map(f => fs.rm(`${opts.b}/${f}`)))).length;
|
||||
count.t = (await Promise.all(unused.t.map(f => fs.rm(`${opts.t}/${f}`)))).length;
|
||||
// invalid
|
||||
count.invalid.b = files.b.filter(f => !extensions.includes(f.toLowerCase().split('.')[1]));
|
||||
count.invalid.t = files.t.filter(f => !f.endsWith('.webp'));
|
||||
|
||||
// clear tmp
|
||||
const tmp = (await fs.readdir(opts.tmp)).filter(f => f !== '.empty');
|
||||
count.tmp = (await Promise.all(tmp.map(f => fs.rm(`${opts.tmp}/${f}`)))).length;
|
||||
// spare
|
||||
for(const file of files.b)
|
||||
if(!f0cks.b.includes(file))
|
||||
count.spare.b.push(`${dirs.b}/${file}`);
|
||||
|
||||
console.log(count, unused);
|
||||
for(const file of files.t)
|
||||
if(!f0cks.t.includes(file))
|
||||
count.spare.t.push(`${dirs.t}/${file}`);
|
||||
|
||||
// show confusing summary
|
||||
console.log(count);
|
||||
|
||||
// delete spare if --dry-run
|
||||
if(!dry) {
|
||||
let q;
|
||||
if(count.spare.b.length > 0) {
|
||||
q = (await rl.question(`delete ${count.spare.b.length} unnecessary files in ${dirs.b}? [y/N] `)) == 'y';
|
||||
if(q) {
|
||||
await Promise.all(count.spare.b.map(f => fs.promises.unlink(f)));
|
||||
console.log(`deleted ${count.spare.b.length} files`);
|
||||
}
|
||||
else
|
||||
console.log('abort...');
|
||||
}
|
||||
|
||||
if(count.spare.t.length > 0) {
|
||||
q = (await rl.question(`delete ${count.spare.t.length} unnecessary files in ${dirs.t}? [y/N] `)) == 'y';
|
||||
if(q) {
|
||||
await Promise.all(count.spare.t.map(f => fs.promises.unlink(f)));
|
||||
console.log(`deleted ${count.spare.t.length} files`);
|
||||
}
|
||||
else
|
||||
console.log('abort...');
|
||||
}
|
||||
|
||||
if(files.tmp.length > 0) {
|
||||
q = (await rl.question(`delete ${files.tmp.length} files in ${dirs.tmp}? [y/N] `)) == 'y';
|
||||
if(q) {
|
||||
await Promise.all(files.tmp.map(f => fs.promises.unlink(`${dirs.tmp}/${f}`)));
|
||||
console.log(`deleted ${files.tmp.length} files`);
|
||||
}
|
||||
else
|
||||
console.log('abort...');
|
||||
}
|
||||
}
|
||||
|
||||
// close connection
|
||||
await db.end();
|
||||
process.exit();
|
||||
|
@ -19,7 +19,7 @@ let items;
|
||||
if(_itemid > 0)
|
||||
items = await sql('items').where('id', _itemid);
|
||||
else
|
||||
items = await sql('items').orderBy('id', 'asc').where('src', 'like', '%soundcloud%').where('mime', 'like', 'audio/%');
|
||||
items = await sql`select id, dest, mime, src from "items"`;
|
||||
let count = 1;
|
||||
let total = items.length;
|
||||
|
||||
@ -32,7 +32,7 @@ for(let item of items) {
|
||||
if(mime.startsWith('video/') || mime == 'image/gif')
|
||||
await exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`);
|
||||
else if(mime.startsWith('image/') && mime != 'image/gif')
|
||||
await exec(`convert ./public/b/${filename} ./tmp/${itemid}.png`);
|
||||
await exec(`magick ./public/b/${filename} ./tmp/${itemid}.png`);
|
||||
else if(mime.startsWith('audio/')) {
|
||||
if(link.match(/soundcloud/)) {
|
||||
let cover = (await exec(`yt-dlp --get-thumbnail "${link}"`)).stdout.trim();
|
||||
@ -42,8 +42,8 @@ for(let item of items) {
|
||||
await exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`);
|
||||
const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size;
|
||||
if(size >= 0) {
|
||||
await exec(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
|
||||
await exec(`convert ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`);
|
||||
await exec(`magick ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
|
||||
await exec(`magick ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
} catch(err) {
|
||||
//console.log(err);
|
||||
@ -51,21 +51,21 @@ for(let item of items) {
|
||||
}
|
||||
else {
|
||||
await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
|
||||
await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
await exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
|
||||
await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
await exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
}
|
||||
|
||||
await exec(`convert "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`);
|
||||
await exec(`magick "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`);
|
||||
await fs.promises.unlink(`./tmp/${itemid}.png`).catch(err => {});
|
||||
await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(err => {});
|
||||
} catch(err) {
|
||||
//console.log(err);
|
||||
await exec(`convert ./mugge.png ./public/t/${itemid}.webp`);
|
||||
await exec(`magick ./mugge.png ./public/t/${itemid}.webp`);
|
||||
}
|
||||
console.log(`current: ${itemid} (${count} / ${total})`);
|
||||
count++;
|
||||
@ -76,7 +76,7 @@ for(let item of items) {
|
||||
const itemid = item.id;
|
||||
const filename = item.dest;
|
||||
await exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./debug/tmp/${itemid}`);
|
||||
await exec(`convert "./debug/tmp/${itemid}" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.png`);
|
||||
await exec(`magick "./debug/tmp/${itemid}" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.png`);
|
||||
await fs.unlink(`./debug/tmp/${itemid}`);
|
||||
console.log(`current: ${itemid} (${count} / ${total})`);
|
||||
} catch(err) {}
|
||||
|
610
f0ck.sql
@ -1,4 +1,9 @@
|
||||
\connect "f0ck";
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
-- Dumped from database version 16.2
|
||||
-- Dumped by pg_dump version 16.2
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
@ -11,7 +16,33 @@ SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
CREATE EXTENSION unaccent;
|
||||
--
|
||||
-- Name: public; Type: SCHEMA; Schema: -; Owner: f0ck
|
||||
--
|
||||
|
||||
-- *not* creating schema, since initdb creates it
|
||||
|
||||
|
||||
ALTER SCHEMA public OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: unaccent; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS unaccent WITH SCHEMA public;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION unaccent; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION unaccent IS 'text search dictionary that removes accents';
|
||||
|
||||
|
||||
--
|
||||
-- Name: delete_unused_tags(); Type: FUNCTION; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.delete_unused_tags() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@ -21,14 +52,17 @@ begin
|
||||
tags.id not in (select tag_id from tags_assign) and
|
||||
tags.id = OLD.tag_id and
|
||||
tags.tag != 'sfw' and
|
||||
tags.tag != 'nsfw' and
|
||||
tags.tag != 'hentai' and
|
||||
tags.tag != 'audio';
|
||||
tags.tag != 'nsfw';
|
||||
return OLD;
|
||||
end $$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.delete_unused_tags() OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: fill_normalized(); Type: FUNCTION; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.fill_normalized() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@ -37,8 +71,13 @@ begin
|
||||
return NEW;
|
||||
end$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.fill_normalized() OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: slugify(text); Type: FUNCTION; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.slugify(v text) RETURNS text
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@ -47,15 +86,47 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.slugify(v text) OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: unaccent_text(text); Type: FUNCTION; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.unaccent_text(text) RETURNS text
|
||||
LANGUAGE sql IMMUTABLE COST 1
|
||||
AS $_$
|
||||
-- unaccent is STABLE, but the indexes must use IMMUTABLE functions.
|
||||
-- comment this line out when calling pg_dump.
|
||||
SELECT unaccent($1);
|
||||
|
||||
-- Uncomment this line when calling pg_dump.
|
||||
--SELECT ''::text;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.unaccent_text(text) OWNER TO f0ck;
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
||||
--
|
||||
-- Name: favorites; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.favorites (
|
||||
user_id integer NOT NULL,
|
||||
item_id integer NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.favorites OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: items_id_seq; Type: SEQUENCE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.items_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
@ -63,7 +134,12 @@ CREATE SEQUENCE public.items_id_seq
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER TABLE public.items_id_seq OWNER TO f0ck;
|
||||
|
||||
ALTER SEQUENCE public.items_id_seq OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: items; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.items (
|
||||
id integer DEFAULT nextval('public.items_id_seq'::regclass) NOT NULL,
|
||||
@ -73,18 +149,115 @@ CREATE TABLE public.items (
|
||||
size integer NOT NULL,
|
||||
checksum character varying(255) NOT NULL,
|
||||
username character varying(40) NOT NULL,
|
||||
userchannel character varying(100) NOT NULL,
|
||||
userchannel character varying(255) NOT NULL,
|
||||
usernetwork character varying(40) NOT NULL,
|
||||
stamp integer NOT NULL,
|
||||
active boolean NOT NULL,
|
||||
thumb character varying(100)
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.items OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: COLUMN items.src; Type: COMMENT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.items.src IS 'src-Link';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN items.dest; Type: COMMENT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.items.dest IS 'filename';
|
||||
|
||||
|
||||
--
|
||||
-- Name: items_li; Type: VIEW; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE VIEW public.items_li AS
|
||||
SELECT
|
||||
NULL::integer AS id,
|
||||
NULL::character varying(255) AS src,
|
||||
NULL::character varying(40) AS dest,
|
||||
NULL::character varying(100) AS mime,
|
||||
NULL::integer AS size,
|
||||
NULL::character varying(255) AS checksum,
|
||||
NULL::character varying(40) AS username,
|
||||
NULL::character varying(255) AS userchannel,
|
||||
NULL::character varying(40) AS usernetwork,
|
||||
NULL::integer AS stamp;
|
||||
|
||||
|
||||
ALTER VIEW public.items_li OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: tags_assign; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.tags_assign (
|
||||
item_id integer NOT NULL,
|
||||
tag_id integer NOT NULL,
|
||||
user_id integer DEFAULT 10 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.tags_assign OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: tags_nsfp; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.tags_nsfp (
|
||||
id integer NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.tags_nsfp OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: items_sfw; Type: VIEW; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE VIEW public.items_sfw AS
|
||||
SELECT ( SELECT
|
||||
CASE
|
||||
WHEN (tags_assign.tag_id > 0) THEN tags_assign.tag_id
|
||||
ELSE 0
|
||||
END AS "case"
|
||||
FROM public.tags_assign
|
||||
WHERE ((tags_assign.tag_id = ANY (ARRAY[1, 2])) AND (tags_assign.item_id = items.id))) AS sfw,
|
||||
( SELECT
|
||||
CASE
|
||||
WHEN (tags_assign.tag_id > 0) THEN 1
|
||||
ELSE 0
|
||||
END AS "case"
|
||||
FROM public.tags_assign
|
||||
WHERE ((tags_assign.tag_id IN ( SELECT tags_nsfp.id
|
||||
FROM public.tags_nsfp)) AND (tags_assign.item_id = items.id))
|
||||
LIMIT 1) AS nsfp,
|
||||
id,
|
||||
src,
|
||||
dest,
|
||||
mime,
|
||||
size,
|
||||
checksum,
|
||||
username,
|
||||
userchannel,
|
||||
usernetwork,
|
||||
stamp,
|
||||
active
|
||||
FROM public.items;
|
||||
|
||||
|
||||
ALTER VIEW public.items_sfw OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: tags_id_seq; Type: SEQUENCE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.tags_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
@ -92,30 +265,25 @@ CREATE SEQUENCE public.tags_id_seq
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER TABLE public.tags_id_seq OWNER TO f0ck;
|
||||
|
||||
ALTER SEQUENCE public.tags_id_seq OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: tags; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.tags (
|
||||
id integer DEFAULT nextval('public.tags_id_seq'::regclass) NOT NULL,
|
||||
tag character varying(45) NOT NULL,
|
||||
normalized character varying(45) NOT NULL
|
||||
tag character varying(70) NOT NULL,
|
||||
normalized character varying(70) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.tags OWNER TO f0ck;
|
||||
|
||||
CREATE TABLE public.tags_alias (
|
||||
tag_orig_id integer NOT NULL,
|
||||
tag_alias character varying NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE public.tags_alias OWNER TO f0ck;
|
||||
|
||||
CREATE TABLE public.tags_assign (
|
||||
item_id integer NOT NULL,
|
||||
tag_id integer NOT NULL,
|
||||
user_id integer DEFAULT 2 NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE public.tags_assign OWNER TO f0ck;
|
||||
--
|
||||
-- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.user_id_seq
|
||||
START WITH 1
|
||||
@ -124,30 +292,128 @@ CREATE SEQUENCE public.user_id_seq
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER TABLE public.user_id_seq OWNER TO f0ck;
|
||||
|
||||
ALTER SEQUENCE public.user_id_seq OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: user; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public."user" (
|
||||
id integer DEFAULT nextval('public.user_id_seq'::regclass) NOT NULL,
|
||||
login character varying(255) NOT NULL,
|
||||
"user" character varying(255) NOT NULL,
|
||||
password character varying(167) NOT NULL,
|
||||
level integer NOT NULL
|
||||
admin boolean NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public."user" OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: items_tags; Type: VIEW; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE VIEW public.items_tags AS
|
||||
SELECT ( SELECT
|
||||
CASE
|
||||
WHEN (tags_assign.tag_id > 0) THEN tags_assign.tag_id
|
||||
ELSE 0
|
||||
END AS "case"
|
||||
FROM public.tags_assign
|
||||
WHERE ((tags_assign.tag_id = ANY (ARRAY[1, 2])) AND (tags_assign.item_id = items.id))) AS sfw,
|
||||
( SELECT
|
||||
CASE
|
||||
WHEN (tags_assign.tag_id > 0) THEN 1
|
||||
ELSE 0
|
||||
END AS "case"
|
||||
FROM public.tags_assign
|
||||
WHERE ((tags_assign.tag_id IN ( SELECT tags_nsfp.id
|
||||
FROM public.tags_nsfp)) AND (tags_assign.item_id = items.id))
|
||||
LIMIT 1) AS nsfp,
|
||||
( SELECT jsonb_agg(jsonb_build_object('id', tags.id, 'normalized', tags.normalized)) AS jsonb_agg
|
||||
FROM (public.tags_assign
|
||||
LEFT JOIN public.tags ON ((tags.id = tags_assign.tag_id)))
|
||||
WHERE (tags_assign.item_id = items.id)) AS tags,
|
||||
( SELECT jsonb_agg(jsonb_build_object('id', favorites.user_id, 'user', "user"."user")) AS jsonb_agg
|
||||
FROM (public.favorites
|
||||
LEFT JOIN public."user" ON (("user".id = favorites.user_id)))
|
||||
WHERE (favorites.item_id = items.id)) AS favs,
|
||||
id,
|
||||
src,
|
||||
dest,
|
||||
mime,
|
||||
size,
|
||||
checksum,
|
||||
username,
|
||||
userchannel,
|
||||
usernetwork,
|
||||
stamp,
|
||||
active
|
||||
FROM public.items;
|
||||
|
||||
|
||||
ALTER VIEW public.items_tags OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: tags_alias; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.tags_alias (
|
||||
tag_orig_id integer NOT NULL,
|
||||
tag_alias character varying NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.tags_alias OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: user_alias; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.user_alias (
|
||||
userid integer NOT NULL,
|
||||
alias character varying(255) NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY public.user_alias REPLICA IDENTITY FULL;
|
||||
|
||||
|
||||
ALTER TABLE public.user_alias OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: user_options; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.user_options (
|
||||
user_id integer NOT NULL,
|
||||
mode integer NOT NULL,
|
||||
theme character varying(50) NOT NULL,
|
||||
avatar integer DEFAULT 1 NOT NULL
|
||||
avatar integer DEFAULT 56660 NOT NULL,
|
||||
fullscreen smallint DEFAULT '0'::smallint NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.user_options OWNER TO f0ck;
|
||||
|
||||
CREATE SEQUENCE public.user_sessions_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
--
|
||||
-- Name: user_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE public.user_sessions_id_seq OWNER TO f0ck;
|
||||
CREATE SEQUENCE public.user_sessions_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
ALTER SEQUENCE public.user_sessions_id_seq OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: user_sessions; Type: TABLE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TABLE public.user_sessions (
|
||||
id integer DEFAULT nextval('public.user_sessions_id_seq'::regclass) NOT NULL,
|
||||
@ -160,55 +426,255 @@ CREATE TABLE public.user_sessions (
|
||||
kmsi smallint DEFAULT '0'::smallint NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.user_sessions OWNER TO f0ck;
|
||||
|
||||
COPY public.items (id, src, dest, mime, size, checksum, username, userchannel, usernetwork, stamp, active, thumb) FROM stdin;
|
||||
1 b761fa9339.png b761fa9339.png image/png 164 keinPlan Flummi #f0ck n0xy 1471250800 t
|
||||
\.
|
||||
--
|
||||
-- Name: favorites idx_16521_primary; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
COPY public.tags (id, tag, normalized) FROM stdin;
|
||||
1 sfw sfw
|
||||
2 nsfw nsfw
|
||||
3 audio audio
|
||||
4 hentai hentai
|
||||
\.
|
||||
ALTER TABLE ONLY public.favorites
|
||||
ADD CONSTRAINT idx_16521_primary PRIMARY KEY (user_id, item_id);
|
||||
|
||||
COPY public.tags_assign (item_id, tag_id, user_id) FROM stdin;
|
||||
1 1 1
|
||||
\.
|
||||
|
||||
COPY public."user" (id, login, "user", password, level) FROM stdin;
|
||||
1 autotagger autotagger f0ck you 0
|
||||
2 deleted deleted f0ck you 0
|
||||
\.
|
||||
--
|
||||
-- Name: items idx_16526_primary; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.items
|
||||
ADD CONSTRAINT idx_16526_primary PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user idx_16554_primary; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."user"
|
||||
ADD CONSTRAINT idx_16554_primary PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_options idx_16567_user_id; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.user_options
|
||||
ADD CONSTRAINT idx_16567_user_id UNIQUE (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_sessions idx_16572_primary; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.user_sessions
|
||||
ADD CONSTRAINT idx_16572_primary PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: items items_checksum; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.items
|
||||
ADD CONSTRAINT items_checksum UNIQUE (checksum);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_alias tags_alias_tag_alias_tag_orig_id; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_alias
|
||||
ADD CONSTRAINT tags_alias_tag_alias_tag_orig_id UNIQUE (tag_alias, tag_orig_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_alias tags_alias_tag_orig_id; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_alias
|
||||
ADD CONSTRAINT tags_alias_tag_orig_id PRIMARY KEY (tag_orig_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_assign tags_assign_item_id_tag_id_primary; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_assign
|
||||
ADD CONSTRAINT tags_assign_item_id_tag_id_primary PRIMARY KEY (item_id, tag_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_assign tags_assign_item_id_tag_id_unique; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_assign
|
||||
ADD CONSTRAINT tags_assign_item_id_tag_id_unique UNIQUE (item_id, tag_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags tags_id; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags
|
||||
ADD CONSTRAINT tags_id PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags tags_normalized; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags
|
||||
ADD CONSTRAINT tags_normalized UNIQUE (normalized);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags tags_tag; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags
|
||||
ADD CONSTRAINT tags_tag UNIQUE (tag);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_options user_options_user_id; Type: CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.user_options
|
||||
ADD CONSTRAINT user_options_user_id PRIMARY KEY (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: items_li _RETURN; Type: RULE; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE OR REPLACE VIEW public.items_li AS
|
||||
SELECT items.id,
|
||||
items.src,
|
||||
items.dest,
|
||||
items.mime,
|
||||
items.size,
|
||||
items.checksum,
|
||||
items.username,
|
||||
items.userchannel,
|
||||
items.usernetwork,
|
||||
items.stamp
|
||||
FROM ((public.items
|
||||
JOIN public.tags_assign ta1 ON (((ta1.tag_id = 1) AND (ta1.item_id = items.id))))
|
||||
JOIN public.tags_assign ta2 ON (((NOT (ta2.tag_id IN ( SELECT tags_nsfp.id
|
||||
FROM public.tags_nsfp))) AND (ta2.item_id = items.id))))
|
||||
WHERE items.active
|
||||
GROUP BY items.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_assign tags_assign_ad; Type: TRIGGER; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('public.items_id_seq', 2, true);
|
||||
SELECT pg_catalog.setval('public.tags_id_seq', 5, true);
|
||||
SELECT pg_catalog.setval('public.user_id_seq', 3, true);
|
||||
SELECT pg_catalog.setval('public.user_sessions_id_seq', 1, true);
|
||||
ALTER TABLE ONLY public.favorites ADD CONSTRAINT idx_16521_primary PRIMARY KEY (user_id, item_id);
|
||||
ALTER TABLE ONLY public.items ADD CONSTRAINT idx_16526_primary PRIMARY KEY (id);
|
||||
ALTER TABLE ONLY public."user" ADD CONSTRAINT idx_16554_primary PRIMARY KEY (id);
|
||||
ALTER TABLE ONLY public.user_options ADD CONSTRAINT idx_16567_user_id UNIQUE (user_id);
|
||||
ALTER TABLE ONLY public.user_sessions ADD CONSTRAINT idx_16572_primary PRIMARY KEY (id);
|
||||
ALTER TABLE ONLY public.items ADD CONSTRAINT items_checksum UNIQUE (checksum);
|
||||
ALTER TABLE ONLY public.tags_alias ADD CONSTRAINT tags_alias_tag_alias_tag_orig_id UNIQUE (tag_alias, tag_orig_id);
|
||||
ALTER TABLE ONLY public.tags_alias ADD CONSTRAINT tags_alias_tag_orig_id PRIMARY KEY (tag_orig_id);
|
||||
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_item_id_tag_id_primary PRIMARY KEY (item_id, tag_id);
|
||||
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_item_id_tag_id_unique UNIQUE (item_id, tag_id);
|
||||
ALTER TABLE ONLY public.tags ADD CONSTRAINT tags_id PRIMARY KEY (id);
|
||||
ALTER TABLE ONLY public.tags ADD CONSTRAINT tags_normalized UNIQUE (normalized);
|
||||
ALTER TABLE ONLY public.tags ADD CONSTRAINT tags_tag UNIQUE (tag);
|
||||
ALTER TABLE ONLY public.user_options ADD CONSTRAINT user_options_user_id PRIMARY KEY (user_id);
|
||||
CREATE TRIGGER tags_assign_ad AFTER DELETE ON public.tags_assign FOR EACH ROW EXECUTE FUNCTION public.delete_unused_tags();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags tags_bi; Type: TRIGGER; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TRIGGER tags_bi BEFORE INSERT ON public.tags FOR EACH ROW EXECUTE FUNCTION public.fill_normalized();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags tags_bu; Type: TRIGGER; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
CREATE TRIGGER tags_bu BEFORE UPDATE ON public.tags FOR EACH ROW EXECUTE FUNCTION public.fill_normalized();
|
||||
ALTER TABLE ONLY public.favorites ADD CONSTRAINT favorites_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.items(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.favorites ADD CONSTRAINT favorites_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.tags_alias ADD CONSTRAINT tags_alias_tag_orig_id_fkey FOREIGN KEY (tag_orig_id) REFERENCES public.tags(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.items(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE SET DEFAULT;
|
||||
ALTER TABLE ONLY public.user_options ADD CONSTRAINT user_options_avatar_fkey FOREIGN KEY (avatar) REFERENCES public.items(id) ON DELETE SET DEFAULT;
|
||||
ALTER TABLE ONLY public.user_options ADD CONSTRAINT user_options_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.user_sessions ADD CONSTRAINT user_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: favorites favorites_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.favorites
|
||||
ADD CONSTRAINT favorites_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.items(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: favorites favorites_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.favorites
|
||||
ADD CONSTRAINT favorites_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_alias tags_alias_tag_orig_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_alias
|
||||
ADD CONSTRAINT tags_alias_tag_orig_id_fkey FOREIGN KEY (tag_orig_id) REFERENCES public.tags(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_assign tags_assign_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_assign
|
||||
ADD CONSTRAINT tags_assign_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.items(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_assign tags_assign_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_assign
|
||||
ADD CONSTRAINT tags_assign_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: tags_assign tags_assign_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.tags_assign
|
||||
ADD CONSTRAINT tags_assign_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE SET DEFAULT;
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_options user_options_avatar_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.user_options
|
||||
ADD CONSTRAINT user_options_avatar_fkey FOREIGN KEY (avatar) REFERENCES public.items(id) ON DELETE SET DEFAULT;
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_options user_options_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.user_options
|
||||
ADD CONSTRAINT user_options_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_sessions user_sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: f0ck
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.user_sessions
|
||||
ADD CONSTRAINT user_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: alltables; Type: PUBLICATION; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE PUBLICATION alltables FOR ALL TABLES WITH (publish = 'insert, update, delete, truncate');
|
||||
|
||||
|
||||
ALTER PUBLICATION alltables OWNER TO f0ck;
|
||||
|
||||
--
|
||||
-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
REVOKE USAGE ON SCHEMA public FROM PUBLIC;
|
||||
GRANT ALL ON SCHEMA public TO PUBLIC;
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
|
BIN
nsfw_model.h5
32
package-lock.json
generated
@ -1,24 +1,24 @@
|
||||
{
|
||||
"name": "f0ckv2",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "f0ckv2",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cuffeo": "^1.1.0",
|
||||
"cuffeo": "^1.2.2",
|
||||
"flumm-fetch": "^1.0.1",
|
||||
"flummpress": "^2.0.5",
|
||||
"postgres": "^3.0.1"
|
||||
"postgres": "^3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/cuffeo": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.1.0.tgz",
|
||||
"integrity": "sha512-7lmx2dvREqCYwy8oUzk3Q0EkyLZkKQTYTBLEjNqKVbinzdEL45Oimi54lmBDFPlrrvTLzQkFvzKmiJ7Zcegi2w==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.2.2.tgz",
|
||||
"integrity": "sha512-Pd2AL/Zp5RCAbaSbT7rLUOyxSEVCFovihaIaje7uYzpQMzIwPbFapQ/mn2d2iz+Tbkjs9LF+FD5JzAvANh9Wzw==",
|
||||
"dependencies": {
|
||||
"flumm-fetch": "^1.0.1"
|
||||
}
|
||||
@ -34,9 +34,9 @@
|
||||
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
|
||||
},
|
||||
"node_modules/postgres": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.1.0.tgz",
|
||||
"integrity": "sha512-yQbJtA7z6SsQuFiF01rmHlkG2gU5IEv9D/Pn2fu8Tz6x3/suNWZs4fIPd59rkL+dfVfhDqYQ7DU3YnUqzEDB5Q==",
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
|
||||
"integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg==",
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/porsager"
|
||||
@ -45,9 +45,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"cuffeo": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.1.0.tgz",
|
||||
"integrity": "sha512-7lmx2dvREqCYwy8oUzk3Q0EkyLZkKQTYTBLEjNqKVbinzdEL45Oimi54lmBDFPlrrvTLzQkFvzKmiJ7Zcegi2w==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.2.2.tgz",
|
||||
"integrity": "sha512-Pd2AL/Zp5RCAbaSbT7rLUOyxSEVCFovihaIaje7uYzpQMzIwPbFapQ/mn2d2iz+Tbkjs9LF+FD5JzAvANh9Wzw==",
|
||||
"requires": {
|
||||
"flumm-fetch": "^1.0.1"
|
||||
}
|
||||
@ -63,9 +63,9 @@
|
||||
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
|
||||
},
|
||||
"postgres": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.1.0.tgz",
|
||||
"integrity": "sha512-yQbJtA7z6SsQuFiF01rmHlkG2gU5IEv9D/Pn2fu8Tz6x3/suNWZs4fIPd59rkL+dfVfhDqYQ7DU3YnUqzEDB5Q=="
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
|
||||
"integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
package.json
@ -3,21 +3,21 @@
|
||||
"version": "2.2.1",
|
||||
"description": "f0ck, kennste?",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node --experimental-json-modules src/index.mjs",
|
||||
"trigger": "node --experimental-json-modules debug/trigger.mjs",
|
||||
"autotagger": "node --experimental-json-modules debug/autotagger.mjs",
|
||||
"thumbnailer": "node --experimental-json-modules debug/thumbnailer.mjs",
|
||||
"test": "node --experimental-json-modules debug/test.mjs",
|
||||
"clean": "node --experimental-json-modules debug/clean.mjs",
|
||||
"adduser": "node --experimental-json-modules debug/adduser.mjs"
|
||||
"start": "node --trace-uncaught src/index.mjs",
|
||||
"trigger": "node debug/trigger.mjs",
|
||||
"autotagger": "node debug/autotagger.mjs",
|
||||
"thumbnailer": "node debug/thumbnailer.mjs",
|
||||
"test": "node debug/test.mjs",
|
||||
"clean": "node debug/clean.mjs"
|
||||
},
|
||||
"author": "Flummi",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cuffeo": "^1.1.0",
|
||||
"cuffeo": "^1.2.2",
|
||||
"flumm-fetch": "^1.0.1",
|
||||
"flummpress": "^2.0.5",
|
||||
"postgres": "^3.0.1"
|
||||
"postgres": "^3.3.4"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
html[theme='f0ck'] {
|
||||
--accent: #9f0;
|
||||
--bg: #171717;
|
||||
--bg: #000000;
|
||||
--black: #000;
|
||||
--white: #fff;
|
||||
--gray: #262626;
|
||||
@ -24,7 +24,7 @@ html[theme='f0ck'] {
|
||||
--dropdown-bg: #232323;
|
||||
--dropdown-item-hover: #0d0d0d;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #2b2b2b;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -42,6 +42,10 @@ html[theme='f0ck'] {
|
||||
--loading-indicator-color: #9f0;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #9f0;
|
||||
--bg-gradient: linear-gradient(0deg, rgba(0, 0, 0, 0.94) 0%, rgb(6, 6, 6) 10%, rgb(43, 43, 43) 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="f0ck"] .admin-search button {
|
||||
@ -68,10 +72,10 @@ html[theme='p1nk'] {
|
||||
--black: #000;
|
||||
--white: #fff;
|
||||
--gray: #262626;
|
||||
--nav-bg: #2b2b2b;
|
||||
--nav-bg: #201f1f;
|
||||
--nav-brand-border: inset 1px #242424;
|
||||
--nav-brand-bg: #171717;
|
||||
--navigation-links-bg: #2b2b2b;
|
||||
--navigation-links-bg: #201f1f;
|
||||
--navigation-links-background-linear-gradient: rgba(0, 0, 0, .12), rgba(0, 0, 0, 0);
|
||||
--navigation-links-border-color: rgba(0, 0, 0, .8) rgba(0, 0, 0, .65) rgba(0, 0, 0, .5);
|
||||
--navigation-links-box-shadow: rgba(255, 255, 255, .05);
|
||||
@ -81,8 +85,8 @@ html[theme='p1nk'] {
|
||||
--dropdown-bg: #232323;
|
||||
--dropdown-item-hover: #0d0d0d;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--pagination-background: #2b2b2b;
|
||||
--font: monospace;
|
||||
--pagination-background: #201f1f;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
;
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -100,6 +104,10 @@ html[theme='p1nk'] {
|
||||
--loading-indicator-color: #ff00d0;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #ff00d0;
|
||||
--bg-gradient: linear-gradient(0deg, rgba(0, 0, 0, 0.94) 0%, rgb(6, 6, 6) 10%, rgb(43, 43, 43) 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="p1nk"] .pagination>a,
|
||||
@ -135,7 +143,7 @@ html[theme='orange'] {
|
||||
--dropdown-bg: #232323;
|
||||
--dropdown-item-hover: #0d0d0d;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #2b2b2b;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -153,6 +161,10 @@ html[theme='orange'] {
|
||||
--loading-indicator-color: #ff6f00;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #ff6f00;
|
||||
--bg-gradient: linear-gradient(0deg, rgba(0, 0, 0, 0.94) 0%, rgb(6, 6, 6) 10%, rgb(43, 43, 43) 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="orange"] .pagination>a,
|
||||
@ -190,7 +202,7 @@ html[theme='amoled'] {
|
||||
--dropdown-bg: #232323;
|
||||
--dropdown-item-hover: #0d0d0d;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #000;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0px rgba(255, 255, 255, 0), inset 0 0px rgba(248, 248, 248, 0), 0 0px 0px rgba(255, 255, 255, 0);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgb(92, 92, 92), inset 0 1px rgb(92, 92, 92), inset 0 -1px rgb(92, 92, 92), 0 1px 1px rgba(92, 92, 92, 0);
|
||||
@ -208,6 +220,9 @@ html[theme='amoled'] {
|
||||
--loading-indicator-color: #fff;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #fff;
|
||||
--bg-gradient: linear-gradient(0deg, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 10%, rgb(0, 0, 0) 100%);
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="amoled"] .metadata {
|
||||
@ -245,6 +260,10 @@ html[theme="amoled"] table.table a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
html[theme="amoled"] .badge.badge-dark {
|
||||
box-shadow: 1px 1px 1px var(--gray);
|
||||
}
|
||||
|
||||
html[theme="paper"] {
|
||||
--accent: #000;
|
||||
--bg: #fff;
|
||||
@ -265,7 +284,7 @@ html[theme="paper"] {
|
||||
--dropdown-bg: #fff;
|
||||
--dropdown-item-hover: #3939354a;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #fff;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -283,6 +302,7 @@ html[theme="paper"] {
|
||||
--loading-indicator-color: #000;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #fff;
|
||||
}
|
||||
|
||||
html[theme="paper"] .err {
|
||||
@ -402,7 +422,7 @@ html[theme="paper"] .v0ck_player_button svg:hover {
|
||||
|
||||
html[theme="paper"] .badge-dark,
|
||||
#themeselector {
|
||||
border: 1px solid var(--white);
|
||||
box-shadow: 1px 1px 0px 0px var(--black);
|
||||
}
|
||||
|
||||
html[theme="paper"] span#favs {
|
||||
@ -431,6 +451,18 @@ html[theme="paper"] a {
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
html[theme="paper"] .login-form {
|
||||
background: white;
|
||||
}
|
||||
|
||||
html[theme="paper"] .login-form input {
|
||||
color: black;
|
||||
}
|
||||
|
||||
html[theme="paper"] .login-form button[type="submit"] {
|
||||
color: black;
|
||||
}
|
||||
|
||||
html[theme="atmos"] {
|
||||
--accent: #1fb2b0;
|
||||
--bg: #161618;
|
||||
@ -451,7 +483,7 @@ html[theme="atmos"] {
|
||||
--dropdown-bg: #232323;
|
||||
--dropdown-item-hover: #0d0d0d;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: rgb(32, 32, 32);
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -462,12 +494,16 @@ html[theme="atmos"] {
|
||||
--posts-meta-bg: #000000b8;
|
||||
--badge-sfw: #68a728;
|
||||
--badge-nsfw: #a72828;
|
||||
--badge-tag: #090909;
|
||||
--badge-tag: #353535;
|
||||
--scrollbar-color: #2b2b2b;
|
||||
--footbar-color: #1fb2b0;
|
||||
--loading-indicator-color: #1fb2b0;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #1fb2b0;
|
||||
--bg-gradient: linear-gradient(0deg, rgba(0, 0, 0, 0.94) 0%, rgb(6, 6, 6) 10%, rgb(43, 43, 43) 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="atmos"] .pagination>a,
|
||||
@ -504,7 +540,7 @@ html[theme="term"] {
|
||||
--dropdown-bg: #040404;
|
||||
--dropdown-item-hover: #282828;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #040404;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -523,6 +559,10 @@ html[theme="term"] {
|
||||
--loading-indicator-color: #00DF00;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #00DF00;
|
||||
--bg-gradient: linear-gradient(0deg, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 10%, rgb(0, 0, 0) 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="term"] .pagination>a,
|
||||
@ -575,7 +615,7 @@ html[theme="iced"] {
|
||||
--dropdown-bg: #111d37;
|
||||
--dropdown-item-hover: #0a3f53;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #111d37;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -594,6 +634,10 @@ html[theme="iced"] {
|
||||
--loading-indicator-color: #0084ff;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #363636;
|
||||
--maximize_button: #0084ff;
|
||||
--bg-gradient: linear-gradient(0deg, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 10%, #062631 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="iced"] ._204863 {
|
||||
@ -647,7 +691,7 @@ html[theme='f0ck95'] {
|
||||
--dropdown-bg: silver;
|
||||
--dropdown-item-hover: #8a8989;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: silver;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -665,6 +709,10 @@ html[theme='f0ck95'] {
|
||||
--loading-indicator-color: #000;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #808080;
|
||||
--maximize_button: #000;
|
||||
--bg-gradient: linear-gradient(0deg, rgba(0, 128, 128, 0.514), rgba(0, 0, 0, 0) 10%, rgba(0, 128, 128, 0.363) 100%);
|
||||
/* appearance */
|
||||
background: teal;
|
||||
}
|
||||
|
||||
html[theme="f0ck95"] html,
|
||||
@ -915,7 +963,7 @@ html[theme='f0ck95d'] {
|
||||
--dropdown-bg: #333131;
|
||||
--dropdown-item-hover: #444242;
|
||||
--nav-brand-font: 'VCR';
|
||||
--font: 'monospace';
|
||||
--font: monospace;
|
||||
--pagination-background: #0b0a0a;
|
||||
--pagination-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 0px rgba(255, 255, 255, .04), inset 0 0px rgba(0, 0, 0, .15), 0 0px 0px rgba(0, 0, 0, .1);
|
||||
--pagination-anchor-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .04), inset 0 1px rgba(255, 255, 255, .04), inset 0 -1px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .1);
|
||||
@ -933,6 +981,10 @@ html[theme='f0ck95d'] {
|
||||
--loading-indicator-color: #fff;
|
||||
--img-border-width: 0;
|
||||
--img-border-color: #808080;
|
||||
--maximize_button: #fff;
|
||||
--bg-gradient: linear-gradient(0deg, rgba(0, 0, 0, 0.94) 0%, rgb(6, 6, 6) 10%, rgb(43, 43, 43) 100%);
|
||||
/* appearance */
|
||||
background: black;
|
||||
}
|
||||
|
||||
html[theme="f0ck95d"] .err {
|
||||
@ -1101,6 +1153,21 @@ html[theme="f0ck95d"] .admin-search button {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* fullscreen */
|
||||
html[res="fullscreen"] .embed-responsive-16by9::before {
|
||||
padding-top: 32.99%;
|
||||
}
|
||||
|
||||
html[res="fullscreen"] .container {
|
||||
max-width: 100% !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
html[res="fullscreen"] span#favs {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
}
|
||||
@ -1130,6 +1197,7 @@ html[theme="f0ck95d"] .admin-search button {
|
||||
html,
|
||||
body {
|
||||
background-color: var(--bg);
|
||||
background: var(--bg-gradient);
|
||||
color: var(--white);
|
||||
margin: 0;
|
||||
font-family: var(--font);
|
||||
@ -1139,6 +1207,7 @@ body {
|
||||
overscroll-behavior-y: contain;
|
||||
overflow: unset;
|
||||
font-size: 14px;
|
||||
height: /* 100%; */auto;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
@ -1186,7 +1255,7 @@ h5 {
|
||||
|
||||
div.posts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
|
||||
justify-items: center;
|
||||
grid-gap: 5px;
|
||||
margin: 0;
|
||||
@ -1194,11 +1263,12 @@ div.posts {
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
@media (max-width: 376px) {
|
||||
/* nervt auf handy xd */
|
||||
/*@media (max-width: 376px) {
|
||||
div.posts {
|
||||
grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
div.posts>a {
|
||||
display: inline-block;
|
||||
@ -1548,9 +1618,10 @@ span.placeholder {
|
||||
}
|
||||
|
||||
@media (max-width: 1325px) {
|
||||
.ranking {
|
||||
/* ranking page - idea */
|
||||
/* .ranking {
|
||||
grid-template-columns: 1fr 1fr !important;
|
||||
}
|
||||
} */
|
||||
|
||||
.by-user,
|
||||
.by-stats,
|
||||
@ -1693,8 +1764,8 @@ span.placeholder {
|
||||
}
|
||||
|
||||
.imageDoor {
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
width: 25px;
|
||||
height: 29px;
|
||||
display: inline-flex;
|
||||
padding: 2px;
|
||||
}
|
||||
@ -1722,8 +1793,8 @@ span.placeholder {
|
||||
.index-container {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background-color: var(--navigation-links-bg);
|
||||
}
|
||||
/* background-color: var(--navigation-links-bg);
|
||||
*/}
|
||||
|
||||
@media (min-width: 361px) {
|
||||
.embed-responsive-image {
|
||||
@ -2278,8 +2349,8 @@ div#footbar {
|
||||
img.avatar {
|
||||
height: auto;
|
||||
position: relative;
|
||||
width: 28px;
|
||||
margin: 0;
|
||||
width: 25px;
|
||||
margin: 1px;
|
||||
left: -7px;
|
||||
top: 0;
|
||||
border-top-left-radius: 3px;
|
||||
@ -2300,7 +2371,7 @@ div.logwrap>p {
|
||||
table.table {
|
||||
border-collapse: collapse;
|
||||
margin: 25px 0;
|
||||
min-width: max-content;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
table.table thead tr {
|
||||
@ -2331,7 +2402,7 @@ table.table tbody tr:nth-of-type(odd) {
|
||||
.ranking {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
justify-content: center;
|
||||
/* justify-content: center; */
|
||||
}
|
||||
|
||||
.ranking div {
|
||||
@ -2340,18 +2411,19 @@ table.table tbody tr:nth-of-type(odd) {
|
||||
background: var(--nav-bg);
|
||||
border: 1px solid black;
|
||||
border-radius: 3px;
|
||||
min-width: 272px;
|
||||
}
|
||||
|
||||
.by-user {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.by-stats {
|
||||
grid-column: 3;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.by-hoster {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
/* tags */
|
||||
@ -2768,185 +2840,184 @@ ul.navbar-nav-guests li.nav-item {
|
||||
}
|
||||
}
|
||||
|
||||
/* mobile random button */
|
||||
/* .random {
|
||||
display: none;
|
||||
/* fadeIn effect */
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metadata {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.random {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
transition: .5s ease-out;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.random a {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.random a::after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.random:active {
|
||||
background: #00000036;
|
||||
}
|
||||
} */
|
||||
|
||||
/* changes for comment mockup */
|
||||
body {
|
||||
overflow: hidden;
|
||||
img#f0ck-image, div.imageDoor, div.posts a, video {
|
||||
animation: 1s ease-out 0s 1 fadeInFX;
|
||||
}
|
||||
|
||||
|
||||
.container {
|
||||
padding-top: 0;
|
||||
max-width: 100%;
|
||||
overflow: scroll;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#main {
|
||||
/* f0ckgle */
|
||||
.f0ckgle {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
#comments1 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comment {
|
||||
background: black;
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
display: grid;
|
||||
grid-template-rows: 0fr 1fr;
|
||||
grid-template-columns: 0fr 1fr;
|
||||
}
|
||||
.sidebar {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: 0fr 0fr auto 0fr;
|
||||
border-top: 1px solid #363636;
|
||||
border-right: 1px solid #363636;
|
||||
background-color: var(--metadata-bg);
|
||||
}
|
||||
|
||||
.commentbox {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 0fr;
|
||||
padding: 5px;
|
||||
background: #373737;
|
||||
grid-gap: 5px;
|
||||
}
|
||||
|
||||
#comments1 {
|
||||
background: #131313;
|
||||
border: 1px solid black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button.commentbutton {
|
||||
width: 5rem;
|
||||
border: 1px solid black;
|
||||
background: #131313;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.commentholder {
|
||||
overflow: scroll;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
|
||||
img.avatar_comments {
|
||||
align-content: center;
|
||||
height: auto;
|
||||
position: relative;
|
||||
width: 35px;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.comment_content {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.comment_content p {
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.comment_username {
|
||||
align-items: baseline;
|
||||
display: grid;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
padding-left: 5px;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.comment {
|
||||
background: #171717;
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
display: grid;
|
||||
grid-template-rows: 0fr 0fr;
|
||||
grid-template-columns: 0fr 1fr;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.comment_content img {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.header_sidebar {
|
||||
background: -webkit-linear-gradient(left, #4f4a4f, #131313);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.header_sidebar span {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* comments responsive */
|
||||
@media (max-width: 799px) {
|
||||
#main {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.commentholder {
|
||||
overflow: unset;
|
||||
}
|
||||
|
||||
.container {
|
||||
overflow: unset;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-title {
|
||||
text-align: center;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
|
||||
.admin-search input {
|
||||
padding: 15px;
|
||||
border: 1px solid var(--accent);
|
||||
min-width: 30%;
|
||||
}
|
||||
|
||||
/* profile */
|
||||
.profile_head {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
background: var(--nav-bg);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.profile_head_avatar {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.profile_head_username {
|
||||
font-weight: bold;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.profile_head_user_stats {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.layersoffear {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.user_content_wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-column-gap: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.f0cks h5, .favs h5 {
|
||||
background: var(--dropdown-bg);
|
||||
}
|
||||
|
||||
.f0cks-header, .favs-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
background: var(--img-border-color);
|
||||
padding: 0px 5px 0px 5px;
|
||||
}
|
||||
|
||||
div.f0cks div.posts {
|
||||
padding: 5px;
|
||||
background: var(--dropdown-bg);
|
||||
}
|
||||
|
||||
div.favs div.posts {
|
||||
padding: 5px;
|
||||
background: var(--dropdown-bg);
|
||||
}
|
||||
|
||||
#bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-filter: blur(100px);
|
||||
filter: blur(100px);
|
||||
transform: translate3d(0, 0, 0);
|
||||
z-index: 0;
|
||||
transition: 2s ease;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
button#togglebg {
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeInFX {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutFX {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fader-in {
|
||||
animation: fadeIn .8s steps(100) forwards;
|
||||
}
|
||||
|
||||
.fader-out {
|
||||
animation: fadeOut .8s steps(100) forwards
|
||||
}
|
||||
|
||||
.settings {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
input[name="i_avatar"] {
|
||||
text-align: center;
|
||||
width: 50px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
input#s_avatar {
|
||||
padding: 5px;
|
||||
border: 1px solid var(--black);
|
||||
border-radius: 3px;
|
||||
background-image: linear-gradient(to bottom, var(--nav-link-background-linear-gradient));
|
||||
box-shadow: var(--nav-link-box-shadow);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#s_avatar:hover {
|
||||
background: #ffffff0f;
|
||||
}
|
||||
.theforceofthree {
|
||||
display: grid;
|
||||
grid-template-columns: 0.4fr 1fr 0.4fr;
|
||||
}
|
@ -72,7 +72,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
.v0ck_player_controls svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: #fff;
|
||||
|
171
public/s/css/w0bm.css
Normal file
@ -0,0 +1,171 @@
|
||||
.navbar-brand {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
.image-brand {
|
||||
width: 5cm;
|
||||
}
|
||||
|
||||
.kontrollelement {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
grid-template-columns: 0.4fr auto 0.4fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
html[theme="f0ck95d"] .badge-dark {
|
||||
border: none;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.steuerung {
|
||||
font-size:x-large;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.steuerung a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blahlol {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
span#favs {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.badge-dark, #themeselector {
|
||||
background-color: unset;
|
||||
border: unset;
|
||||
}
|
||||
|
||||
._204863 {
|
||||
background: none;
|
||||
border: none;
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
html[theme="iced"] ._204863 {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.media-object {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.v0ck {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
html[theme="f0ck95d"] .embed-responsive.embed-responsive-16by9 {
|
||||
background:#0000008f !important;
|
||||
}
|
||||
|
||||
html[theme="f0ck95"] .embed-responsive.embed-responsive-16by9 {
|
||||
background:var(--nav-bg) !important;
|
||||
}
|
||||
|
||||
.v0ck_overlay {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.v0ck_player_controls {
|
||||
background:rgba(0, 0, 0, 0.95) !important;
|
||||
}
|
||||
|
||||
html[theme="term"] .image-brand {
|
||||
filter: hue-rotate(-50deg);
|
||||
}
|
||||
|
||||
html[theme="f0ck"] .image-brand {
|
||||
filter: hue-rotate(-50deg);
|
||||
}
|
||||
|
||||
html[theme="p1nk"] .image-brand {
|
||||
filter: hue-rotate(150deg);
|
||||
}
|
||||
|
||||
html[theme="orange"] .image-brand {
|
||||
filter: hue-rotate(-160deg);
|
||||
}
|
||||
|
||||
.v0ck_overlay {
|
||||
background-color: none !important;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.tags { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.tags { grid-template-columns: repeat(3, 1fr); }
|
||||
}
|
||||
|
||||
.tag {
|
||||
box-shadow: 1px 1px 1px black;
|
||||
display: grid;
|
||||
margin: 5px;
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
.navigation-rechts {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.about {
|
||||
background: none!important;
|
||||
}
|
||||
|
||||
.login-image {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.tos {
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
.nav-link[data-toggle="dropdown"]::after {
|
||||
content: "" !important;
|
||||
}
|
||||
|
||||
.dropdown-menu.shii {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.tags {
|
||||
grid-template-columns: repeat(auto-fill, 20em);
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tag img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1056px) {
|
||||
html, body {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
html[theme="f0ck95d"] .embed-responsive.embed-responsive-16by9 {
|
||||
border-bottom: outset 2px silver;
|
||||
}
|
||||
|
||||
html[theme="f0ck95"] .embed-responsive.embed-responsive-16by9 {
|
||||
border-bottom: outset 2px silver;
|
||||
}
|
BIN
public/s/img/404.gif
Normal file
After Width: | Height: | Size: 652 KiB |
BIN
public/s/img/cockfag.png
Normal file
After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 68 KiB |
@ -6,5 +6,7 @@
|
||||
<symbol id="heart_regular" viewBox="0 0 512 512"><path d="M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"/></symbol>
|
||||
<symbol id="heart_solid" viewBox="0 0 512 512"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"/></symbol>
|
||||
<symbol id="cross" viewBox="0 0 512 512"><path d="M53.6,62.3 L458.4,458.4 M458.4,62.3 L53.6,458.4" style="stroke-linecap: round;stroke-width: 60;"/></symbol>
|
||||
<symbol id="window-maximize" viewBox="0 0 512 512"><path d="M464 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm0 394c0 3.3-2.7 6-6 6H54c-3.3 0-6-2.7-6-6V192h416v234z" style="fill: var(--maximize_button);"/></symbol>
|
||||
<symbol id="window-minimize" viewBox="0 0 512 512"><path d="M464 0H144c-26.5 0-48 21.5-48 48v48H48c-26.5 0-48 21.5-48 48v320c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zm-96 464H48V256h320v208zm96-96h-48V144c0-26.5-21.5-48-48-48H144V48h320v320z" style="fill: var(--maximize_button);"/></symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
public/s/img/w0bm_mosh_banner_by_marderchen.gif
Normal file
After Width: | Height: | Size: 824 KiB |
@ -1,41 +1,3 @@
|
||||
let flashActive = false;
|
||||
const flashTypes = [ "error", "success", "warn" ];
|
||||
|
||||
const flash = ({ type, msg }) => {
|
||||
let flashContainer;
|
||||
if(tmp = document.querySelector("div#flash"))
|
||||
flashContainer = tmp;
|
||||
else {
|
||||
flashContainer = document.createElement("div");
|
||||
flashContainer.id = "flash";
|
||||
document.body.insertAdjacentElement("afterbegin", flashContainer);
|
||||
}
|
||||
|
||||
flashContainer.innerHTML = msg;
|
||||
if(flashTypes.includes(type)) {
|
||||
flashContainer.className = "";
|
||||
flashContainer.classList.add(type);
|
||||
}
|
||||
if(flashActive)
|
||||
return false;
|
||||
|
||||
flashActive = true;
|
||||
flashContainer.animate(
|
||||
[ { bottom: "-28px" }, { bottom: 0 } ], {
|
||||
duration: 500,
|
||||
fill: "both"
|
||||
}
|
||||
).onfinish = () => setTimeout(() => {
|
||||
flashContainer.animate(
|
||||
[ { bottom: 0 }, { bottom: "-28px" } ], {
|
||||
duration: 500,
|
||||
fill: "both"
|
||||
}
|
||||
).onfinish = () => flashActive = false;
|
||||
}, 4 * 1e3);
|
||||
return true;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
if(_addtag = document.querySelector("a#a_addtag")) {
|
||||
const postid = +document.querySelector("a.id-link").innerText;
|
||||
@ -85,7 +47,7 @@ const flash = ({ type, msg }) => {
|
||||
[...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag));
|
||||
_tags.reverse().forEach(tag => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/tag/${tag.tag}`;
|
||||
a.href = `/tag/${tag.normalized}`;
|
||||
a.style = "color: inherit !important";
|
||||
a.innerHTML = tag.tag;
|
||||
a.addEventListener("click", editTagEvent); // tmp
|
||||
@ -140,10 +102,8 @@ const flash = ({ type, msg }) => {
|
||||
tagname: tmptag
|
||||
});
|
||||
if(!res.success) {
|
||||
return flash({
|
||||
type: "error",
|
||||
msg: res.msg
|
||||
});
|
||||
alert(res.msg);
|
||||
return false;
|
||||
}
|
||||
tags = res.tags.map(t => t.tag);
|
||||
renderTags(res.tags);
|
||||
@ -202,12 +162,6 @@ const flash = ({ type, msg }) => {
|
||||
})).json();
|
||||
|
||||
renderTags(res.tags);
|
||||
tags = res.tags.map(t => t.tag);
|
||||
|
||||
flash({
|
||||
type: "success",
|
||||
msg: tags.join()
|
||||
});
|
||||
};
|
||||
|
||||
const deleteButtonEvent = async e => {
|
||||
@ -218,17 +172,8 @@ const flash = ({ type, msg }) => {
|
||||
const res = await post("/api/v2/admin/deletepost", {
|
||||
postid: postid
|
||||
});
|
||||
if(res.success) {
|
||||
flash({
|
||||
type: "success",
|
||||
msg: "post was successfully deleted"
|
||||
});
|
||||
}
|
||||
else {
|
||||
flash({
|
||||
type: "error",
|
||||
msg: res.msg
|
||||
});
|
||||
if(!res.success) {
|
||||
alert(res.msg);
|
||||
}
|
||||
};
|
||||
|
||||
@ -333,11 +278,12 @@ const flash = ({ type, msg }) => {
|
||||
document.querySelector("a#a_toggle").addEventListener("click", toggleEvent);
|
||||
[...document.querySelectorAll("#tags > .badge > a:first-child")].map(t => t.addEventListener("click", editTagEvent));
|
||||
[...document.querySelectorAll("#tags > .badge > a:last-child")].map(t => t.addEventListener("click", deleteEvent));
|
||||
if(document.querySelector("svg#a_delete"))
|
||||
document.querySelector("svg#a_delete").addEventListener("click", deleteButtonEvent);
|
||||
document.querySelector("svg#a_favo").addEventListener("click", toggleFavEvent);
|
||||
|
||||
document.addEventListener("keyup", e => {
|
||||
if(e.target.tagName === "INPUT")
|
||||
if(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")
|
||||
return;
|
||||
if(e.key === "p")
|
||||
toggleEvent();
|
||||
|
@ -1,13 +1,57 @@
|
||||
|
||||
window.requestAnimFrame = (function(){
|
||||
return window.requestAnimationFrame
|
||||
|| window.webkitRequestAnimationFrame
|
||||
|| window.mozRequestAnimationFrame
|
||||
|| function(callback) { window.setTimeout(callback, 1000 / 60);};
|
||||
})();
|
||||
|
||||
(() => {
|
||||
let video;
|
||||
if(elem = document.querySelector("#my-video")) {
|
||||
video = new v0ck(elem);
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.key === " " && e.target.tagName !== "INPUT") {
|
||||
if(e.key === " " && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
||||
video[video.paused ? 'play' : 'pause']();
|
||||
document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('togglebg').addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
background = !background;
|
||||
localStorage.setItem('background', background.toString());
|
||||
var canvas = document.getElementById('bg');
|
||||
if (background) {
|
||||
canvas.classList.add('fader-in');
|
||||
canvas.classList.remove('fader-out');
|
||||
} else {
|
||||
canvas.classList.add('fader-out');
|
||||
canvas.classList.remove('fader-in');
|
||||
}
|
||||
animationLoop();
|
||||
});
|
||||
|
||||
if(elem !== null) {
|
||||
if(localStorage.getItem('background') == undefined) {
|
||||
localStorage.setItem('background', 'true');
|
||||
}
|
||||
|
||||
var background = localStorage.getItem('background') === 'true';
|
||||
var canvas = document.getElementById('bg');
|
||||
var context = canvas.getContext('2d');
|
||||
var cw = canvas.width = canvas.clientWidth | 0;
|
||||
var ch = canvas.height = canvas.clientHeight | 0;
|
||||
|
||||
function animationLoop() {
|
||||
if(video.paused || video.ended || !background)
|
||||
return;
|
||||
context.drawImage(video, 0, 0, cw, ch);
|
||||
window.requestAnimFrame(animationLoop);
|
||||
}
|
||||
|
||||
elem.addEventListener('play', animationLoop);
|
||||
}
|
||||
}
|
||||
|
||||
let tt = false;
|
||||
@ -28,7 +72,7 @@
|
||||
" ": clickOnElementBinding("#f0ck-image")
|
||||
};
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.key in keybindings && e.target.tagName !== "INPUT") {
|
||||
if(e.key in keybindings && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
||||
if(e.shiftKey || e.ctrlKey || e.metaKey || e.altKey)
|
||||
return;
|
||||
e.preventDefault();
|
||||
@ -46,19 +90,44 @@
|
||||
i.src = e.src;
|
||||
});
|
||||
|
||||
// <wheeler>
|
||||
const wheelEventListener = function(event) {
|
||||
if (event.target.closest('.media-object')) {
|
||||
if (event.deltaY < 0) {
|
||||
document.getElementById('next').click();
|
||||
} else if (event.deltaY > 0) {
|
||||
document.getElementById('prev').click();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('wheel', wheelEventListener);
|
||||
// </wheeler>
|
||||
|
||||
|
||||
if(f0ckimage = document.querySelector("img#f0ck-image")) {
|
||||
const f0ckimagescroll = document.querySelector("#image-scroll");
|
||||
|
||||
let isImageExpanded = false;
|
||||
console.log("entry point - image unclicked")
|
||||
|
||||
f0ckimage.addEventListener("click", async e => {
|
||||
e.preventDefault();
|
||||
const img = await imgSize(f0ckimage);
|
||||
if(img.width > img.height)
|
||||
return;
|
||||
f0ckimagescroll.hasAttribute("style")
|
||||
? f0ckimagescroll.removeAttribute("style")
|
||||
: f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
|
||||
f0ckimage.hasAttribute("style")
|
||||
? f0ckimage.removeAttribute("style")
|
||||
: f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0; border: var(--img-border-width) solid var(--img-border-color); border-top: none; border-bottom: none;");
|
||||
console.log("img clicked");
|
||||
if (isImageExpanded) {
|
||||
isImageExpanded = false;
|
||||
f0ckimagescroll.removeAttribute("style");
|
||||
f0ckimage.removeAttribute("style");
|
||||
console.log("image is not expanded")
|
||||
window.addEventListener('wheel', wheelEventListener);
|
||||
} else {
|
||||
if (img.width > img.height) return;
|
||||
isImageExpanded = true;
|
||||
window.removeEventListener('wheel', wheelEventListener);
|
||||
f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
|
||||
f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0; border: var(--img-border-width) solid var(--img-border-color); border-top: none; border-bottom: none;");
|
||||
}
|
||||
});
|
||||
}
|
||||
// </image-responsive>
|
||||
@ -68,7 +137,7 @@
|
||||
const scroll_treshold = 1;
|
||||
if([...document.querySelectorAll("div.posts")].length === 1) {
|
||||
document.addEventListener("wheel", e => {
|
||||
if(Math.ceil(window.innerHeight + window.scrollY) >= document.body.offsetHeight && e.deltaY > 0) { // down
|
||||
if(Math.ceil(window.innerHeight + window.scrollY) >= document.querySelector('#main').offsetHeight && e.deltaY > 0) { // down
|
||||
if(elem = document.querySelector(".pagination > .next:not(.disabled)")) {
|
||||
if(tts < scroll_treshold) {
|
||||
document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--footbar-color)";
|
||||
@ -234,4 +303,8 @@
|
||||
});
|
||||
}
|
||||
// </mediakeys>
|
||||
|
||||
// <scroller>
|
||||
|
||||
// </scroller>
|
||||
})();
|
@ -8,13 +8,14 @@ const Cookie = {
|
||||
opts['max-age'] = opts.days * 60 * 60 * 24;
|
||||
delete opts.days;
|
||||
}
|
||||
opts.SameSite = 'Strict';
|
||||
opts = Object.entries(opts).reduce((accumulatedStr, [k, v]) => `${accumulatedStr}; ${k}=${v}`, '');
|
||||
document.cookie = name + '=' + encodeURIComponent(value) + opts
|
||||
}
|
||||
};
|
||||
|
||||
(() => {
|
||||
const acttheme = Cookie.get('theme') ?? "f0ck";
|
||||
const acttheme = Cookie.get('theme') ?? "w0bm";
|
||||
const themecontainer = document.querySelector("li#themes > ul.dropdown-menu");
|
||||
const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase());
|
||||
if(acttheme !== document.documentElement.getAttribute("theme") && themes.includes(acttheme))
|
||||
@ -29,9 +30,9 @@ const Cookie = {
|
||||
}));
|
||||
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.target.tagName === "INPUT")
|
||||
if(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")
|
||||
return;
|
||||
const acttheme = Cookie.get('theme') ?? "f0ck";
|
||||
const acttheme = Cookie.get('theme') ?? "w0bm";
|
||||
const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase());
|
||||
const k = e.key;
|
||||
if(k === "t") {
|
||||
@ -44,4 +45,21 @@ const Cookie = {
|
||||
Cookie.set("theme", themes[i], { path: "/", days: 360 });
|
||||
}
|
||||
});
|
||||
|
||||
if(tbuttonfull = document.querySelector('svg#a_tfull')) {
|
||||
tbuttonfull.addEventListener('click', e => {
|
||||
let f = Cookie.get('fullscreen');
|
||||
if(f == 1) {
|
||||
Cookie.set('fullscreen', 0);
|
||||
document.querySelector('html').setAttribute('res', '');
|
||||
tbuttonfull.innerHTML = `<use href="/s/img/iconset.svg#window-maximize"></use>`;
|
||||
}
|
||||
else {
|
||||
Cookie.set('fullscreen', 1);
|
||||
document.querySelector('html').setAttribute('res', 'fullscreen');
|
||||
tbuttonfull.innerHTML = `<use href="/s/img/iconset.svg#window-minimize"></use>`;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
229
public/s/js/user.js
Normal file
@ -0,0 +1,229 @@
|
||||
(async () => {
|
||||
if(_addtag = document.querySelector("a#a_addtag")) {
|
||||
const postid = +document.querySelector("a.id-link").innerText;
|
||||
const poster = document.querySelector("a#a_username").innerText;
|
||||
let tags = [...document.querySelectorAll("#tags > .badge")].map(t => t.innerText.slice(0, -2));
|
||||
|
||||
const queryapi = async (url, data, method = 'GET') => {
|
||||
let req;
|
||||
if(method == 'POST') {
|
||||
req = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
else {
|
||||
let s = [];
|
||||
for(const [ key, val ] of Object.entries(data))
|
||||
s.push(encodeURIComponent(key) + "=" + encodeURIComponent(val));
|
||||
req = await fetch(url + '?' + s.join('&'));
|
||||
}
|
||||
return await req.json();
|
||||
};
|
||||
|
||||
const get = async (url, data) => queryapi(url, data, 'GET');
|
||||
const post = async (url, data) => queryapi(url, data, 'POST');
|
||||
|
||||
const renderTags = _tags => {
|
||||
[...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag));
|
||||
_tags.reverse().forEach(tag => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/tag/${tag.normalized}`;
|
||||
a.style = "color: inherit !important";
|
||||
a.innerHTML = tag.tag;
|
||||
|
||||
const span = document.createElement("span");
|
||||
span.classList.add("badge", "mr-2");
|
||||
span.setAttribute('tooltip', tag.user);
|
||||
|
||||
tag.badge.split(" ").forEach(b => span.classList.add(b));
|
||||
|
||||
span.insertAdjacentElement("beforeend", a);
|
||||
|
||||
document.querySelector("#tags").insertAdjacentElement("afterbegin", span);
|
||||
});
|
||||
};
|
||||
|
||||
const addtagClick = (ae = false) => {
|
||||
if(ae)
|
||||
ae.preventDefault();
|
||||
|
||||
const insert = document.querySelector("a#a_addtag");
|
||||
const span = document.createElement("span");
|
||||
span.classList.add("badge", "badge-light", "mr-2");
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.size = "10";
|
||||
input.value = "";
|
||||
input.setAttribute("list", "testlist");
|
||||
input.setAttribute("autoComplete", "off");
|
||||
|
||||
span.insertAdjacentElement("afterbegin", input);
|
||||
insert.insertAdjacentElement("beforebegin", span);
|
||||
|
||||
input.focus();
|
||||
|
||||
let tt = null;
|
||||
let lastInput = '';
|
||||
const testList = document.querySelector('#testlist');
|
||||
|
||||
input.addEventListener("keyup", async e => {
|
||||
if(e.key === "Enter") {
|
||||
const tmptag = input.value?.trim();
|
||||
if(tags.includes(tmptag))
|
||||
return alert("tag already exists");
|
||||
const res = await post("/api/v2/admin/" + postid + "/tags", {
|
||||
tagname: tmptag
|
||||
});
|
||||
if(!res.success) {
|
||||
alert(res.msg);
|
||||
return false;
|
||||
}
|
||||
tags = res.tags.map(t => t.tag);
|
||||
renderTags(res.tags);
|
||||
addtagClick();
|
||||
testList.innerText = "";
|
||||
}
|
||||
else if(e.key === "Escape") {
|
||||
span.parentElement.removeChild(span);
|
||||
testList.innerText = "";
|
||||
}
|
||||
else {
|
||||
if(tt != null)
|
||||
clearTimeout(tt);
|
||||
|
||||
tt = setTimeout(async () => {
|
||||
tt = null;
|
||||
|
||||
const tmptag = input.value?.trim();
|
||||
|
||||
if(tmptag == lastInput || tmptag.length <= 1)
|
||||
return false;
|
||||
|
||||
testList.innerText = "";
|
||||
lastInput = tmptag;
|
||||
|
||||
const res = await get('/api/v2/admin/tags/suggest', {
|
||||
q: tmptag
|
||||
});
|
||||
|
||||
for(const entry of res.suggestions) {
|
||||
const option = document.createElement('option');
|
||||
option.value = entry.tag;
|
||||
|
||||
if(!/fox/.test(navigator.userAgent))
|
||||
option.innerText = `tagged ${entry.tagged} times (score: ${entry.score.toFixed(2)})`;
|
||||
|
||||
testList.insertAdjacentElement('beforeEnd', option);
|
||||
};
|
||||
}, 500);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
input.addEventListener("focusout", ie => {
|
||||
if(input.value.length === 0)
|
||||
input.parentElement.parentElement.removeChild(input.parentElement);
|
||||
});
|
||||
};
|
||||
|
||||
const toggleEvent = async (e = false) => {
|
||||
if(e)
|
||||
e.preventDefault();
|
||||
|
||||
const res = await (await fetch('/api/v2/admin/' + encodeURIComponent(postid) + '/tags/toggle', {
|
||||
method: 'PUT'
|
||||
})).json();
|
||||
|
||||
renderTags(res.tags);
|
||||
};
|
||||
|
||||
const toggleFavEvent = async e => {
|
||||
const res = await post('/api/v2/admin/togglefav', {
|
||||
postid: postid
|
||||
});
|
||||
if(res.success) {
|
||||
const fav = document.querySelector("svg#a_favo > use").href;
|
||||
fav.baseVal = '/s/img/iconset.svg#heart_' + (fav.baseVal.match(/heart_(regular|solid)$/)[1] == "solid" ? "regular" : "solid");
|
||||
|
||||
// span#favs
|
||||
const favcontainer = document.querySelector('span#favs');
|
||||
favcontainer.innerHTML = "";
|
||||
|
||||
favcontainer.hidden = !(favcontainer.hidden || res.favs.length > 0);
|
||||
|
||||
res.favs.forEach(f => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/user/${f.user}/favs`;
|
||||
a.setAttribute('tooltip', f.user);
|
||||
a.setAttribute('flow', 'up');
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = `/t/${f.avatar}.webp`;
|
||||
img.style.height = "32px";
|
||||
img.style.width = "32px";
|
||||
|
||||
a.insertAdjacentElement('beforeend', img);
|
||||
favcontainer.insertAdjacentElement('beforeend', a);
|
||||
favcontainer.innerHTML += " ";
|
||||
});
|
||||
}
|
||||
else {
|
||||
// lul
|
||||
}
|
||||
};
|
||||
|
||||
_addtag.addEventListener("click", addtagClick);
|
||||
document.querySelector("a#a_toggle").addEventListener("click", toggleEvent);
|
||||
document.querySelector("svg#a_favo").addEventListener("click", toggleFavEvent);
|
||||
|
||||
document.addEventListener("keyup", e => {
|
||||
if(e.target.tagName === "INPUT")
|
||||
return;
|
||||
if(e.key === "p")
|
||||
toggleEvent();
|
||||
else if(e.key === "i")
|
||||
addtagClick();
|
||||
else if(e.key === "x")
|
||||
deleteButtonEvent();
|
||||
else if(e.key === "f")
|
||||
toggleFavEvent();
|
||||
});
|
||||
}
|
||||
|
||||
if(document.location.pathname === '/settings') {
|
||||
const saveAvatar = async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const avatar = +document.querySelector('input[name="i_avatar"]').value;
|
||||
let res = await fetch('/api/v2/settings/setAvatar', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ avatar })
|
||||
});
|
||||
const code = res.status;
|
||||
res = await res.json();
|
||||
|
||||
switch(code) {
|
||||
case 200:
|
||||
document.querySelector('#img_avatar').src = `/t/${avatar}.webp`;
|
||||
document.querySelector('img.avatar').src = `/t/${avatar}.webp`;
|
||||
break;
|
||||
default:
|
||||
console.log(res);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelector('input#s_avatar').addEventListener('click', saveAvatar);
|
||||
document.querySelector('input[name="i_avatar"]').addEventListener('keyup', async e => {
|
||||
if(e.key === 'Enter')
|
||||
await saveAvatar(e);
|
||||
});
|
||||
}
|
||||
})();
|
@ -18,6 +18,7 @@ const tpl_player = svg => `<div class="v0ck_player_controls">
|
||||
<input type="range" name="volume" min="0" max="1" step="0.01" value="1" />
|
||||
<button class="v0ck_player_button v0ck_playtime">00:00 / 00:00</button>
|
||||
<span style="flex: 30"></span>
|
||||
<button id="togglebg">💡</button>
|
||||
<button data-skip="-10" class="v0ck_player_button">
|
||||
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_backward" href="${svg}#backward"></use></svg>
|
||||
</button>
|
||||
|
32
src/inc/autotagger.mjs
Normal file
@ -0,0 +1,32 @@
|
||||
import fetch from 'flumm-fetch';
|
||||
import cfg from './config.mjs';
|
||||
|
||||
export default new class autotagger {
|
||||
async isNSFW(filename, filesize) {
|
||||
let opts = {
|
||||
method: 'POST',
|
||||
};
|
||||
let apiurl;
|
||||
if(filesize < 4194304) {
|
||||
apiurl = cfg.apis.nsfw1.url;
|
||||
opts.headers = cfg.apis.nsfw1.headers;
|
||||
opts.body = JSON.stringify({
|
||||
DataRepresentation: "URL",
|
||||
Value: `${cfg.main.url.full}/b/${filename}`
|
||||
});
|
||||
}
|
||||
else {
|
||||
apiurl = cfg.apis.nsfw2.url;
|
||||
opts.headers = cfg.apis.nsfw2.headers;
|
||||
opts.body = JSON.stringify({
|
||||
url: `${cfg.main.url.full}/b/${filename}`
|
||||
})
|
||||
}
|
||||
const res = await (await fetch(apiurl, opts)).json();
|
||||
|
||||
if(filesize < 4194304)
|
||||
return res.IsImageAdultClassified || res.RacyClassificationScore > 0.6;
|
||||
else
|
||||
return res.unsafe;
|
||||
};
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import _config from "../../config.json" assert { type: "json" };
|
||||
import _config from "../../config.json" with { type: "json" };
|
||||
|
||||
let config = JSON.parse(JSON.stringify(_config));
|
||||
|
||||
|
318
src/inc/events/callback_query.mjs
Normal file
@ -0,0 +1,318 @@
|
||||
import logger from "../log.mjs";
|
||||
import { getLevel } from "../../inc/admin.mjs";
|
||||
import { promises as fs } from "fs";
|
||||
import cfg from "../config.mjs";
|
||||
import db from "../sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
|
||||
const tagkeyboard = id => {
|
||||
const tags = [{
|
||||
tag: 'music',
|
||||
id: 115
|
||||
}, {
|
||||
tag: 'german',
|
||||
id: 329
|
||||
}, {
|
||||
tag: 'cat',
|
||||
id: 217
|
||||
}, {
|
||||
tag: 'doggo',
|
||||
id: 5
|
||||
}];
|
||||
return Promise.all(tags.map(async t => ({ text: `${await lib.hasTag(id, t.id) ? '✓ ' : ''}${t.tag}`, callback_data: `b_settag_${t.id}:${id}` })));
|
||||
};
|
||||
|
||||
export default async bot => {
|
||||
|
||||
return [{
|
||||
name: "callback_query",
|
||||
listener: "callback_query",
|
||||
f: async e => {
|
||||
logger.info(`${e.network} -> ${e.channel} -> ${e.user.nick}: ${e.message}`);
|
||||
|
||||
let [ cmd, id ] = e.opt.data.split(':');
|
||||
let f0ck;
|
||||
id = +id;
|
||||
|
||||
if(cmd.startsWith('b_settag_')) {
|
||||
const tagid = +cmd.replace('b_settag_', '');
|
||||
|
||||
if(!(await lib.getTags(id)).filter(tag => tag.id == tagid).length) {
|
||||
// insert
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: id,
|
||||
tag_id: tagid,
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
await db`
|
||||
delete from "tags_assign"
|
||||
where tag_id = ${tagid}
|
||||
and item_id = ${id}
|
||||
`;
|
||||
}
|
||||
|
||||
const keyboard = await tagkeyboard(id);
|
||||
|
||||
return await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
...keyboard
|
||||
], [
|
||||
{ text: 'back', callback_data: `b_back:${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
switch(cmd) {
|
||||
case "b_tags":
|
||||
if(!id)
|
||||
return;
|
||||
|
||||
const keyboard = await tagkeyboard(id);
|
||||
|
||||
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
...keyboard
|
||||
], [
|
||||
{ text: 'back', callback_data: `b_back:${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
break;
|
||||
case "b_back":
|
||||
if(!id)
|
||||
return;
|
||||
|
||||
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
|
||||
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
|
||||
{ text: 'tags', callback_data: `b_tags:${id}` },
|
||||
{ text: '❌ delete', callback_data: `b_delete:${id}` }
|
||||
], [
|
||||
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
break;
|
||||
case "b_sfw":
|
||||
|
||||
if(!id)
|
||||
return;
|
||||
|
||||
if(!await lib.hasTag(id, 1)) {
|
||||
// insert
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: id,
|
||||
tag_id: 1, // sfw
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
if(await lib.hasTag(id, 2)) {
|
||||
await db`
|
||||
delete from "tags_assign"
|
||||
where tag_id = 2
|
||||
and item_id = ${id}
|
||||
`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
await db`
|
||||
delete from "tags_assign"
|
||||
where tag_id = 1
|
||||
and item_id = ${id}
|
||||
`;
|
||||
}
|
||||
|
||||
return await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
|
||||
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
|
||||
{ text: 'tags', callback_data: `b_tags:${id}` },
|
||||
{ text: '❌ delete', callback_data: `b_delete:${id}` }
|
||||
], [
|
||||
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
|
||||
break;
|
||||
case "b_nsfw":
|
||||
if(!id)
|
||||
return;
|
||||
|
||||
if(!await lib.hasTag(id, 2)) {
|
||||
// insert
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: id,
|
||||
tag_id: 2, // nsfw
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
if(await lib.hasTag(id, 1)) {
|
||||
await db`
|
||||
delete from "tags_assign"
|
||||
where tag_id = 1
|
||||
and item_id = ${id}
|
||||
`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
await db`
|
||||
delete from "tags_assign"
|
||||
where tag_id = 2
|
||||
and item_id = ${id}
|
||||
`;
|
||||
}
|
||||
|
||||
return await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
|
||||
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
|
||||
{ text: 'tags', callback_data: `b_tags:${id}` },
|
||||
{ text: '❌ delete', callback_data: `b_delete:${id}` }
|
||||
], [
|
||||
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
break;
|
||||
case "b_delete":
|
||||
if(id <= 1)
|
||||
return;
|
||||
|
||||
e.user = {
|
||||
prefix: `${e.raw.reply_to_message.from.username}!${e.raw.reply_to_message.from.id}@${e.network}`,
|
||||
nick: e.raw.reply_to_message.from.first_name,
|
||||
username: e.raw.reply_to_message.from.username,
|
||||
account: e.raw.reply_to_message.from.id.toString()
|
||||
};
|
||||
|
||||
f0ck = await db`
|
||||
select dest, mime, username, userchannel, usernetwork
|
||||
from "items"
|
||||
where
|
||||
id = ${id} and
|
||||
active = 'true'
|
||||
limit 1
|
||||
`;
|
||||
const level = getLevel(e.user).level;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
return await e.reply(`f0ck ${id}: f0ck not found`);
|
||||
}
|
||||
|
||||
if(
|
||||
(f0ck[0].username !== (e.user.nick || e.user.username) ||
|
||||
f0ck[0].userchannel !== e.channel ||
|
||||
f0ck[0].usernetwork !== e.network) &&
|
||||
level < 100
|
||||
) {
|
||||
return await e.reply(`f0ck ${id}: insufficient permissions`);
|
||||
}
|
||||
|
||||
if(~~(new Date() / 1e3) >= (f0ck[0].stamp + 600) && level < 100) {
|
||||
return await e.reply(`f0ck ${id}: too late lol`);
|
||||
}
|
||||
|
||||
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(_=>{});
|
||||
}
|
||||
|
||||
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
|
||||
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
|
||||
{ text: 'tags', callback_data: `b_tags:${id}` },
|
||||
{ text: 'recover', callback_data: `b_recover:${id}` }
|
||||
], [
|
||||
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
break;
|
||||
case "b_recover":
|
||||
if(id <= 1)
|
||||
return;
|
||||
|
||||
e.user = {
|
||||
prefix: `${e.raw.reply_to_message.from.username}!${e.raw.reply_to_message.from.id}@${e.network}`,
|
||||
nick: e.raw.reply_to_message.from.first_name,
|
||||
username: e.raw.reply_to_message.from.username,
|
||||
account: e.raw.reply_to_message.from.id.toString()
|
||||
};
|
||||
|
||||
f0ck = await db`
|
||||
select dest, mime
|
||||
from "items"
|
||||
where
|
||||
id = ${id} and
|
||||
active = 'false'
|
||||
limit 1
|
||||
`;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
return await e.reply(`f0ck ${id}: f0ck not found`);
|
||||
}
|
||||
|
||||
await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{});
|
||||
await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{});
|
||||
|
||||
if(f0ck[0].mime.startsWith('audio')) {
|
||||
await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{});
|
||||
await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{});
|
||||
}
|
||||
|
||||
await db`update "items" set active = 'true' where id = ${id}`;
|
||||
|
||||
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
|
||||
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
|
||||
{ text: 'tags', callback_data: `b_tags:${id}` },
|
||||
{ text: '❌ delete', callback_data: `b_delete:${id}` }
|
||||
], [
|
||||
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
break;
|
||||
default:
|
||||
await e.reply('lol');
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
};
|
@ -40,7 +40,7 @@ export default async bot => {
|
||||
}
|
||||
catch(err) {
|
||||
console.error(err);
|
||||
e.reply(`${t[0]}: An error occured.`);
|
||||
await e.reply(`${t[0]}: An error occured.`);
|
||||
logger.error(`${e.network} -> ${e.channel} -> ${e.user.nick}: ${err.toString ? err : JSON.stringify(err)}`);
|
||||
}
|
||||
});
|
||||
|
@ -84,32 +84,43 @@ export default new class {
|
||||
|
||||
// async funcs
|
||||
async countf0cks() {
|
||||
const tagged = (await db`
|
||||
const tagged = +(await db`
|
||||
select count(*) as total
|
||||
from "items"
|
||||
where id in (select item_id from tags_assign group by item_id)
|
||||
where id in (select item_id from tags_assign group by item_id) and active = true
|
||||
`)[0].total;
|
||||
const untagged = (await db`
|
||||
const untagged = +(await db`
|
||||
select count(*) as total
|
||||
from "items"
|
||||
where id not in (select item_id from tags_assign group by item_id)
|
||||
where id not in (select item_id from tags_assign group by item_id) and active = true
|
||||
`)[0].total;
|
||||
const sfw = (await db`
|
||||
const sfw = +(await db`
|
||||
select count(*) as total
|
||||
from "items"
|
||||
where id in (select item_id from tags_assign where tag_id = 1 group by item_id)
|
||||
where id in (select item_id from tags_assign where tag_id = 1 group by item_id) and active = true
|
||||
`)[0].total;
|
||||
const nsfw = (await db`
|
||||
const nsfw = +(await db`
|
||||
select count(*) as total
|
||||
from "items"
|
||||
where id in (select item_id from tags_assign where tag_id = 2 group by item_id)
|
||||
where id in (select item_id from tags_assign where tag_id = 2 group by item_id) and active = true
|
||||
`)[0].total;
|
||||
const deleted = +(await db`
|
||||
select count(*) as total
|
||||
from "items"
|
||||
where active = false
|
||||
`)[0].total;
|
||||
const lastf0ck = +(await db`
|
||||
select max(id) as id
|
||||
from "items"
|
||||
`)[0].id;
|
||||
return {
|
||||
tagged,
|
||||
untagged,
|
||||
total: +tagged + +untagged,
|
||||
total: tagged + untagged,
|
||||
deleted,
|
||||
untracked: lastf0ck - (tagged + untagged + deleted),
|
||||
sfw,
|
||||
nsfw
|
||||
nsfw,
|
||||
};
|
||||
};
|
||||
async hash(str) {
|
||||
@ -123,15 +134,6 @@ export default new class {
|
||||
const derivedKey = await scrypt(str, salt, 64);
|
||||
return crypto.timingSafeEqual(keyBuffer, derivedKey);
|
||||
};
|
||||
async auth(req, res, next) {
|
||||
if(!req.session) {
|
||||
return res.reply({
|
||||
code: 401,
|
||||
body: "401 - Unauthorized"
|
||||
});
|
||||
}
|
||||
return next();
|
||||
};
|
||||
async getTags(itemid) {
|
||||
const tags = await db`
|
||||
select "tags".id, "tags".tag, "tags".normalized, "user".user
|
||||
@ -161,6 +163,17 @@ export default new class {
|
||||
}
|
||||
return tags;
|
||||
};
|
||||
async hasTag(itemid, tagid) {
|
||||
const tag = (await db`
|
||||
select *
|
||||
from "tags_assign"
|
||||
where
|
||||
item_id = ${+itemid} and
|
||||
tag_id = ${+tagid}
|
||||
limit 1
|
||||
`).length;
|
||||
return !!tag;
|
||||
};
|
||||
async detectNSFW(dest) {
|
||||
return false;
|
||||
const { stdout, stderr } = await exec(
|
||||
@ -195,6 +208,27 @@ export default new class {
|
||||
TABLE_NAME='user_options' and
|
||||
COLUMN_NAME = 'avatar'
|
||||
`)[0].avatar;
|
||||
};
|
||||
|
||||
// meddlware
|
||||
async auth(req, res, next) {
|
||||
if(!req.session || !req.session.admin) {
|
||||
return res.reply({
|
||||
code: 401,
|
||||
body: "401 - Unauthorized"
|
||||
});
|
||||
}
|
||||
return next();
|
||||
};
|
||||
|
||||
async loggedin(req, res, next) {
|
||||
if(!req.session) {
|
||||
return res.reply({
|
||||
code: 401,
|
||||
body: "401 - Unauthorized"
|
||||
});
|
||||
}
|
||||
return next();
|
||||
};
|
||||
|
||||
};
|
||||
|
122
src/inc/queue.mjs
Normal file
@ -0,0 +1,122 @@
|
||||
import fetch from "flumm-fetch";
|
||||
import { exec as _exec } from "child_process";
|
||||
import fs from "fs";
|
||||
import db from "./sql.mjs";
|
||||
|
||||
export default new class queue {
|
||||
|
||||
constructor() {
|
||||
|
||||
};
|
||||
|
||||
addqueue(e, link) {
|
||||
//this.#queue.push(e, link);
|
||||
};
|
||||
|
||||
exec(cmd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_exec(cmd, { maxBuffer: 5e3 * 1024 }, (err, stdout, stderr) => {
|
||||
if(err)
|
||||
return reject(err);
|
||||
if(stderr)
|
||||
console.error(stderr);
|
||||
resolve({ stdout: stdout });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async genuuid() {
|
||||
return (await db`
|
||||
select gen_random_uuid() as uuid
|
||||
`)[0].uuid.substring(0, 8);
|
||||
};
|
||||
|
||||
async checkrepostlink(link) {
|
||||
const q_repost = await db`
|
||||
select id
|
||||
from "items"
|
||||
where src = ${link}
|
||||
`;
|
||||
return q_repost.length > 0 ? q_repost[0].id : false;
|
||||
};
|
||||
|
||||
async checkrepostsum(checksum) {
|
||||
const q_repost = await db`
|
||||
select id
|
||||
from "items"
|
||||
where checksum = ${checksum}
|
||||
`;
|
||||
return q_repost.length > 0 ? q_repost[0].id : false;
|
||||
};
|
||||
|
||||
async getItemID(filename) {
|
||||
return (await db`
|
||||
select *
|
||||
from "items"
|
||||
where dest = ${filename}
|
||||
limit 1
|
||||
`)[0].id;
|
||||
};
|
||||
|
||||
async genThumbnail(filename, mime, itemid, link) {
|
||||
if(mime.startsWith('video/') || mime == 'image/gif')
|
||||
await this.exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`);
|
||||
else if(mime.startsWith('image/') && mime != 'image/gif')
|
||||
await this.exec(`magick "./public/b/${filename}[0]" ./tmp/${itemid}.png`);
|
||||
else if(mime.startsWith('audio/')) {
|
||||
if(link.match(/soundcloud/)) {
|
||||
let cover = (await this.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --get-thumbnail "${link}"`)).stdout.trim();
|
||||
if(!cover.match(/default_avatar/)) {
|
||||
cover = cover.replace(/-(large|original)\./, '-t500x500.');
|
||||
try {
|
||||
await this.exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`);
|
||||
const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size;
|
||||
if(size >= 0) {
|
||||
await this.exec(`magick ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
|
||||
await this.exec(`magick ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
} catch(err) {}
|
||||
}
|
||||
else {
|
||||
await this.exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
|
||||
await this.exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await this.exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
|
||||
await this.exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
}
|
||||
|
||||
await this.exec(`magick "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`);
|
||||
await fs.promises.unlink(`./tmp/${itemid}.png`).catch(_=>{});
|
||||
await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(_=>{});
|
||||
return true;
|
||||
};
|
||||
|
||||
// tags
|
||||
async tagSFW(itemid) {
|
||||
return await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: itemid,
|
||||
tag_id: 1,
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
async tagNSFW(itemid) {
|
||||
return await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: itemid,
|
||||
tag_id: 2,
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
};
|
@ -7,12 +7,13 @@ import url from "url";
|
||||
const globalfilter = cfg.nsfp.map(n => `tag_id = ${n}`).join(' or ');
|
||||
|
||||
export default {
|
||||
getf0cks: async (o = { user, tag, mime, page, mode, fav, session }) => {
|
||||
getf0cks: async (o = { user, tag, mime, page, mode, fav, session, limit }) => {
|
||||
const user = o.user ? decodeURI(o.user) : null;
|
||||
const tag = lib.parseTag(o.tag ?? null);
|
||||
const mime = o.mime ?? null;
|
||||
const page = +(o.page ?? 1);
|
||||
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
|
||||
const eps = o.limit ?? cfg.websrv.eps;
|
||||
|
||||
const tmp = { user, tag, mime, smime, page, mode: o.mode };
|
||||
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
||||
@ -32,7 +33,7 @@ export default {
|
||||
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||
${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||
${ !o.session ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
group by items.id, tags.tag
|
||||
`)?.length || 0;
|
||||
|
||||
@ -66,11 +67,11 @@ export default {
|
||||
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||
${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||
${ !o.session ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
group by items.id, tags.tag, ta.tag_id
|
||||
order by items.id desc
|
||||
offset ${offset}
|
||||
limit ${cfg.websrv.eps}
|
||||
limit ${eps}
|
||||
`;
|
||||
|
||||
const cheat = [];
|
||||
@ -128,7 +129,7 @@ export default {
|
||||
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||
${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||
${ !o.session ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
group by items.id, tags.tag, ta.tag_id
|
||||
order by items.id desc
|
||||
`;
|
||||
@ -165,7 +166,7 @@ export default {
|
||||
success: true,
|
||||
user: {
|
||||
name: actitem.username,
|
||||
channel: actitem.usernetwork == "Telegram" && actitem.userchannel !== "f0ck.me" ? "anonymous" : actitem.userchannel,
|
||||
channel: actitem.usernetwork == "Telegram" && actitem.userchannel !== "w0bm.com" ? "anonymous" : actitem.userchannel,
|
||||
network: actitem.usernetwork
|
||||
},
|
||||
item: {
|
||||
@ -186,7 +187,7 @@ export default {
|
||||
favorites: favorites,
|
||||
tags: tags
|
||||
},
|
||||
title: `${actitem.id} - f0ck.me`,
|
||||
title: `${actitem.id} - w0bm.com`,
|
||||
pagination: {
|
||||
end: items[items.length - 1]?.id,
|
||||
start: items[0]?.id,
|
||||
@ -200,8 +201,7 @@ export default {
|
||||
tmp
|
||||
};
|
||||
return data;
|
||||
},
|
||||
getRandom: async (o = ({ user, tag, mime, mode, session })) => {
|
||||
},getRandom: async (o = ({ user, tag, mime, mode, fav, session })) => {
|
||||
const user = o.user ? decodeURI(o.user) : null;
|
||||
const tag = lib.parseTag(o.tag ?? null);
|
||||
const mime = (o.mime ?? "");
|
||||
@ -209,28 +209,45 @@ export default {
|
||||
|
||||
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
||||
|
||||
const item = await db`
|
||||
let item;
|
||||
|
||||
if (o.fav && user) {
|
||||
// Special case: random from user's favorites
|
||||
item = await db`
|
||||
select
|
||||
items.id
|
||||
from favorites
|
||||
inner join items on favorites.item_id = items.id
|
||||
inner join "user" on "user".id = favorites.user_id
|
||||
where
|
||||
"user".user ilike ${'%' + user + '%'}
|
||||
and items.active = 'true'
|
||||
${mime ? db`and items.mime ilike ${smime}` : db``}
|
||||
order by random()
|
||||
limit 1
|
||||
`;
|
||||
} else {
|
||||
// Normal random logic
|
||||
item = await db`
|
||||
select
|
||||
items.id
|
||||
from items
|
||||
left join tags_assign on tags_assign.item_id = items.id
|
||||
left join tags on tags.id = tags_assign.tag_id
|
||||
left join favorites on favorites.item_id = items.id
|
||||
left join "user" on "user".id = favorites.user_id
|
||||
where
|
||||
${ db.unsafe(modequery) }
|
||||
${db.unsafe(modequery)}
|
||||
and items.active = 'true'
|
||||
${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
|
||||
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||
${ user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||
${ !o.session ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
${tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db``}
|
||||
${user ? db`and items.username ilike ${'%' + user + '%'}` : db``}
|
||||
${mime ? db`and items.mime ilike ${smime}` : db``}
|
||||
${!o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db``}
|
||||
group by items.id, tags.tag
|
||||
order by random()
|
||||
limit 1
|
||||
`;
|
||||
}
|
||||
|
||||
if(item.length === 0) {
|
||||
if (item.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message: "no f0cks found :("
|
||||
@ -245,4 +262,4 @@ export default {
|
||||
itemid: item[0].id
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -3,21 +3,16 @@ import lib from "../lib.mjs";
|
||||
import { exec } from "child_process";
|
||||
import { promises as fs } from "fs";
|
||||
|
||||
const auth = async (req, res, next) => {
|
||||
if(!req.session) {
|
||||
export default (router, tpl) => {
|
||||
router.get(/^\/login(\/)?$/, async (req, res) => {
|
||||
if(req.cookies.session) {
|
||||
return res.reply({
|
||||
code: 401,
|
||||
body: "401 - Unauthorized"
|
||||
body: tpl.render('error', {
|
||||
message: "you're already logged in lol",
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
}
|
||||
return next();
|
||||
};
|
||||
|
||||
export default (router, tpl) => {
|
||||
|
||||
router.get(/^\/login(\/)?$/, async (req, res) => {
|
||||
if(req.cookies.session)
|
||||
return res.reply({ body: "du bist schon eingeloggt lol" });
|
||||
res.reply({
|
||||
body: tpl.render("login", { theme: req.cookies.theme ?? "f0ck" })
|
||||
});
|
||||
@ -66,7 +61,7 @@ export default (router, tpl) => {
|
||||
}).end();
|
||||
});
|
||||
|
||||
router.get(/^\/logout$/, auth, async (req, res) => {
|
||||
router.get(/^\/logout$/, lib.loggedin, async (req, res) => {
|
||||
const usersession = await db`
|
||||
select *
|
||||
from "user_sessions"
|
||||
@ -97,14 +92,18 @@ export default (router, tpl) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage
|
||||
router.get(/^\/admin(\/)?$/, lib.auth, async (req, res) => { // frontpage
|
||||
|
||||
res.reply({
|
||||
body: tpl.render("admin", { totals: await lib.countf0cks(), session: req.session }, req)
|
||||
body: tpl.render("admin", {
|
||||
totals: await lib.countf0cks(),
|
||||
session: req.session,
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/admin\/sessions(\/)?$/, auth, async (req, res) => {
|
||||
router.get(/^\/admin\/sessions(\/)?$/, lib.auth, async (req, res) => {
|
||||
const rows = await db`
|
||||
select "user_sessions".*, "user".user
|
||||
from "user_sessions"
|
||||
@ -116,76 +115,85 @@ export default (router, tpl) => {
|
||||
body: tpl.render("admin/sessions", {
|
||||
session: req.session,
|
||||
sessions: rows,
|
||||
totals: await lib.countf0cks()
|
||||
totals: await lib.countf0cks(),
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/admin\/log(\/)?$/, auth, async (req, res) => {
|
||||
exec("journalctl -qeu f0ck --no-pager", (err, stdout) => {
|
||||
res.reply({
|
||||
body: tpl.render("admin/log", {
|
||||
log: stdout.split("\n").slice(0, -1)
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
});
|
||||
// router.get(/^\/admin\/log(\/)?$/, lib.auth, async (req, res) => {
|
||||
// // Funktioniert ohne systemd service natürlich nicht.
|
||||
// exec("journalctl -qeu f0ck --no-pager", (err, stdout) => {
|
||||
// res.reply({
|
||||
// body: tpl.render("admin/log", {
|
||||
// log: stdout.split("\n").slice(0, -1),
|
||||
// tmp: null
|
||||
// }, req)
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
router.get(/^\/admin\/recover\/?/, auth, async (req, res) => {
|
||||
if(req.url.qs?.id) {
|
||||
const id = +req.url.qs.id;
|
||||
const f0ck = await db`
|
||||
select dest, mime
|
||||
from "items"
|
||||
where
|
||||
id = ${id} and
|
||||
active = 'false'
|
||||
limit 1
|
||||
`;
|
||||
if(f0ck.length === 0) {
|
||||
return res.reply({
|
||||
body: `f0ck ${id}: f0ck not found`
|
||||
});
|
||||
}
|
||||
// router.get(/^\/admin\/recover\/?/, lib.auth, async (req, res) => {
|
||||
// Gelöschte Objekte werden nicht aufgehoben.
|
||||
// if(req.url.qs?.id) {
|
||||
// const id = +req.url.qs.id;
|
||||
// const f0ck = await db`
|
||||
// select dest, mime
|
||||
// from "items"
|
||||
// where
|
||||
// id = ${id} and
|
||||
// active = 'false'
|
||||
// limit 1
|
||||
// `;
|
||||
// if(f0ck.length === 0) {
|
||||
// return res.reply({
|
||||
// body: `f0ck ${id}: f0ck not found`
|
||||
// });
|
||||
// }
|
||||
|
||||
await db`update "items" set active = 'true' where id = ${id}`;
|
||||
// await db`update "items" set active = 'true' where id = ${id}`;
|
||||
|
||||
await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{});
|
||||
await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{});
|
||||
// await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
// await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{});
|
||||
// await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
// await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{});
|
||||
|
||||
if(f0ck[0].mime.startsWith('audio')) {
|
||||
await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{});
|
||||
await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{});
|
||||
}
|
||||
// if(f0ck[0].mime.startsWith('audio')) {
|
||||
// await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{});
|
||||
// await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{});
|
||||
// }
|
||||
|
||||
return res.reply({
|
||||
body: `f0ck ${id} recovered. <a href="/admin/recover">back</a>`
|
||||
});
|
||||
}
|
||||
// return res.reply({
|
||||
// body: `f0ck ${id} recovered. <a href="/admin/recover">back</a>`
|
||||
// });
|
||||
// }
|
||||
|
||||
const _posts = await db`
|
||||
select id, mime, username
|
||||
from "items"
|
||||
where
|
||||
active = 'false'
|
||||
`;
|
||||
// const _posts = await db`
|
||||
// select id, mime, username
|
||||
// from "items"
|
||||
// where
|
||||
// active = 'false'
|
||||
// order by id desc
|
||||
// `;
|
||||
|
||||
if(_posts.length === 0) {
|
||||
return res.reply({
|
||||
body: 'blah'
|
||||
});
|
||||
}
|
||||
// if(_posts.length === 0) {
|
||||
// return res.reply({
|
||||
// body: 'blah'
|
||||
// });
|
||||
// }
|
||||
|
||||
const posts = await Promise.all(_posts.map(async p => ({ ...p, thumbnail: (await fs.readFile(`./deleted/t/${p.id}.webp`)).toString('base64') })));
|
||||
// const posts = await Promise.all(_posts.map(async p => ({
|
||||
// ...p,
|
||||
// thumbnail: (await fs.readFile(`./deleted/t/${p.id}.webp`)).toString('base64')
|
||||
// })));
|
||||
|
||||
res.reply({
|
||||
body: tpl.render('admin/recover', {
|
||||
posts
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
// res.reply({
|
||||
// body: tpl.render('admin/recover', {
|
||||
// posts,
|
||||
// tmp: null
|
||||
// }, req)
|
||||
// });
|
||||
// });
|
||||
|
||||
return router;
|
||||
};
|
||||
|
@ -139,7 +139,7 @@ export default router => {
|
||||
|
||||
// tags lol
|
||||
|
||||
group.put(/\/admin\/tags\/(?<tagname>.*)/, lib.auth, async (req, res) => {
|
||||
group.put(/\/admin\/tags\/(?<tagname>.*)/, lib.loggedin, async (req, res) => {
|
||||
if(!req.params.tagname || !req.post.newtag) {
|
||||
return res.json({
|
||||
success: false,
|
||||
@ -187,7 +187,7 @@ export default router => {
|
||||
return res.json(q, tagname === newtag ? 200 : 201); // created (modified)
|
||||
});
|
||||
|
||||
group.get(/\/admin\/tags\/suggest$/, lib.auth, async (req, res) => {
|
||||
group.get(/\/admin\/tags\/suggest$/, lib.loggedin, async (req, res) => {
|
||||
const reply = {
|
||||
success: false,
|
||||
suggestions: {}
|
||||
@ -267,7 +267,7 @@ export default router => {
|
||||
});
|
||||
});
|
||||
|
||||
group.post(/\/admin\/togglefav$/, lib.auth, async (req, res) => {
|
||||
group.post(/\/admin\/togglefav$/, lib.loggedin, async (req, res) => {
|
||||
const postid = +req.post.postid;
|
||||
|
||||
let favs = await db`
|
||||
|
@ -3,7 +3,7 @@ import lib from '../../lib.mjs';
|
||||
|
||||
export default router => {
|
||||
router.group(/^\/api\/v2\/settings/, group => {
|
||||
group.put(/\/setAvatar/, lib.auth, async (req, res) => {
|
||||
group.put(/\/setAvatar/, lib.loggedin, async (req, res) => {
|
||||
if(!req.post.avatar) {
|
||||
return res.json({
|
||||
msg: 'no avatar provided',
|
||||
|
@ -3,7 +3,7 @@ import lib from '../../lib.mjs';
|
||||
|
||||
export default router => {
|
||||
router.group(/^\/api\/v2\/admin\/(?<postid>\d+)\/tags/, group => {
|
||||
group.get(/$/, lib.auth, async (req, res) => {
|
||||
group.get(/$/, lib.loggedin, async (req, res) => {
|
||||
// get tags
|
||||
if(!req.params.postid) {
|
||||
return res.json({
|
||||
@ -18,7 +18,7 @@ export default router => {
|
||||
});
|
||||
});
|
||||
|
||||
group.post(/$/, lib.auth, async (req, res) => {
|
||||
group.post(/$/, lib.loggedin, async (req, res) => {
|
||||
// assign and/or create tag
|
||||
if(!req.params.postid || !req.post.tagname) {
|
||||
return res.json({
|
||||
@ -80,7 +80,7 @@ export default router => {
|
||||
});
|
||||
});
|
||||
|
||||
group.put(/\/toggle$/, lib.auth, async (req, res) => {
|
||||
group.put(/\/toggle$/, lib.loggedin, async (req, res) => {
|
||||
// xD
|
||||
if(!req.params.postid) {
|
||||
return res.json({
|
||||
@ -131,13 +131,13 @@ export default router => {
|
||||
const postid = +req.params.postid;
|
||||
const tagname = decodeURIComponent(req.params.tagname);
|
||||
|
||||
if(tagname == 'sfw' || tagname == 'nsfw' || tagname == 'hentai' || tagname == 'audio') {
|
||||
return res.json({
|
||||
success: false,
|
||||
msg: 'blacklisted',
|
||||
tags: await lib.getTags(postid)
|
||||
});
|
||||
}
|
||||
// if(tagname == 'sfw' || tagname == 'nsfw' || tagname == 'hentai' || tagname == 'audio') {
|
||||
// return res.json({
|
||||
// success: false,
|
||||
// msg: 'blacklisted',
|
||||
// tags: await lib.getTags(postid)
|
||||
// });
|
||||
// }
|
||||
|
||||
const tags = await lib.getTags(postid);
|
||||
|
||||
|
@ -14,7 +14,7 @@ export default (router, tpl) => {
|
||||
const user = decodeURIComponent(req.params.user);
|
||||
|
||||
const query = await db`
|
||||
select "user".user, user_options.*
|
||||
select "user".user, "user".admin, "user".created_at, user_options.*
|
||||
from user_options
|
||||
left join "user" on "user".id = user_options.user_id
|
||||
where "user".user ilike ${user}
|
||||
@ -31,27 +31,48 @@ export default (router, tpl) => {
|
||||
});
|
||||
}
|
||||
|
||||
const f0cks = await f0cklib.getf0cks({
|
||||
let f0cks, favs;
|
||||
const count = {
|
||||
f0cks: 0,
|
||||
favs: 0
|
||||
};
|
||||
try {
|
||||
f0cks = await f0cklib.getf0cks({
|
||||
user: user,
|
||||
mode: req.session.mode,
|
||||
fav: false,
|
||||
session: !!req.session
|
||||
session: !!req.session,
|
||||
limit: 99999999
|
||||
});
|
||||
const favs = await f0cklib.getf0cks({
|
||||
if('items' in f0cks) {
|
||||
count.f0cks = f0cks.items.length;
|
||||
f0cks.items = f0cks.items.slice(0, 50);
|
||||
}
|
||||
} catch(err) {
|
||||
f0cks = false;
|
||||
count.f0cks = 0;
|
||||
}
|
||||
try {
|
||||
favs = await f0cklib.getf0cks({
|
||||
user: user,
|
||||
mode: req.session.mode,
|
||||
fav: true,
|
||||
session: !!req.session
|
||||
session: !!req.session,
|
||||
limit: 99999999
|
||||
});
|
||||
|
||||
if('items' in f0cks)
|
||||
f0cks.items = f0cks.items.slice(0, 50);
|
||||
if('items' in favs)
|
||||
if('items' in favs) {
|
||||
count.favs = favs.items.length;
|
||||
favs.items = favs.items.slice(0, 50);
|
||||
}
|
||||
} catch(err) {
|
||||
favs = false;
|
||||
count.favs = 0;
|
||||
}
|
||||
|
||||
const data = {
|
||||
user: query[0],
|
||||
f0cks,
|
||||
count,
|
||||
favs,
|
||||
tmp: null
|
||||
};
|
||||
@ -90,7 +111,13 @@ export default (router, tpl) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/mode\/(\d)/, auth, async (req, res) => {
|
||||
router.get(/^\/(terms)$/, (req, res) => {
|
||||
res.reply({
|
||||
body: tpl.render(req.url.split[0], { tmp: null }, req)
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/mode\/(\d)/, lib.loggedin, async (req, res) => {
|
||||
const mode = +req.url.split[1];
|
||||
let referertmp = req.headers.referer;
|
||||
let referer = "";
|
||||
@ -118,46 +145,5 @@ export default (router, tpl) => {
|
||||
res.redirect(`/${referer}`);
|
||||
});
|
||||
|
||||
router.get(/^\/ranking$/, async (req, res) => {
|
||||
try {
|
||||
const list = await db`
|
||||
select
|
||||
"user".user,
|
||||
coalesce("user_options".avatar, ${await lib.getDefaultAvatar()}) as avatar,
|
||||
count(distinct(tag_id, item_id)) as count
|
||||
from "tags_assign"
|
||||
left join "user" on "user".id = "tags_assign".user_id
|
||||
left join "user_options" on "user_options".user_id = "user".id
|
||||
group by "user".user, "user_options".avatar
|
||||
order by count desc
|
||||
`;
|
||||
const stats = await lib.countf0cks();
|
||||
|
||||
const hoster = await db`
|
||||
with t as (
|
||||
select
|
||||
split_part(substring(src, position('//' in src)+2), '/', 1) part
|
||||
from items
|
||||
)
|
||||
select t.part, count(t.part) as c
|
||||
from t
|
||||
group by t.part
|
||||
order by c desc
|
||||
limit 20
|
||||
`;
|
||||
|
||||
res.reply({
|
||||
body: tpl.render('ranking', {
|
||||
list,
|
||||
stats,
|
||||
hoster,
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
} catch(err) {
|
||||
res.end(JSON.stringify(err.message));
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
|
26
src/inc/routes/picdump.mjs
Normal file
@ -0,0 +1,26 @@
|
||||
import db from "../../inc/sql.mjs";
|
||||
import cfg from "../../inc/config.mjs";
|
||||
import f0cklib from "../routeinc/f0cklib.mjs";
|
||||
|
||||
export default (router, tpl) => {
|
||||
router.get(/^\/picdump$/, async (req, res) => {
|
||||
const dump = await db`
|
||||
SELECT *
|
||||
FROM items
|
||||
WHERE (
|
||||
to_timestamp(stamp) >= date_trunc('week', CURRENT_TIMESTAMP - interval '1 week') AND
|
||||
to_timestamp(stamp) < date_trunc('week', CURRENT_TIMESTAMP)
|
||||
) AND
|
||||
mime LIKE 'image/%'
|
||||
ORDER BY stamp DESC
|
||||
`;
|
||||
|
||||
res.reply({
|
||||
body: tpl.render('picdump', {
|
||||
dump,
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
return router;
|
||||
};
|
@ -6,10 +6,14 @@ export default (router, tpl) => {
|
||||
let referer = req.headers.referer ?? '';
|
||||
let opts = {};
|
||||
|
||||
if(referer.match(new RegExp(cfg.main.url.regex))) { // parse referer
|
||||
if (referer.match(new RegExp(cfg.main.url.regex))) { // parse referer
|
||||
referer = referer.split(cfg.main.url.domain)[1];
|
||||
console.log("referer: ", referer);
|
||||
|
||||
const tmp = referer.match(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/);
|
||||
if(tmp)
|
||||
console.log("tmp: ", tmp);
|
||||
|
||||
if (tmp && tmp.groups)
|
||||
opts = tmp.groups;
|
||||
}
|
||||
|
||||
@ -18,12 +22,14 @@ export default (router, tpl) => {
|
||||
tag: opts.tag,
|
||||
mime: opts.mime,
|
||||
page: opts.page,
|
||||
fav: opts.mode == 'favs',
|
||||
fav: opts.mode === 'favs',
|
||||
mode: req.session.mode,
|
||||
session: !!req.session
|
||||
});
|
||||
|
||||
if(!data.success) {
|
||||
console.log("data", data);
|
||||
|
||||
if (!data.success) {
|
||||
return res.reply({
|
||||
code: 404,
|
||||
body: tpl.render('error', {
|
||||
@ -33,7 +39,8 @@ export default (router, tpl) => {
|
||||
});
|
||||
}
|
||||
|
||||
res.redirect(`${data.link.main}${data.link.path}${data.itemid}`);
|
||||
res.redirect(encodeURI(`${data.link.main}${data.link.path}${data.itemid}`));
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
97
src/inc/routes/ranking.mjs
Normal file
@ -0,0 +1,97 @@
|
||||
import db from "../../inc/sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
import config from "../config.mjs";
|
||||
import fetch from "flumm-fetch";
|
||||
|
||||
export default (router, tpl) => {
|
||||
router.get(/^\/ranking$/, async (req, res) => {
|
||||
try {
|
||||
const list = await db`
|
||||
select
|
||||
"user".user, "user".admin,
|
||||
coalesce("user_options".avatar, ${await lib.getDefaultAvatar()}) as avatar,
|
||||
count(distinct(tag_id, item_id)) as count
|
||||
from "tags_assign"
|
||||
left join "user" on "user".id = "tags_assign".user_id
|
||||
left join "user_options" on "user_options".user_id = "user".id
|
||||
group by "user".user, "user_options".avatar, "user".admin
|
||||
order by count desc
|
||||
`;
|
||||
const stats = await lib.countf0cks();
|
||||
|
||||
const hoster = await db`
|
||||
with t as (
|
||||
select
|
||||
split_part(substring(src, position('//' in src)+2), '/', 1) part
|
||||
from items
|
||||
)
|
||||
select t.part, count(t.part) as c
|
||||
from t
|
||||
group by t.part
|
||||
order by c desc
|
||||
limit 20
|
||||
`;
|
||||
|
||||
const favotop = await db`
|
||||
select item_id, count(*) favs
|
||||
from favorites
|
||||
group by item_id
|
||||
having count(*) > 1
|
||||
order by favs desc
|
||||
limit 10
|
||||
`;
|
||||
|
||||
res.reply({
|
||||
body: tpl.render('ranking', {
|
||||
list,
|
||||
stats,
|
||||
hoster,
|
||||
favotop,
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
} catch(err) {
|
||||
res.end(JSON.stringify(err.message));
|
||||
}
|
||||
});
|
||||
|
||||
router.get(/^\/top10$/, async (req, res) => {
|
||||
const d = new Date();
|
||||
d.setMonth(d.getMonth() - 1);
|
||||
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||
const year = d.getFullYear();
|
||||
|
||||
const url = `${config.apis.stats.url1}/${year}-${month}/${config.apis.stats.url2}`;
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: config.apis.stats.auth
|
||||
}
|
||||
};
|
||||
|
||||
const topres = await (await fetch(url, options)).text();
|
||||
const list = topres.match(/(f0ck.me\/b\/)(?<link>.{8}\.\w*)/g).slice(0, 10).map(e => e.split('/')[2]);
|
||||
|
||||
const f0cks = [];
|
||||
for(const l of list) {
|
||||
const f = await db`
|
||||
select id, username
|
||||
from items
|
||||
where dest = ${l}
|
||||
limit 1
|
||||
`;
|
||||
f0cks.push(f[0]);
|
||||
}
|
||||
|
||||
res.reply({
|
||||
body: tpl.render('top10', {
|
||||
f0cks,
|
||||
year,
|
||||
month,
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
@ -5,7 +5,7 @@ import search from "../routeinc/search.mjs";
|
||||
const _eps = 20;
|
||||
|
||||
export default (router, tpl) => {
|
||||
router.get(/^\/search(\/)?$/, lib.auth, async (req, res) => {
|
||||
router.get(/^\/search(\/)?$/, lib.loggedin, async (req, res) => {
|
||||
let ret;
|
||||
let tag = req.url.qs.tag ?? [];
|
||||
let page = req.url.qs.page ?? 1;
|
||||
|
@ -12,5 +12,19 @@ export default (router, tpl) => {
|
||||
"Location": req.headers.referer ?? "/"
|
||||
}).end();
|
||||
});
|
||||
|
||||
router.get(/^\/tfull\//, async (req, res) => {
|
||||
let full = req.session.fullscreen;
|
||||
if(full == 1)
|
||||
full = 0;
|
||||
else
|
||||
full = 1;
|
||||
|
||||
return res.writeHead(301, {
|
||||
"Cache-Control": "no-cache, public",
|
||||
"Set-Cookie": `theme=${full}; Path=/`,
|
||||
"Location": req.headers.referer ?? "/"
|
||||
}).end();
|
||||
});
|
||||
return router;
|
||||
};
|
||||
|
42
src/inc/routes/toptags.mjs
Normal file
@ -0,0 +1,42 @@
|
||||
import db from "../../inc/sql.mjs";
|
||||
import cfg from "../../inc/config.mjs";
|
||||
|
||||
export default (router, tpl) => {
|
||||
router.get(/^\/tags$/, async (req, res) => {
|
||||
|
||||
const phrase = cfg.websrv.phrases[~~(Math.random() * cfg.websrv.phrases.length)];
|
||||
|
||||
const nsfp = cfg.nsfp.map(n => `${n}`);
|
||||
|
||||
const toptags = await db`
|
||||
SELECT t.id, t.tag, COUNT(DISTINCT ta.item_id) AS total_items
|
||||
FROM tags t
|
||||
LEFT JOIN tags_assign ta ON t.id = ta.tag_id
|
||||
WHERE t.id not in (${db.unsafe(nsfp)})
|
||||
GROUP BY t.id, t.tag
|
||||
ORDER BY total_items DESC
|
||||
LIMIT 500
|
||||
;
|
||||
`;
|
||||
|
||||
const toptags_regged = await db`
|
||||
SELECT t.id, t.tag, COUNT(DISTINCT ta.item_id) AS total_items
|
||||
FROM tags t
|
||||
LEFT JOIN tags_assign ta ON t.id = ta.tag_id
|
||||
GROUP BY t.id, t.tag
|
||||
ORDER BY total_items DESC
|
||||
LIMIT 500
|
||||
;
|
||||
`;
|
||||
|
||||
res.reply({
|
||||
body: tpl.render('tags', {
|
||||
toptags,
|
||||
toptags_regged,
|
||||
phrase,
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
return router;
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
import { getLevel } from "../admin.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
import fetch from "flumm-fetch";
|
||||
import vm from "vm";
|
||||
|
||||
@ -7,7 +8,9 @@ let context = vm.createContext({
|
||||
e: null,
|
||||
bot: null,
|
||||
admins: null,
|
||||
fetch: fetch,
|
||||
fetch,
|
||||
lib,
|
||||
console,
|
||||
|
||||
a: null,
|
||||
resolve: null
|
||||
@ -19,16 +22,16 @@ export default async bot => {
|
||||
name: "level",
|
||||
call: /^!level (.*)/i,
|
||||
active: true,
|
||||
f: e => {
|
||||
f: async e => {
|
||||
const user = e.message.trim().substring(7);
|
||||
e.reply( JSON.stringify( getLevel( e.self.user.get(user) || {} ) ) );
|
||||
await e.reply( JSON.stringify( getLevel( e.self.user.get(user) || {} ) ) );
|
||||
}
|
||||
}, {
|
||||
name: "self",
|
||||
call: /^!self$/i,
|
||||
active: true,
|
||||
f: e => {
|
||||
e.reply( JSON.stringify( e.user ) );
|
||||
f: async e => {
|
||||
await e.reply( JSON.stringify( e.user ) );
|
||||
}
|
||||
}, {
|
||||
name: "sandbox_debug",
|
||||
@ -41,6 +44,7 @@ export default async bot => {
|
||||
context.e = e;
|
||||
context.bot = bot;
|
||||
context.level = getLevel;
|
||||
context.hasTag = lib.hasTag;
|
||||
context.a = null;
|
||||
|
||||
await new Promise(resolve => {
|
||||
@ -52,9 +56,9 @@ export default async bot => {
|
||||
|
||||
let output = JSON.stringify(context.a);
|
||||
if(output.length > maxoutput)
|
||||
return e.reply(`fuggg, Ausgabe wäre viel zu lang! (${output.length} Zeichen :DDDDDD)`);
|
||||
return await e.reply(`fuggg, Ausgabe wäre viel zu lang! (${output.length} Zeichen :DDDDDD)`);
|
||||
else
|
||||
return e.reply(output);
|
||||
return await e.reply(output);
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ export default async bot => {
|
||||
const level = getLevel(e.user).level;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
e.reply(`f0ck ${id}: f0ck not found`);
|
||||
await e.reply(`f0ck ${id}: f0ck not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -37,12 +37,12 @@ export default async bot => {
|
||||
f0ck[0].usernetwork !== e.network) &&
|
||||
level < 100
|
||||
) {
|
||||
e.reply(`f0ck ${id}: insufficient permissions`);
|
||||
await e.reply(`f0ck ${id}: insufficient permissions`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(~~(new Date() / 1e3) >= (f0ck[0].stamp + 600) && level < 100) {
|
||||
e.reply(`f0ck ${id}: too late lol`);
|
||||
await e.reply(`f0ck ${id}: too late lol`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ export default async bot => {
|
||||
deleted.push(id);
|
||||
}
|
||||
|
||||
e.reply(`deleted ${deleted.length}/${e.args.length} f0cks (${deleted.join(",")})`);
|
||||
await e.reply(`deleted ${deleted.length}/${e.args.length} f0cks (${deleted.join(",")})`);
|
||||
}
|
||||
}, {
|
||||
name: "recover",
|
||||
@ -86,7 +86,7 @@ export default async bot => {
|
||||
`;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
e.reply(`f0ck ${id}: f0ck not found`);
|
||||
await e.reply(`f0ck ${id}: f0ck not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ export default async bot => {
|
||||
recovered.push(id);
|
||||
}
|
||||
|
||||
e.reply(`recovered ${recovered.length}/${e.args.length} f0cks (${recovered.join(",")})`);
|
||||
await e.reply(`recovered ${recovered.length}/${e.args.length} f0cks (${recovered.join(",")})`);
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
@ -5,29 +5,6 @@ import cfg from "../config.mjs";
|
||||
import db from "../sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
|
||||
/*const cleanTags = async () => {
|
||||
const tags = await db`
|
||||
select *
|
||||
from "tags"
|
||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
||||
where "tags_assign".item_id is null
|
||||
`;
|
||||
|
||||
if(tags.length === 0)
|
||||
return 0;
|
||||
|
||||
let deleteTag = sql("tags");
|
||||
let dtags = 0;
|
||||
tags.forEach(tag => {
|
||||
if(["sfw", "nsfw"].includes(tag.tag.toLowerCase()))
|
||||
return dtags;
|
||||
deleteTag = deleteTag.orWhere("id", tag.id);
|
||||
dtags++;
|
||||
});
|
||||
await deleteTag.del();
|
||||
return dtags;
|
||||
};*/
|
||||
|
||||
export default async bot => {
|
||||
|
||||
return [{
|
||||
@ -48,60 +25,56 @@ export default async bot => {
|
||||
t: lib.formatSize((await Promise.all(dirs.t.map( async file => (await fs.stat(`./public/t/${file}`)).size)) ).reduce((a, b) => b + a)),
|
||||
ca: lib.formatSize((await Promise.all(dirs.ca.map(async file => (await fs.stat(`./public/ca/${file}`)).size))).reduce((a, b) => b + a)),
|
||||
};
|
||||
return e.reply(`${dirs.b.length} f0cks: ${sizes.b}, ${dirs.t.length} thumbnails: ${sizes.t}, ${dirs.ca.length} coverarts: ${sizes.ca}`);
|
||||
return await e.reply(`${dirs.b.length} f0cks: ${sizes.b}, ${dirs.t.length} thumbnails: ${sizes.t}, ${dirs.ca.length} coverarts: ${sizes.ca}`);
|
||||
case "limit":
|
||||
return e.reply(`up to ${lib.formatSize(cfg.main.maxfilesize)} (${lib.formatSize(cfg.main.maxfilesize * cfg.main.adminmultiplier)} for admins)`);
|
||||
return await e.reply(`up to ${lib.formatSize(cfg.main.maxfilesize)} (${lib.formatSize(cfg.main.maxfilesize * cfg.main.adminmultiplier)} for admins)`);
|
||||
case "thumb":
|
||||
const rows = await db`
|
||||
select id
|
||||
from "items"
|
||||
`;
|
||||
const dir = (await fs.readdir("./public/t")).filter(d => d.endsWith(".png")).map(e => +e.split(".")[0]);
|
||||
const dir = (await fs.readdir("./public/t")).filter(d => d.endsWith(".webp")).map(e => +e.split(".")[0]);
|
||||
const tmp = [];
|
||||
for(let row of rows)
|
||||
!dir.includes(row.id) ? tmp.push(row.id) : null;
|
||||
e.reply(`${tmp.length}, ${rows.length}, ${dir.length}`);
|
||||
await e.reply(`${tmp.length}, ${rows.length}, ${dir.length}`);
|
||||
break;
|
||||
case "cache":
|
||||
cfg.websrv.cache = !cfg.websrv.cache;
|
||||
return e.reply(`Cache is ${cfg.websrv.cache ? "enabled" : "disabled"}`);
|
||||
return await e.reply(`Cache is ${cfg.websrv.cache ? "enabled" : "disabled"}`);
|
||||
case "uptime":
|
||||
exec('sudo systemctl status f0ck', (err, stdout) => {
|
||||
exec('sudo systemctl status f0ck', async (err, stdout) => {
|
||||
if(!err)
|
||||
return e.reply(stdout.split('\n')[2].trim().replace("Active: active (running)", "i'm active"));
|
||||
return await e.reply(stdout.split('\n')[2].trim().replace("Active: active (running)", "i'm active"));
|
||||
});
|
||||
break;
|
||||
case "restart":
|
||||
e.reply("hay hay patron, hemen!");
|
||||
await e.reply("hay hay patron, hemen!");
|
||||
exec("sudo systemctl restart f0ck");
|
||||
break;
|
||||
/*case "cleanTags":
|
||||
const tags = await cleanTags();
|
||||
e.reply(tags + " tags removed");
|
||||
break;*/
|
||||
case "clearTmp":
|
||||
await Promise.all((await fs.readdir("./tmp")).filter(d => d !== ".empty").map(async d => fs.unlink(`./tmp/${d}`)));
|
||||
e.reply("cleared lol");
|
||||
await e.reply("cleared lol");
|
||||
break;
|
||||
case "status":
|
||||
const tmpc = await lib.countf0cks();
|
||||
e.reply(`tagged: ${tmpc.tagged}; untagged: ${tmpc.untagged}; sfw: ${tmpc.sfw}; nsfw: ${tmpc.nsfw}; total: ${tmpc.total}`);
|
||||
await e.reply(`tagged: ${tmpc.tagged}; untagged: ${tmpc.untagged}; sfw: ${tmpc.sfw}; nsfw: ${tmpc.nsfw}; total: ${tmpc.total}`);
|
||||
break;
|
||||
case "autotagger":
|
||||
/*case "autotagger":
|
||||
const body = { headers: { Authorization: `Basic ${cfg.tagger.btoa}` } };
|
||||
const res = await (await fetch(`${cfg.tagger.endpoint}/usage`, body)).json();
|
||||
if(res) {
|
||||
const processed = res.result.monthly_processed;
|
||||
const limit = res.result.monthly_limit;
|
||||
return e.reply(`autotagger: usage/limit: ${processed}/${limit}`);
|
||||
return await e.reply(`autotagger: usage/limit: ${processed}/${limit}`);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
break;*/
|
||||
/*case "renameTag":
|
||||
const origTag = e.args.slice(1).join(' ');
|
||||
|
||||
if(origTag.length <= 1)
|
||||
return e.reply("absichtliche Provokation!");
|
||||
return await e.reply("absichtliche Provokation!");
|
||||
|
||||
const origTagID = (await sql('tags').where('tag', origTag))[0].id;
|
||||
|
||||
@ -116,10 +89,10 @@ export default async bot => {
|
||||
.del()
|
||||
);
|
||||
|
||||
e.reply(JSON.stringify({ affected, deleted }));
|
||||
await e.reply(JSON.stringify({ affected, deleted }));
|
||||
break;*/
|
||||
case "help":
|
||||
e.reply("cmds: stats, limit, thumb, cache, uptime, restart, cleanTags, clearTmp, status");
|
||||
await e.reply("cmds: stats, limit, thumb, cache, uptime, restart, cleanTags, clearTmp, status");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -2,7 +2,7 @@ import cfg from "../config.mjs";
|
||||
import db from "../sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
|
||||
const regex = /(https\:\/\/f0ck\.me|http\:\/\/fockmoonsb24iczs7odozzy5uktlzbcgp337nabrgffzxv5ihabgpvyd\.onion)(\/(video|image|audio))?\/(\d+|(?:b\/)(\w{8})\.(jpg|webm|gif|mp4|png|mov|mp3|ogg|flac))/gi;
|
||||
const regex = /(https\:\/\/w0bm\.com)(\/(video|image|audio))?\/(\d+|(?:b\/)(\w{8})\.(jpg|webm|gif|mp4|png|mov|mp3|ogg|flac))/gi;
|
||||
|
||||
export default async bot => {
|
||||
|
||||
@ -22,9 +22,9 @@ export default async bot => {
|
||||
`;
|
||||
|
||||
if(rows.length === 0)
|
||||
return e.reply("no f0cks given! lol D:");
|
||||
return await e.reply("no f0cks given! lol D:");
|
||||
|
||||
e.reply([
|
||||
await e.reply([
|
||||
`${cfg.main.url.full}/${rows[0].id}`,
|
||||
`user: ${rows[0].username} @ ${rows[0].usernetwork} ${rows[0].userchannel}`,
|
||||
`~${lib.formatSize(rows[0].size)}`,
|
||||
|
@ -10,6 +10,7 @@ export default async bot => {
|
||||
active: false,
|
||||
f: async e => {
|
||||
let args = e.args.slice(1);
|
||||
|
||||
/*let rows = sql("items").select("id", "username", "mime", "size");
|
||||
|
||||
for(let i = 0; i < args.length; i++) {
|
||||
@ -21,10 +22,21 @@ export default async bot => {
|
||||
|
||||
rows = await rows.orderByRaw("random()").limit(1);*/
|
||||
|
||||
if(rows.length === 0)
|
||||
return e.reply("nothing found, f0cker");
|
||||
const rows = await db`
|
||||
select id, mime, username, size
|
||||
from "items"
|
||||
where
|
||||
${ args.map(a => a.charAt(0) === "!"
|
||||
? db`username not ilike ${a.slice(1)}`
|
||||
: db`username ilike ${a}`
|
||||
).join(' and ')}
|
||||
order by random()
|
||||
`;
|
||||
|
||||
return e.reply(`f0ckrnd: ${cfg.main.url.full}/${rows[0].id} by: ${rows[0].username} (${rows[0].mime}, ~${lib.formatSize(rows[0].size)})`);
|
||||
if(rows.length === 0)
|
||||
return await e.reply("nothing found, f0cker");
|
||||
|
||||
return await e.reply(`f0ckrnd: ${cfg.main.url.full}/${rows[0].id} by: ${rows[0].username} (${rows[0].mime}, ~${lib.formatSize(rows[0].size)})`);
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
@ -2,128 +2,240 @@ import cfg from "../config.mjs";
|
||||
import db from "../sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
import { getLevel } from "../admin.mjs";
|
||||
import queue from "../queue.mjs";
|
||||
import autotagger from "../autotagger.mjs";
|
||||
import fetch from "flumm-fetch";
|
||||
|
||||
import fs from "fs";
|
||||
import { exec as _exec } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
const exec = cmd => new Promise((resolve, reject) => {
|
||||
_exec(cmd, { maxBuffer: 5e3 * 1024 }, (err, stdout, stderr) => {
|
||||
if(err)
|
||||
return reject(err);
|
||||
if(stderr)
|
||||
console.error(stderr);
|
||||
resolve({ stdout: stdout });
|
||||
});
|
||||
});
|
||||
|
||||
//const regex = /https?:\/\/[\w\S(\.|:|/)]+/gi;
|
||||
const regex = /https?:\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gi;
|
||||
const regex = {
|
||||
all: /https?:\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gi,
|
||||
yt: /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\/?\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})/gi,
|
||||
imgur: /(?:https?:)?\/\/(\w+\.)?imgur\.com\/(\S*)(\..{3,4})/i,
|
||||
instagram: /(?:https?:\/\/www\.)?instagram\.com\S*?\/(?:p|reel)\/(\w{11})\/?/im
|
||||
};
|
||||
const mediagroupids = new Set();
|
||||
|
||||
export default async bot => {
|
||||
|
||||
return [{
|
||||
name: "parser",
|
||||
call: regex,
|
||||
call: regex.all,
|
||||
active: true,
|
||||
f: e => {
|
||||
const links = e.message.match(regex)?.filter(link => !link.includes("f0ck.me")) || [];
|
||||
|
||||
const links = e.message.match(regex.all)?.filter(link => !link.includes(cfg.main.url.domain)) || [];
|
||||
let repost;
|
||||
if(e.media)
|
||||
links.push(e.media);
|
||||
|
||||
if(links.length === 0)
|
||||
return false;
|
||||
|
||||
if(e.message.match(/(!|-)ignore/))
|
||||
if(e.message.match(/\!i(gnore)?\b/))
|
||||
return false;
|
||||
|
||||
if(!e.channel.includes("f0ck") && !e.message.match(/(!|-)f0ck/i))
|
||||
if(!e.channel.includes("f0bm") && (!e.message.match(/\!f(0ck)?\b/i) && (typeof e.raw.forward_from == 'undefined')))
|
||||
return false;
|
||||
|
||||
if(e.type === 'tg' && // proto: tg
|
||||
!e.message.match(/\!f(0ck)?\b/i) && // !f / !f0ck
|
||||
!e.raw.forward_date && // is forwarded?
|
||||
!mediagroupids.has(e.raw.media_group_id) // prepared mediagroup?
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
else if(e.raw.media_group_id && e.message.match(/\!f(0ck)?\b/i)) {
|
||||
mediagroupids.add(e.raw.media_group_id);
|
||||
}
|
||||
|
||||
console.log(`parsing ${links.length} link${links.length > 1 ? "s" : ""}...`);
|
||||
|
||||
links.forEach(async link => {
|
||||
//if(regex.imgur.test(link))
|
||||
// return await e.reply(`fuck imgur... seriously`);
|
||||
|
||||
if(regex.instagram.test(link))
|
||||
await e.reply(`instagay`);
|
||||
|
||||
if(regex.yt.test(link))
|
||||
await e.reply(`hi yt here, I am gay this is why I am not giving you the file`);
|
||||
|
||||
// check repost (link)
|
||||
const q_repost = await db`
|
||||
select id
|
||||
from "items"
|
||||
where src = ${link}
|
||||
`;
|
||||
if(q_repost.length > 0)
|
||||
return e.reply(`repost motherf0cker (link): ${cfg.main.url.full}/${q_repost[0].id}`);
|
||||
repost = await queue.checkrepostlink(link);
|
||||
if(repost)
|
||||
return await e.reply(`repost motherf0cker (link): ${cfg.main.url.full}/${repost}`);
|
||||
|
||||
// generate uuid
|
||||
const uuid = (await db`
|
||||
select gen_random_uuid() as uuid
|
||||
`)[0].uuid.substring(0, 8);
|
||||
const uuid = await queue.genuuid();
|
||||
|
||||
const maxfilesize = (getLevel(e.user).level > 50 ? cfg.main.maxfilesize * cfg.main.adminmultiplier : cfg.main.maxfilesize) / 1024;
|
||||
const maxfilesize = (getLevel(e.user).level > 50 ? cfg.main.maxfilesize * cfg.main.adminmultiplier : cfg.main.maxfilesize);
|
||||
|
||||
let meta;
|
||||
// read metadata
|
||||
let ext;
|
||||
if(link.match(regex.instagram)) {
|
||||
// is instagram
|
||||
try {
|
||||
meta = JSON.parse((await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
|
||||
}
|
||||
catch(err) {
|
||||
//e.reply("[error] f0ck has no bock :(");
|
||||
//console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Object.values(cfg.mimes).includes(meta.ext.toLowerCase())) {
|
||||
// @flummi -> is there a variable for the actual work directory so it doesn't have to be hardcoded?
|
||||
const meta = JSON.parse((await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
|
||||
ext = meta.ext;
|
||||
} catch(err) {
|
||||
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
||||
if(!Object.keys(cfg.mimes).includes(tmphead))
|
||||
return;
|
||||
meta.ext = cfg.mimes[tmphead];
|
||||
// this can be undefined for unsupported mime types, but will be caught in the general mime check below
|
||||
ext = cfg.mimes[tmphead];
|
||||
}
|
||||
|
||||
let filename = `${uuid}.${meta.ext}`;
|
||||
|
||||
e.reply(`[charging the f0cker] downloading: ${uuid}`);
|
||||
|
||||
// download data
|
||||
const start = new Date();
|
||||
let source;
|
||||
if(meta.ext === "mp4") {
|
||||
source = (await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize}k --merge-output-format mp4 -o ./tmp/${filename}`)).stdout.trim();
|
||||
//change 720 to any other available resolution, higher = better quality but bigger filesize
|
||||
}
|
||||
else if(link.match(regex.imgur)) {
|
||||
// imghure
|
||||
ext = link.split('.').pop();
|
||||
}
|
||||
else {
|
||||
source = (await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize}k -o ./tmp/${filename}`)).stdout.trim();
|
||||
//change 720 to any other available resolution, higher = better quality but bigger filesize
|
||||
// is not instagram
|
||||
try {
|
||||
const meta = JSON.parse((await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
|
||||
ext = meta.ext;
|
||||
} catch(err) {
|
||||
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
||||
// this can be undefined for unsupported mime types, but will be caught in the general mime check below
|
||||
ext = cfg.mimes[tmphead];
|
||||
}
|
||||
}
|
||||
|
||||
if(source.match(/larger than/))
|
||||
return e.reply("too large lol");
|
||||
if(!Object.values(cfg.mimes).includes(ext?.toLowerCase())) {
|
||||
return console.log('mime schmime ' + ext);
|
||||
}
|
||||
|
||||
const msg = await e.reply(`[charging the f0cker] downloading: ${uuid}`, {
|
||||
disable_notification: true
|
||||
});
|
||||
|
||||
// <download data>
|
||||
const start = new Date();
|
||||
let source;
|
||||
|
||||
if(link.match(regex.instagram)) {
|
||||
try {
|
||||
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited
|
||||
source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
|
||||
} catch(err) {
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "instagram dl error");
|
||||
return await e.reply("instagram dl error", err);
|
||||
}
|
||||
}
|
||||
else if(link.match(regex.imgur)) {
|
||||
// imghure via torsocks
|
||||
try {
|
||||
await queue.exec(`torsocks wget ${link} -O ./tmp/${uuid}.${ext}`);
|
||||
source = `./tmp/${uuid}.${ext}`;
|
||||
} catch(err) {
|
||||
console.error('err:', err);
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, err);
|
||||
return await e.reply('something went wrong lol');
|
||||
}
|
||||
}
|
||||
else if(link.match(regex.yt)) {
|
||||
try {
|
||||
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited
|
||||
source = (await queue.exec(`yt-dlp --cookies /tmp/cookies.txt -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
|
||||
} catch(err) {
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "yt dl error");
|
||||
return await e.reply("yt dl error", err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
|
||||
} catch(err) {
|
||||
console.error('err:', err);
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, err);
|
||||
return await e.reply('something went wrong lol / check maxfilesize?');
|
||||
}
|
||||
}
|
||||
// </download data>
|
||||
|
||||
if(!source) {
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "something went wrong lol");
|
||||
return await e.reply("something went wrong lol");
|
||||
}
|
||||
|
||||
if(source.match(/larger than/)) {
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "too large lol");
|
||||
return await e.reply("too large lol");
|
||||
}
|
||||
const end = ~~((new Date() - start) / 1e3);
|
||||
|
||||
// generate checksum
|
||||
const checksum = (await exec(`sha256sum ./tmp/${filename}`)).stdout.trim().split(" ")[0];
|
||||
const size = fs.statSync(`./tmp/${filename}`).size;
|
||||
|
||||
// mime check
|
||||
const mime = (await exec(`file --mime-type -b ./tmp/${filename}`)).stdout.trim();
|
||||
if(!Object.keys(cfg.mimes).includes(mime))
|
||||
return e.reply(`lol, go f0ck yourself (${mime})`);
|
||||
|
||||
if(!Object.values(cfg.mimes).includes(meta.ext.toLowerCase())) {
|
||||
let tmpext = cfg.mimes[meta.ext.toLowerCase()];
|
||||
fs.renameSync(`./tmp/${filename}`, `./tmp/${uuid}.${tmpext}`);
|
||||
filename = `${uuid}.${tmpext}`;
|
||||
// filesize check
|
||||
const size = fs.statSync(source).size;
|
||||
if(size > maxfilesize) {
|
||||
await fs.promises.unlink(source).catch(_=>{});
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `too large lol. (${lib.formatSize(size)} / ${lib.formatSize(maxfilesize)})`);
|
||||
return await e.reply(`too large lol. (${lib.formatSize(size)} / ${lib.formatSize(maxfilesize)})`);
|
||||
}
|
||||
|
||||
// check repost (checksum)
|
||||
const q_repostc = await db`
|
||||
select id
|
||||
from "items"
|
||||
where checksum = ${checksum}
|
||||
`;
|
||||
if(q_repostc.length > 0)
|
||||
return e.reply(`repost motherf0cker (checksum): ${cfg.main.url.full}/${q_repostc[0].id}`);
|
||||
// mime check
|
||||
let mime = (await queue.exec(`file --mime-type -b ${source}`)).stdout.trim();
|
||||
try {
|
||||
if(mime == 'video/x-matroska') { // mkv failsafe
|
||||
await queue.exec(`ffmpeg -i ./tmp/${uuid}.mkv -codec copy ./tmp/${uuid}.mp4`);
|
||||
await fs.promises.unlink(source).catch(_=>{});
|
||||
source = source.replace(/\.mkv$/, '.mp4');
|
||||
mime = 'video/mp4';
|
||||
}
|
||||
if(source.match(/\.opus$/)) { // opus failsafe
|
||||
await queue.exec(`ffmpeg -i ./tmp/${uuid}.opus -codec copy ./tmp/${uuid}.ogg`);
|
||||
await fs.promises.unlink(source);
|
||||
source = source.replace(/\.opus$/, '.ogg');
|
||||
mime = 'audio/ogg';
|
||||
}
|
||||
} catch(err) {
|
||||
await fs.promises.unlink(source).catch(_=>{});
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "something went wrong lol");
|
||||
return await e.reply("something went wrong lol");
|
||||
}
|
||||
|
||||
await fs.promises.copyFile(`./tmp/${filename}`, `./public/b/${filename}`);
|
||||
await fs.promises.unlink(`./tmp/${filename}`).catch(_=>{});
|
||||
if(!Object.keys(cfg.mimes).includes(mime)) {
|
||||
await fs.promises.unlink(source).catch(_=>{});
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `lol, go f0ck yourself (${mime})`);
|
||||
return await e.reply(`lol, go f0ck yourself (${mime})`);
|
||||
}
|
||||
|
||||
// generate checksum
|
||||
const checksum = (await queue.exec(`sha256sum ${source}`)).stdout.trim().split(" ")[0];
|
||||
|
||||
// check repost (checksum)
|
||||
repost = await queue.checkrepostsum(checksum);
|
||||
if(repost) {
|
||||
await fs.promises.unlink(source).catch(_=>{});
|
||||
if(e.type == 'tg')
|
||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `repost motherf0cker (checksum): ${cfg.main.url.full}/${repost}`);
|
||||
return await e.reply(`repost motherf0cker (checksum): ${cfg.main.url.full}/${repost}`);
|
||||
}
|
||||
|
||||
const filename = path.basename(source);
|
||||
|
||||
await fs.promises.copyFile(source, `./public/b/${filename}`);
|
||||
await fs.promises.unlink(source).catch(_=>{});
|
||||
|
||||
// user alias
|
||||
let username = e.user.nick || e.user.username;
|
||||
const alias = (await db`
|
||||
select "user"."user"
|
||||
from "user_alias"
|
||||
join "user" on "user".id = user_alias.userid
|
||||
where lower(user_alias.alias) ilike ${username}
|
||||
limit 1
|
||||
`)?.[0]?.user;
|
||||
if(alias) {
|
||||
username = alias;
|
||||
}
|
||||
|
||||
await db`
|
||||
insert into items ${
|
||||
@ -133,7 +245,7 @@ export default async bot => {
|
||||
mime: mime,
|
||||
size: size,
|
||||
checksum: checksum,
|
||||
username: e.user.nick || e.user.username,
|
||||
username: username,
|
||||
userchannel: e.channel,
|
||||
usernetwork: e.network,
|
||||
stamp: ~~(new Date() / 1000),
|
||||
@ -142,113 +254,56 @@ export default async bot => {
|
||||
}
|
||||
`;
|
||||
|
||||
const itemid = (await db`
|
||||
select *
|
||||
from "items"
|
||||
where dest = ${filename}
|
||||
limit 1
|
||||
`)[0].id;
|
||||
const itemid = await queue.getItemID(filename);
|
||||
|
||||
// generate thumbnail
|
||||
try {
|
||||
if(mime.startsWith('video/') || mime == 'image/gif')
|
||||
await exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`);
|
||||
else if(mime.startsWith('image/') && mime != 'image/gif')
|
||||
await exec(`convert ./public/b/${filename} ./tmp/${itemid}.png`);
|
||||
else if(mime.startsWith('audio/')) {
|
||||
if(link.match(/soundcloud/)) {
|
||||
let cover = (await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --get-thumbnail "${link}"`)).stdout.trim();
|
||||
if(!cover.match(/default_avatar/)) {
|
||||
cover = cover.replace(/-(large|original)\./, '-t500x500.');
|
||||
try {
|
||||
await exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`);
|
||||
const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size;
|
||||
if(size >= 0) {
|
||||
await exec(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
|
||||
await exec(`convert ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
} catch(err) {}
|
||||
}
|
||||
else {
|
||||
await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
|
||||
await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
|
||||
await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||
}
|
||||
}
|
||||
|
||||
await exec(`convert "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`);
|
||||
await fs.promises.unlink(`./tmp/${itemid}.png`).catch(err => {});
|
||||
await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(err => {});
|
||||
await queue.genThumbnail(filename, mime, itemid, link);
|
||||
} catch(err) {
|
||||
await exec(`convert ./mugge.png ./public/t/${itemid}.webp`);
|
||||
await queue.exec(`magick ./mugge.png ./public/t/${itemid}.webp`);
|
||||
}
|
||||
|
||||
let speed = lib.calcSpeed(size, end);
|
||||
speed = !Number.isFinite(speed) ? "yes" : `${speed.toFixed(2)} Mbit/s`;
|
||||
|
||||
// autotagger
|
||||
/*let tags = [];
|
||||
let score = 0;
|
||||
try {
|
||||
if(mime.startsWith('image')) {
|
||||
const res = await lib.detectNSFW(filename);
|
||||
score = res.score;
|
||||
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: itemid,
|
||||
tag_id: res.isNSFW ? 2 : 1,
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
tags.push(res.isNSFW ? 'nsfw' : 'sfw');
|
||||
|
||||
if(res.hentai >= .7) {
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: f.id,
|
||||
tag_id: 4, // hentai
|
||||
user_id: 1 // autotagger
|
||||
})
|
||||
}
|
||||
`;
|
||||
tags.push('hentai');
|
||||
}
|
||||
}
|
||||
else if(mime.startsWith('audio')) {
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db([{
|
||||
item_id: itemid,
|
||||
tag_id: 1,
|
||||
user_id: 1
|
||||
}, {
|
||||
item_id: itemid,
|
||||
tag_id: 3, // audio
|
||||
user_id: 1
|
||||
}])
|
||||
}
|
||||
`;
|
||||
tags.push('sfw', 'audio');
|
||||
}
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
let tags = [];
|
||||
/*if(cfg.apis?.nsfw1 && mime.startsWith('image')) {
|
||||
const nsfw = await autotagger.isNSFW(filename, size);
|
||||
tags.push(nsfw ? 'nsfw' : 'sfw');
|
||||
if(nsfw)
|
||||
await queue.tagNSFW(itemid);
|
||||
else
|
||||
await queue.tagSFW(itemid);
|
||||
}*/
|
||||
|
||||
/*e.reply([
|
||||
`[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}` + (tags.length > 0 ? ` | tags: ${tags.join(', ')} (score: ${score.toFixed(2)})` : '')
|
||||
]);*/
|
||||
e.reply([
|
||||
`[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}`
|
||||
]);
|
||||
let outputmsgirc = `[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}`;
|
||||
let outputmsgtg = `[f0cked] size: ${lib.formatSize(size)} | speed: ${speed}`;
|
||||
|
||||
if(tags?.length > 0) {
|
||||
const tagstr = tags.join(', ');
|
||||
outputmsgirc += ` | tags: ${tagstr}`;
|
||||
outputmsgtg += ` | tags: ${tagstr}`;
|
||||
}
|
||||
|
||||
if(e.type == 'tg') {
|
||||
await e.deleteMessage(msg.result.chat.id, msg.result.message_id);
|
||||
await e.reply(outputmsgtg, {
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [[
|
||||
{ text: (await lib.hasTag(itemid, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${itemid}` },
|
||||
{ text: (await lib.hasTag(itemid, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${itemid}` },
|
||||
{ text: 'tags', callback_data: `b_tags:${itemid}` },
|
||||
{ text: '❌ delete', callback_data: `b_delete:${itemid}` }
|
||||
], [
|
||||
{ text: `open f0ck #${itemid}`, url: `${cfg.main.url.full}/${itemid}` }
|
||||
]]
|
||||
})
|
||||
});
|
||||
}
|
||||
else {
|
||||
await e.reply(outputmsgirc);
|
||||
}
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
@ -12,11 +12,11 @@ export default async bot => {
|
||||
f: async e => {
|
||||
const id = +e.args[1];
|
||||
if(!id)
|
||||
return e.reply("lol no");
|
||||
return await e.reply("lol no");
|
||||
const tags = (await lib.getTags(id)).map(t => t.tag);
|
||||
if(tags.length === 0)
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} is tagged as: ${tags.join(', ')}`);
|
||||
return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return await e.reply(`item ${cfg.main.url.full}/${id} is tagged as: ${tags.join(', ')}`);
|
||||
}
|
||||
}, {
|
||||
name: "tags add",
|
||||
@ -26,7 +26,7 @@ export default async bot => {
|
||||
f: async e => {
|
||||
const id = +e.args[1];
|
||||
if(!id)
|
||||
return e.reply("lol no");
|
||||
return await e.reply("lol no");
|
||||
|
||||
const tags = (await lib.getTags(id)).map(t => t.tag);
|
||||
|
||||
@ -35,7 +35,7 @@ export default async bot => {
|
||||
: e.args.splice(2)).filter(t => !tags.includes(t) && t.length > 0);
|
||||
|
||||
if(newtags.length === 0)
|
||||
return e.reply("no (new) tags provided");
|
||||
return await e.reply("no (new) tags provided");
|
||||
|
||||
await Promise.all(newtags.map(async ntag => {
|
||||
try {
|
||||
@ -73,8 +73,8 @@ export default async bot => {
|
||||
|
||||
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
||||
if(ntags.length === 0)
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return await e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
}
|
||||
}, {
|
||||
name: "tags remove",
|
||||
@ -84,7 +84,7 @@ export default async bot => {
|
||||
f: async e => {
|
||||
const id = +e.args[1];
|
||||
if(!id)
|
||||
return e.reply("lol no");
|
||||
return await e.reply("lol no");
|
||||
|
||||
const tags = await lib.getTags(id);
|
||||
|
||||
@ -93,7 +93,7 @@ export default async bot => {
|
||||
: e.args.splice(2)).filter(t => t.length > 0);
|
||||
|
||||
if(removetags.length === 0)
|
||||
return e.reply("no tags provided");
|
||||
return await e.reply("no tags provided");
|
||||
|
||||
const res = await Promise.all(removetags.map(async rtag => {
|
||||
const tagid = tags.filter(t => t.tag === rtag)[0]?.id ?? null;
|
||||
@ -122,12 +122,12 @@ export default async bot => {
|
||||
};
|
||||
}));
|
||||
|
||||
e.reply(JSON.stringify(res));
|
||||
await e.reply(JSON.stringify(res));
|
||||
|
||||
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
||||
if(ntags.length === 0)
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return await e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
42
src/inc/trigger/thumbnails.mjs
Normal file
@ -0,0 +1,42 @@
|
||||
import queue from '../queue.mjs';
|
||||
import db from '../sql.mjs';
|
||||
|
||||
export default async bot => {
|
||||
|
||||
return [{
|
||||
name: "thumbnailer",
|
||||
call: /^\!thumb .*/i,
|
||||
active: true,
|
||||
level: 100,
|
||||
f: async e => {
|
||||
let processed = [];
|
||||
|
||||
for(let id of e.args) {
|
||||
id = +id;
|
||||
if(id <= 1)
|
||||
continue;
|
||||
|
||||
const f0ck = await db`
|
||||
select id, dest, mime, src
|
||||
from "items"
|
||||
where
|
||||
id = ${id} and
|
||||
active = 'true'
|
||||
limit 1
|
||||
`;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
await e.reply(`f0ck ${id}: f0ck not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// gen thumb
|
||||
await queue.genThumbnail(f0ck[0].dest, f0ck[0].mime, f0ck[0].id, f0ck[0].src);
|
||||
|
||||
processed.push(id);
|
||||
}
|
||||
|
||||
return await e.reply(`thumbnails: ${processed.length}/${e.args.length} (${processed.join(",")})`);
|
||||
}
|
||||
}];
|
||||
};
|
@ -5,6 +5,11 @@ import cuffeo from "cuffeo";
|
||||
import { promises as fs } from "fs";
|
||||
import flummpress from "flummpress";
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
console.error(err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const self = {
|
||||
_trigger: new Map(),
|
||||
@ -61,11 +66,12 @@ import flummpress from "flummpress";
|
||||
req.session = false;
|
||||
if(req.url.pathname.match(/^\/(s|b|t|ca)\//))
|
||||
return;
|
||||
req.theme = req.cookies.theme || 'f0ck';
|
||||
req.theme = req.cookies.theme || 'atmos';
|
||||
req.fullscreen = req.cookies.fullscreen || 0;
|
||||
|
||||
if(req.cookies.session) {
|
||||
const user = await db`
|
||||
select "user".id, "user".login, "user".user, "user".level, "user_sessions".id as sess_id, "user_options".*
|
||||
select "user".id, "user".login, "user".user, "user".admin, "user_sessions".id as sess_id, "user_options".*
|
||||
from "user_sessions"
|
||||
left join "user" on "user".id = "user_sessions".user_id
|
||||
left join "user_options" on "user_options".user_id = "user_sessions".user_id
|
||||
@ -96,6 +102,7 @@ import flummpress from "flummpress";
|
||||
`;
|
||||
|
||||
req.session.theme = req.cookies.theme;
|
||||
req.session.fullscreen = req.cookies.fullscreen;
|
||||
|
||||
// update userprofile
|
||||
await db`
|
||||
@ -103,12 +110,14 @@ import flummpress from "flummpress";
|
||||
db({
|
||||
user_id: +user[0].id,
|
||||
mode: user[0].mode ?? 0,
|
||||
theme: req.session.theme ?? 'f0ck'
|
||||
}, 'user_id', 'mode', 'theme')
|
||||
theme: req.session.theme ?? 'atmos',
|
||||
fullscreen: req.session.fullscreen || 0
|
||||
}, 'user_id', 'mode', 'theme', 'fullscreen')
|
||||
}
|
||||
on conflict ("user_id") do update set
|
||||
mode = excluded.mode,
|
||||
theme = excluded.theme,
|
||||
fullscreen = excluded.fullscreen,
|
||||
user_id = excluded.user_id
|
||||
`;
|
||||
}
|
||||
|
@ -1,49 +1,13 @@
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<div class="about">
|
||||
<div>
|
||||
<a href="//f0ck.me/48908"><img src="//f0ck.me/s/img/loool.webp" /></a>
|
||||
<p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif <3</p>
|
||||
</div>
|
||||
<h5>f0ck Contact</h5>
|
||||
<p>Whatever it is, we might have a answer, even though it might not be the one you were looking for: <a href="mailto:admin@f0ck.me">admin@f0ck.me</a></p>
|
||||
<h5>About f0ck</h5>
|
||||
<p>f0ck is your friendly IRC shitposting bot, it's built for catching urls that are passed to it and displays the content of passed urls on a simple and accessible web gallery reachable at <a href="/">f0ck.me</a></p>
|
||||
<h5>WTF is a f0ck?</h5>
|
||||
<p>A f0ck is basically giving a fuck about some internet bullshit, like stupid images, videos and so on, but also for great things like good music taste and shit, it's basically "a f0ck was given" and f0ck and it's users gave a lot of f0cks over the past years, it's not hard to finally start giving a damn f0ck about something, just f0ck it dood!</p>
|
||||
<h5>Where to f0ck?</h5>
|
||||
<h4>#f0ck on n0xy.net</h4>
|
||||
<p>You can invite f0ck to your channel on the following supported networks by simply typing<br><code>/invite f0ck</code></p>
|
||||
<ul>
|
||||
<li><a href="https://n0xy.net">n0xy.net</a></li>
|
||||
<li><a href="https://www.rizon.net/">rizon.net</a></li>
|
||||
<li><a href="https://libera.chat/">libera.chat</a></li>
|
||||
</ul>
|
||||
<p>To start f0cking the shit out of something simply add a <code>!f0ck</code> behind the url you want to f0ck, that's it</p>
|
||||
<p>#f0ck specific: to have f0ck ignore a link add <code>!ignore</code> at the end <br>Example: <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">https://www.youtube.com/watch?v=dQw4w9WgXcQ</a> !ignore</p>
|
||||
<p>f0ck supports a variety of websites, in fact all websites supported by <code>yt-dlp</code> are supported by f0ck aswell!</p>
|
||||
<h5>f0ck Rules</h5>
|
||||
<ul>
|
||||
<li>You must be 18 years or older to visit or post</li>
|
||||
<li>You shall not post animal cruelty, we like our animals alive and well, living a happy life until they are ready for our Schnitzel!</li>
|
||||
<li>You shall not post under <b>ANY</b> circumstances: Snuff, Beastiality, Rape, Terrorist stuff (Beheadings, First person shootings, warcrimes), Childporn, Childmodeling</li>
|
||||
</ul>
|
||||
<h5>f0cked up?</h5>
|
||||
<p>To have something removed in case you accidentally f0cked something that actually shouldn't be f0cked you can always contact the admins either via IRC or Email</p>
|
||||
<ul>
|
||||
<li>irc.n0xy.net #f0ck</li>
|
||||
<li>admin@f0ck.me</li>
|
||||
</ul>
|
||||
<h5>Compatibility</h5>
|
||||
<p>f0ck is developed and tested for Firefox and Chromium in their latest versions</p>
|
||||
<p>If you encounter bugs please report them so we can fix them.</p>
|
||||
<p>Microsoft Edgy is not actively supported, but if it werks, great! Same for anything apple related.</p>
|
||||
<h5>Tinfoil f0ckers listen!</h5>
|
||||
<p>f0ck onions and moons, but fockulite!</p>
|
||||
<p>http://fockmoonsb24iczs7odozzy5uktlzbcgp337nabrgffzxv5ihabgpvyd.onion</p>
|
||||
<p>http://fockulite74atso2xsxxw6q2gzqrgck572tiwvkyf5vdxictjn2vmlyd.onion</p>
|
||||
<p>f0ck is completely functional without javascript enabled, you can be the beardiest neckbeard of all, we got you m'gentleman, if you want to use a custom theme you gotta allow our style cookie.</p>
|
||||
<h5>f0ck Privacy?</h5>
|
||||
<p>Cookies: Yes, we set 1 cookie for your prefered stylesheet, this is a optional cookie and not required for the site to function, simply cosmetics, you can block this cookie and the site will still work as intended and will default to the default f0ck theme called "f0ck"</p>
|
||||
<p>Logs: We do not log users accessing our website.</p>
|
||||
<p>Welcome stranger!</p>
|
||||
<p>bringing you some of the greatest webms from the past, the present and the future!</p>
|
||||
<p>Enjoy your stay.</p>
|
||||
<img style="width: 200px" src="/s/img/cockfag.png" alt="cockfag">
|
||||
<p>If you have any questions you can reach out via Mail.</p>
|
||||
<p>mail: admin@w0bm.com</p>
|
||||
<p>Please also make yourself familiar with the <a href="/terms">Terms Of Service</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,10 +1,22 @@
|
||||
@include(snippets/header_admin)
|
||||
<div class="container">
|
||||
<h1>Henlo, {{ session.user }}</h1>
|
||||
<p>Hier entsteht eine Internetpräsenz!</p>
|
||||
<img src="/s/img/favicon.gif" alt="f0ck bash">
|
||||
<p>@if(typeof totals !== "undefined")
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<div class="container">
|
||||
<h1>ADMINBEREICH</h1>
|
||||
<h5>Hallo, {{ session.user }}</h5>
|
||||
<span>Hier entsteht eine Internetpräsenz!</span><br>
|
||||
<hr>
|
||||
<p>f0ck stats: @if(typeof totals !== "undefined")
|
||||
total: {{ totals.total }} | tagged: {{ totals.tagged }} | untagged: {{ totals.untagged }} | sfw: {{ totals.sfw }} | nsfw: {{ totals.nsfw }}
|
||||
@endif</p>
|
||||
<hr>
|
||||
<div class="admintools">
|
||||
<p>Adminwerkzeuge</p>
|
||||
<ul>
|
||||
<!-- <li><a href="/admin/log">Logs</a></li>
|
||||
<li><a href="/admin/recover">Recover f0cks</a></li> -->
|
||||
<li><a href="/admin/sessions">Sessions</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,16 +1,18 @@
|
||||
@include(snippets/header_admin)
|
||||
@if(log)
|
||||
<h1>last {{ log.length }} entries:</h1>
|
||||
<div class="logwrap">
|
||||
@each(log as line)
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
@if(log)
|
||||
<h1>last {{ log.length }} entries:</h1>
|
||||
<div class="logwrap">
|
||||
@each(log as line)
|
||||
<p>{{ line }}</p>
|
||||
@endeach
|
||||
</div>
|
||||
<script>
|
||||
@endeach
|
||||
</div>
|
||||
<script>
|
||||
(() => {
|
||||
const d = document.querySelector("div.logwrap");
|
||||
d.scrollTop = d.scrollHeight;
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
</script>
|
||||
@endif
|
||||
</div>
|
||||
@include(snippets/footer)
|
||||
|
@ -1,24 +1,26 @@
|
||||
@include(snippets/header_admin)
|
||||
<table class="table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<table class="table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>ID</td>
|
||||
<td>f0cker</td>
|
||||
<td>mime</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@each(posts as post)
|
||||
<tr>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@each(posts as post)
|
||||
<tr>
|
||||
<td><img src="data:image/webp;base64,{{ post.thumbnail }}" /></td>
|
||||
<td>{{ post.id }}</td>
|
||||
<td>{{ post.username }}</td>
|
||||
<td>{{ post.mime }}</td>
|
||||
<td><a href="/admin/recover/?id={{ post.id }}">recover</a></td>
|
||||
</tr>
|
||||
@endeach
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
@endeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,7 +1,8 @@
|
||||
@include(snippets/header_admin)
|
||||
<table class="table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<table class="table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>ID</td>
|
||||
<td>userid</td>
|
||||
@ -10,11 +11,11 @@
|
||||
<td>created_at</td>
|
||||
<td>last_used</td>
|
||||
<td>last_action</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@each(sessions as session)
|
||||
<tr>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@each(sessions as session)
|
||||
<tr>
|
||||
<td>{{ session.kmsi ? '⚓' : '' }}</td>
|
||||
<td>{{ session.id }}</td>
|
||||
<td>{{ session.user_id }}</td>
|
||||
@ -23,8 +24,9 @@
|
||||
<td>{{ new Date(session.created_at * 1e3).toLocaleString("de-DE") }}</td>
|
||||
<td>{{ new Date(session.last_used * 1e3).toLocaleString("de-DE") }}</td>
|
||||
<td>{{ session.last_action }}</td>
|
||||
</tr>
|
||||
@endeach
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
@endeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,165 +0,0 @@
|
||||
<div class="sidebar">
|
||||
<div class="header_sidebar">
|
||||
<span>Comments</span>
|
||||
</div>
|
||||
<div class="commentbox">
|
||||
<textarea name="comments" id="comments1" cols="30" rows="5" placeholder="Write a shitpost?"></textarea>
|
||||
<button class="commentbutton">Shit</button>
|
||||
</div>
|
||||
<div class="commentholder">
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,6 @@
|
||||
@include(snippets/header)
|
||||
<div class="container">
|
||||
<div id="main">
|
||||
<div class="container">
|
||||
<div class="_error_wrapper">
|
||||
<div class="err">
|
||||
<div class="_error_topbar">
|
||||
@ -14,5 +15,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,6 +1,8 @@
|
||||
@include(snippets/header)
|
||||
<div class="pagewrapper">
|
||||
<div id="main">
|
||||
<div class="index-container">
|
||||
@if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
|
||||
@if(tmp.user)<h2>user: <a href="/user/{{ tmp.user.toLowerCase() }}">{!! tmp.user.toLowerCase() !!}</a>@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
|
||||
@if(tmp.tag)<h2>tag: @if(session)<a href="/search?tag={!! tmp.tag.toLowerCase() !!}" target="_blank">{!! tmp.tag.toLowerCase() !!}</a>@else{!! tmp.tag.toLowerCase() !!}@endif@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
|
||||
<div class="posts">
|
||||
@each(items as item)
|
||||
@ -11,4 +13,6 @@
|
||||
▼
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
||||
|
@ -1,17 +1,17 @@
|
||||
<div class="wrapper">
|
||||
@include(snippets/header)
|
||||
<canvas class="hidden-xs" id="bg"></canvas>
|
||||
<div class="wrapper">
|
||||
<div id="main">
|
||||
|
||||
<div class="container">
|
||||
<div class="_204863">
|
||||
<div class="imageDoor" style="--hover-image: url('/t/{{ item.id }}.webp');">
|
||||
<img src="/t/{{ item.id }}.webp" alt="" />
|
||||
</div>
|
||||
<div class="gapLeft">
|
||||
<span class="populateME"><b>f0ck</b> - {{ item.id }}</span>
|
||||
</div>
|
||||
<div class="imageDoor"></div>
|
||||
<div class="gapLeft"></div>
|
||||
@if(session)
|
||||
<div class="gapRight">
|
||||
<svg class="iconset" id="a_favo"><use href="/s/img/iconset.svg#heart_{{ Object.values(item.favorites).filter(u => u.user == session.user)[0] ? 'solid' : 'regular' }}"></use></svg>
|
||||
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>
|
||||
<svg class="iconset" id="a_tfull"><use href="/s/img/iconset.svg#window-{{ fullscreen == 1 ? 'minimize' : 'maximize' }}"></use></svg>
|
||||
@if(session.admin)<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@ -33,7 +33,7 @@
|
||||
<video id="my-video" class="embed-responsive-item" width="640" height="360" src="{{ item.dest }}" preload="auto" autoplay controls loop playsinline></video>
|
||||
</div>
|
||||
@elseif(item.mime.startsWith("audio"))
|
||||
<div class="embed-responsive embed-responsive-16by9" style="background: url('@if(item.coverart)//f0ck.me{{ item.coverart }}@else/s/img/200.gif@endif') no-repeat center / contain black;">
|
||||
<div class="embed-responsive embed-responsive-16by9" style="background: url('@if(item.coverart)//w0bm.com{{ item.coverart }}@else/s/img/200.gif@endif') no-repeat center / contain black;">
|
||||
<audio id="my-video" class="embed-responsive-item" autoplay controls loop src="{{ item.dest }}" data-setup="{}" poster="@if(item.coverart){{ item.coverart }}@else/s/img/200.gif@endif" type="{{ item.mime }}"></audio>
|
||||
</div>
|
||||
@elseif(item.mime.startsWith("image"))
|
||||
@ -59,28 +59,44 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="metadata">
|
||||
<div class="kontrollelement">
|
||||
<div class="einheit">
|
||||
@if(typeof pagination !== "undefined")
|
||||
<nav class="steuerung">
|
||||
@if(pagination.prev)
|
||||
<a id="" href="{{ link.main }}{{ pagination.prev }}">← next</a>
|
||||
@else
|
||||
<a id="" href="#" style="visibility: hidden">← next</a>
|
||||
@endif
|
||||
<span>|</span>
|
||||
<a id="random" class="" href="/random">
|
||||
<span>random</span>
|
||||
</a>
|
||||
<span>
|
||||
|
|
||||
</span>
|
||||
@if(pagination.next)
|
||||
<a id="" href="{{ link.main }}{{ pagination.next }}">prev →</a>
|
||||
@else
|
||||
<a id="" href="#" style="visibility: hidden">prev →</a>
|
||||
@endif
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="blahlol">
|
||||
<span class="badge badge-dark">
|
||||
<a href="/{{ item.id }}" class="id-link">{{ item.id }}</a>
|
||||
@if(session)
|
||||
(<a id="a_username" href="/user/{{ user.name.toLowerCase() }}/f0cks@if(tmp.mime)/{{ tmp.mime }}@endif">{{ user.name }}</a>)
|
||||
@endif
|
||||
</span>
|
||||
<span class="badge badge-dark">{{ user.network }} / {{ user.channel }}</span>
|
||||
<span class="badge badge-dark image-source">
|
||||
@if(item.src.long.length)
|
||||
<a href="{{ item.src.long }}" target="_blank">{{ item.src.short }}</a>
|
||||
@else
|
||||
hidden
|
||||
@endif
|
||||
</span>
|
||||
<span class="badge badge-dark"><a class="dest-link" href="{{ item.dest }}" target="_blank">{{ item.mime }}</a> / {{ item.size }}</span>
|
||||
<span class="badge badge-dark"><time class="timeago" tooltip="{{ item.timestamp.timefull }}">{{ item.timestamp.timeago }}</time></span>
|
||||
<span class="badge badge-dark">{{ phrase }}</span>
|
||||
<span class="badge badge-dark" id="tags">
|
||||
@if(typeof item.tags !== "undefined")
|
||||
@each(item.tags as tag)
|
||||
<span @if(session)tooltip="{{ tag.user }}"@endif class="badge {{ tag.badge }} mr-2">
|
||||
<a href="/tag/{{ tag.tag }}">{!! tag.tag !!}</a>@if(session) <a class="removetag" href="#">×</a>@endif
|
||||
<a href="/tag/{{ tag.normalized }}">{!! tag.tag !!}</a>@if(session.admin) <a class="removetag" href="#">×</a>@endif
|
||||
</span>
|
||||
@endeach
|
||||
@endif
|
||||
@ -97,10 +113,7 @@
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
<div class="random">
|
||||
<a href="/random"></a>
|
||||
</div>
|
||||
</div>
|
||||
@include(comments)
|
||||
@include(snippets/footer)
|
||||
</div>
|
||||
@include(snippets/footer)
|
||||
|
@ -3,12 +3,12 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>f0ck login</title>
|
||||
<title>login</title>
|
||||
<link href="/s/css/f0ck.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body type="login">
|
||||
<form class="login-form" method="post" action="/login">
|
||||
<p><a href="/"><img src="/s/img/f0ck_small.png" /></a></p>
|
||||
<img class="login-image" src="/s/img/w0bm_mosh_banner_by_marderchen.gif" alt="">
|
||||
<input type="text" name="username" placeholder="username" autocomplete="off" required />
|
||||
<input type="password" name="password" placeholder="password" autocomplete="off" required />
|
||||
<p><input type="checkbox" id="kmsi" name="kmsi" /> <label for="kmsi">stay signed in</label></p>
|
||||
|
22
views/picdump.html
Normal file
@ -0,0 +1,22 @@
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<h2>Picdump (last week)</h2>
|
||||
<div class="picdump">
|
||||
@each(dump as line)
|
||||
<a href="/{{ line.id }}"><img src="/t/{{ line.id }}.webp"></a>
|
||||
@endeach
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.picdump {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
.picdump > a {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.picdump > a > img {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@include(snippets/footer)
|
@ -1,31 +1,33 @@
|
||||
@include(snippets/header)
|
||||
<div class="topf0ckers">
|
||||
<h3>Top f0ckers of all time <br>- Ranking -</h3>
|
||||
</div>
|
||||
<div class="ranking">
|
||||
<div id="main">
|
||||
<div class="topf0ckers">
|
||||
<h3>f0ck Rankings?</h3>
|
||||
</div>
|
||||
<div class="theforceofthree">
|
||||
<div class="triin">
|
||||
</div>
|
||||
<div class="ranking">
|
||||
<div class="by-user">
|
||||
<h3>Biggest taggers</h3>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Avatar</th>
|
||||
<th>Username</th>
|
||||
<th>Tagged</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
@for(let i = 0; i < list.length; i++)
|
||||
<tr>
|
||||
<td>{{ i + 1 }}</td>
|
||||
<td><a href="/{{ list[i].avatar }}"><img class="avatar" src="/t/{{ list[i].avatar }}.webp" /></a></td>
|
||||
<td><a href="/user/{!! list[i].user !!}">{!! list[i].user !!}</a></td>
|
||||
<td><a href="/{{ list[i].avatar }}"><img class="avatar" src="/t/{{ list[i].avatar }}.webp"></a></td>
|
||||
<td>@if(list[i].admin)⭐ @endif<a href="/user/{!! list[i].user !!}">{!! list[i].user !!}</a></td>
|
||||
<td>{{ list[i].count }}</td>
|
||||
</tr>
|
||||
@endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="by-hoster">
|
||||
<h3>Top {{ hoster.length }} hoster</h3>
|
||||
<table class="table">
|
||||
@each(hoster as host)
|
||||
<tr><td>{{ host.part.length ? host.part : "Telegram" }}</td><td>{{ host.c }}</td></tr>
|
||||
@endeach
|
||||
</table>
|
||||
</div>
|
||||
<div class="by-stats">
|
||||
<h3>Tag stats</h3>
|
||||
<table class="table">
|
||||
@ -34,7 +36,20 @@
|
||||
<tr><td>untagged</td><td>{{ stats.untagged }}</td></tr>
|
||||
<tr><td>SFW</td><td>{{ stats.sfw }}</td></tr>
|
||||
<tr><td>NSFW</td><td>{{ stats.nsfw }}</td></tr>
|
||||
<tr><td>deleted</td><td>{{ stats.deleted }}</td></tr>
|
||||
<tr><td>missing ids</td><td>{{ stats.untracked }}</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Top f0cks</h3>
|
||||
<table class="table">
|
||||
@each(favotop as favo)
|
||||
<tr><td><a href="/{{ favo.item_id }}">{{ favo.item_id }}</a></td><td>{{ favo.favs }}</td></tr>
|
||||
@endeach
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="triout">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
||||
|
@ -1,8 +1,11 @@
|
||||
@include(snippets/header)
|
||||
<div class="f0ckgle">
|
||||
<h1 style="text-align: center">f0ckgle</h1>
|
||||
<div id="main">
|
||||
<div class="f0ckgle">
|
||||
<div class="search-title">
|
||||
<span>search</span>
|
||||
</div>
|
||||
<form action="/search" class="admin-search">
|
||||
<input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit"><b>f0ck</b></button>
|
||||
<input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit">🔍</button>
|
||||
</form>
|
||||
<div class="results">
|
||||
@if(result)
|
||||
@ -33,5 +36,6 @@
|
||||
</table>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,29 +1,36 @@
|
||||
@include(snippets/header)
|
||||
<h1>Settings</h1>
|
||||
@if(session.avatar)<a href="/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif
|
||||
<h2>Account</h2>
|
||||
<table class="table">
|
||||
<div class="settings">
|
||||
<h1>Settings</h1>
|
||||
<h2>Account</h2>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>UserID</td>
|
||||
<td>{{ session.id }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>level</td>
|
||||
<td>{{ session.level }}/100</td>
|
||||
<td>admin</td>
|
||||
<td>{{ !!session.admin }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>username</td>
|
||||
<td>{!! session.user !!}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>avatar</td>
|
||||
<td><input type="text" class="input" name="i_avatar" value="{{ session.avatar }}" /><input type="submit" id="s_avatar" value="save" /></td>
|
||||
<td>@if(session.avatar)<a href="/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp"></a>@endif</td>
|
||||
<td><input type="text" class="input" name="i_avatar" value="{{ session.avatar }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mail</td>
|
||||
<td><input type="text" class="input" name="i_mail" placeholder="hashed" disabled></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><input type="submit" id="s_avatar" value="save"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Sessions</h2>
|
||||
<table class="table">
|
||||
</table>
|
||||
<h2>Sessions</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
@ -48,5 +55,6 @@
|
||||
</tr>
|
||||
@endeach
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
@include(snippets/footer)
|
||||
|
@ -1,8 +1,10 @@
|
||||
@if(session)<!--<div style="position: fixed; bottom: 0; z-index: 998; background-color: var(--bg); width: 100%; height: 29px; border-top: 1px solid var(--accent)">{{ JSON.stringify(session) }}</div>-->@endif
|
||||
</div>
|
||||
<script async src="/s/js/theme.js?v=@mtime(/public/s/js/theme.js)"></script>
|
||||
<script src="/s/js/v0ck.js?v=@mtime(/public/s/js/v0ck.js)"></script>
|
||||
<script src="/s/js/f0ck.js?v=@mtime(/public/s/js/f0ck.js)"></script>
|
||||
@if(session)<script src="/s/js/admin.js?v=@mtime(/public/s/js/admin.js)"></script>@endif
|
||||
@if(session && session.admin)
|
||||
<script src="/s/js/admin.js?v=@mtime(/public/s/js/admin.js)"></script>
|
||||
@elseif(session && !session.admin)
|
||||
<script src="/s/js/user.js?v=@mtime(/public/s/js/user.js)"></script>
|
||||
@endif
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,15 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif">
|
||||
<html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif" res="@if(typeof fullscreen !== "undefined"){{ fullscreen == 1 ? 'fullscreen' : '' }}@endif">
|
||||
<head>
|
||||
<title>f0ck!</title>
|
||||
<meta name="description" content="Welcome to the internet"/>
|
||||
<meta property="og:image" content="/s/img/favicon.gif">
|
||||
<link rel="icon" type="image/gif" href="/s/img/favicon.gif" />
|
||||
<title>f0bm</title>
|
||||
<link rel="icon" type="image/gif" href="/s/img/favicon.png" />
|
||||
<link rel="stylesheet" href="/s/css/f0ck.css?v=@mtime(/public/s/css/f0ck.css)">
|
||||
<link rel="stylesheet" href="/s/css/w0bm.css?v=@mtime(/public/s/css/w0bm.css)">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@if(typeof item !== "undefined")<link rel="canonical" href="https://f0ck.me/{{ item.id }}" />@endif
|
||||
@if(typeof item !== "undefined")<link rel="canonical" href="https://w0bm.com/{{ item.id }}" />@endif
|
||||
</head>
|
||||
<body>
|
||||
@include(snippets/navbar)
|
||||
<div id="main">
|
@ -1,19 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif">
|
||||
<head>
|
||||
<title>@if(typeof data !== "undefined" && data.title){{ data.title }}@elsef0ck!@endif</title>
|
||||
<link rel="icon" type="image/gif" href="/s/img/favicon.gif" />
|
||||
<link rel="stylesheet" href="/s/css/f0ck.css">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="f0ck.me is the place where internet purists gather to celebrate content of all kinds">
|
||||
@if(typeof data !== "undefined" && data.item)
|
||||
<meta property="og:site_name" content="f0ck.me" />
|
||||
<meta property="og:description"/>
|
||||
<meta name="Description"/>
|
||||
<meta property="og:image" content="{{ item.thumbnail }}" />
|
||||
@endif
|
||||
</head>
|
||||
<body>
|
||||
@include(snippets/navbar_admin)
|
||||
<div id="main">
|
@ -1,40 +1,33 @@
|
||||
@if(session)
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
|
||||
<a class="navbar-brand" href="/"><img class="image-brand" src="/s/img/w0bm_mosh_banner_by_marderchen.gif" alt="w...?"><span class="f0ck" width="" height=""></span></a>
|
||||
<div class="navigation-links">
|
||||
<div class="pagination-container-fluid">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link user" href="#" content="{{ session.user }}" data-toggle="dropdown">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span>
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar"><span>@if(session.admin)⭐ @endif{{ session.user }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/admin">adminpanel</a></li>
|
||||
<li><a href="/user/{{ session.user.toLowerCase() }}">my profile</a></li>
|
||||
<li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li>
|
||||
<li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li>
|
||||
<li><a href="/settings">settings</a></li>
|
||||
<li><a href="/search">search</a></li>
|
||||
@if(session.admin)<li><a href="/admin">Admin</a></li>@endif
|
||||
<li><a href="/about">About</a></li>
|
||||
<li><a href="/ranking">Ranking</a></li>
|
||||
<li><a href="/ranking">ranking</a></li>
|
||||
<li><a href="/settings">settings</a></li>
|
||||
<li><a href="/logout">logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown" id="themes">
|
||||
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
|
||||
<ul class="dropdown-menu">
|
||||
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">𐂧</a>
|
||||
<ul class="dropdown-menu shii">
|
||||
@each(themes as t)
|
||||
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
|
||||
@endeach
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime) ▼@endif</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item @if(session)dropdown@endif">
|
||||
<a class="nav-link ddcontent" href="#"@if(typeof session.mode !== "undefined") content="{{ modes[session.mode] ?? 'sfw' }}" data-toggle="dropdown"@endif>Mode</a>
|
||||
<ul class="dropdown-menu">
|
||||
@ -43,6 +36,11 @@
|
||||
@endfor
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a id="explore-tags" class="nav-link" href="/tags">
|
||||
<span class="nav-link-identifier">Tags</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a id="random" class="nav-link" href="/random">
|
||||
<span class="nav-link-identifier">Random</span>
|
||||
@ -50,8 +48,8 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
||||
<div class="pagination-container-fluid">
|
||||
<div class="pagination-wrapper">
|
||||
@if(typeof pagination !== "undefined")
|
||||
<nav class="pagination">
|
||||
@ -69,20 +67,19 @@
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@else
|
||||
<!-- not logged in -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
|
||||
<a class="navbar-brand" href="/"><img class="image-brand" src="/s/img/w0bm_mosh_banner_by_marderchen.gif" alt="w...?"><span class="f0ck" width="" height=""></span></a>
|
||||
|
||||
<div class="navigation-links-guest">
|
||||
<ul class="navbar-nav-guests">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/login">login</a></li>
|
||||
</ul>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/about">About</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown" id="themes">
|
||||
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
|
||||
@ -92,14 +89,10 @@
|
||||
@endeach
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime) ▼@endif</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
|
||||
</ul>
|
||||
<li class="nav-item">
|
||||
<a id="explore-tags" class="nav-link" href="/tags">
|
||||
<span class="nav-link-identifier">Tags</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a id="random" class="nav-link" href="/random">
|
||||
@ -108,6 +101,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- rechte seite -->
|
||||
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
||||
<div class="pagination-container-fluid">
|
||||
<div class="pagination-wrapper">
|
||||
|
@ -1,33 +0,0 @@
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<a class="navbar-brand" href="/"><span class="f0ck">F0CK</span></a>
|
||||
<div class="navigation-links">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown" id="themes">
|
||||
<a class="nav-link" href="#" content="{{ theme }}" data-toggle="dropdown">Theme</a>
|
||||
<ul class="dropdown-menu">
|
||||
@each(themes as t)
|
||||
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
|
||||
@endeach
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/sessions">
|
||||
<span class="nav-link-identifier">sessions</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/log">
|
||||
<span class="nav-link-identifier">Log</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/recover">
|
||||
<span class="nav-link-identifier">Recover</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/logout">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
28
views/tags.html
Normal file
@ -0,0 +1,28 @@
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<div class="container">
|
||||
<h3 style="text-align: center;">☯</h3>
|
||||
<div class="tags">
|
||||
@if(session)
|
||||
@each(toptags_regged as toptag)
|
||||
<div class="tag badge badge-light mr-2">
|
||||
<div class="tagbox-body">
|
||||
<span class="toptag_id">{!! toptag.tag !!}</span>
|
||||
<span class="toptag_tag"><a href="/tag/{!! toptag.tag !!}">{{ toptag.total_items }}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
@endeach
|
||||
@else
|
||||
@each(toptags as toptag)
|
||||
<div class="tag badge badge-light mr-2">
|
||||
<div class="tagbox-body">
|
||||
<span class="toptag_id">{!! toptag.tag !!}</span>
|
||||
<span class="toptag_tag"><a href="/tag/{!! toptag.tag !!}">{{ toptag.total_items }}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
@endeach
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
56
views/terms.html
Normal file
@ -0,0 +1,56 @@
|
||||
@include(snippets/header)
|
||||
<div id="main">
|
||||
<div class="tos">
|
||||
<p>Terms of Service</p>
|
||||
<ol>
|
||||
<li>Acceptance of Terms</li>
|
||||
<p>By accessing and using this website, you acknowledge that your access is a privilege, not a right. If you do not agree with these terms, you are free to leave at any time.</p>
|
||||
<li>No Claims</li>
|
||||
<p>Visitors to this website have no claims whatsoever against the website owner or operators. Access to the website and its content is provided as-is, with no guarantees, warranties, or entitlements of any kind.</p>
|
||||
<li>No Liability</li>
|
||||
<p>This website and its operators assume no liability for any errors, omissions, inaccuracies, or any other issues that may arise from the use of this site. Use of this website is entirely at your own risk.</p>
|
||||
<li>No Warranty</li>
|
||||
<p>There is no warranty regarding the completeness, accuracy, reliability, or availability of the content provided on this website. The content may change at any time without notice.</p>
|
||||
<li>Compliance with Requests</li>
|
||||
<p>The website owner reserves the right to remove content, restrict access, or comply with any valid legal or personal requests at their sole discretion.</p>
|
||||
<li>Changes to Terms</li>
|
||||
<p>These terms may be updated at any time without prior notice. It is your responsibility to review them periodically.</p>
|
||||
</ol>
|
||||
<p>Data Privacy</p>
|
||||
<ol>
|
||||
<li>No Data Logging</li>
|
||||
<p>This website does not collect, store, or log any personal data, including IP addresses or other identifying information of its visitors. No server-side logs are maintained.</p>
|
||||
|
||||
<li>Use of Cookies</li>
|
||||
<p>Upon changing the theme, a single cookie is set. This cookie solely stores the name of the currently active theme to enhance the visual experience. It does not contain any personal data, tracking information, or other identifiers.</p>
|
||||
|
||||
<li>Cookie Control</li>
|
||||
<p>The cookie is purely of cosmetic nature and not essential for the website's functionality. Users can disable cookies for this website entirely via their browser settings without affecting their ability to access and use the site.</p>
|
||||
|
||||
<li>No Third-Party Tracking</li>
|
||||
<p>This website does not use third-party tracking services, analytics tools, or embedded content that collects user data.</p>
|
||||
|
||||
<li>User Accounts</li>
|
||||
<p>When a former visitor is granted access with an account, the following data is collected:</p>
|
||||
<ul>
|
||||
<li>The User Agent</li>
|
||||
<li>The Timestamp of the first login</li>
|
||||
<li>The Timestamp of the account's last usage</li>
|
||||
<li>The User's last recorded action</li>
|
||||
</ul>
|
||||
|
||||
<li>Email Communication</li>
|
||||
<p>If you send me an email your mail is stored on our server, we can make a connection to your Email-Address and your user account if you contact us this way.</p>
|
||||
<p>The Emails are not deleted after being answered.</p>
|
||||
|
||||
<li>Fully complying with Art. 15 GDPR</li>
|
||||
<p>You can ask anytime what data we have of you and how we use it, see Email Communication too.</p>
|
||||
|
||||
<li>Changes to This Policy</li>
|
||||
<p>This privacy policy may be updated from time to time. Users are encouraged to review it periodically to stay informed about any changes.</p>
|
||||
|
||||
<p>By using this website, you acknowledge and accept the terms of service and the data privacy policy.</p>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|
14
views/top10.html
Normal file
@ -0,0 +1,14 @@
|
||||
@include(snippets/header)
|
||||
|
||||
<h3>Top10 ({{ year }}-{{ month }})</h3>
|
||||
<table class="table">
|
||||
@each(f0cks as f0ck)
|
||||
<tr>
|
||||
<td><a href="//w0bm.com/{{ f0ck.id }}"><img src="//w0bm.com/t/{{ f0ck.id }}.webp" /></a></td>
|
||||
<td><a href="//w0bm.com/{{ f0ck.id }}">{{ f0ck.id }}</a></td>
|
||||
<td>{{ f0ck.username }}</td>
|
||||
</tr>
|
||||
@endeach
|
||||
</table>
|
||||
|
||||
@include(snippets/footer)
|
@ -1,28 +1,49 @@
|
||||
@include(snippets/header)
|
||||
<h1>{{ user.user }}</h1>
|
||||
|
||||
<h2>f0cks:</h2>
|
||||
@if('items' in f0cks)
|
||||
<div class="posts">
|
||||
<div id="main">
|
||||
<div class="profile_head">
|
||||
@if(user.avatar)
|
||||
<div class="profile_head_avatar">
|
||||
<img src="/t/{{ user.avatar }}.webp" style="display: grid;width: 55px" />
|
||||
</div>
|
||||
@endif
|
||||
<div class="layersoffear">
|
||||
<div class="profile_head_username">
|
||||
<span>@if(user.admin)⭐ @endif{{ user.user }}</span>
|
||||
</div>
|
||||
<div class="profile_head_user_stats">
|
||||
ID: {{ user.user_id }} – Joined: {{ user.created_at }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user_content_wrapper">
|
||||
<div class="f0cks">
|
||||
<div class="f0cks-header">
|
||||
f0ck{{ count.f0cks == 1 ? '' : 's' }}: {{ count.f0cks }} <a href="{{ f0cks.link?.main }}">view all</a>
|
||||
</div>
|
||||
@if(count.f0cks)
|
||||
<div class="posts">
|
||||
@each(f0cks.items as item)
|
||||
<a href="{{ f0cks.link.main }}{{ item.id }}" data-mime="{{ item.mime }}" data-mode="{{ item.tag_id ? ['','sfw','nsfw'][item.tag_id] : 'null' }}" style="background-image: url('/t/{{ item.id }}.webp')"><p></p></a>
|
||||
@endeach
|
||||
</div>
|
||||
<a href="{{ f0cks.link.main }}">show all f0cks</a>
|
||||
@else
|
||||
</div>
|
||||
@else
|
||||
no f0cks given
|
||||
@endif
|
||||
|
||||
<h2>favs:</h2>
|
||||
@if('items' in favs)
|
||||
<div class="posts">
|
||||
@endif
|
||||
</div>
|
||||
<div class="favs">
|
||||
<div class="favs-header">
|
||||
fav{{ count.favs == 1 ? '' : 's' }}: {{ count.favs }} <a href="{{ favs.link?.main }}">view all</a>
|
||||
</div>
|
||||
@if(count.favs)
|
||||
<div class="posts">
|
||||
@each(favs.items as item)
|
||||
<a href="{{ favs.link.main }}{{ item.id }}" data-mime="{{ item.mime }}" data-mode="{{ item.tag_id ? ['','sfw','nsfw'][item.tag_id] : 'null' }}" style="background-image: url('/t/{{ item.id }}.webp')"><p></p></a>
|
||||
@endeach
|
||||
</div>
|
||||
<a href="{{ favs.link.main }}">show all favs</a>
|
||||
@else
|
||||
</div>
|
||||
@else
|
||||
no favorites
|
||||
@endif
|
||||
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include(snippets/footer)
|