Compare commits
331 Commits
06813a87d4
...
f0ck_local
Author | SHA1 | Date | |
---|---|---|---|
00db7c6113 | |||
050c9e66c8 | |||
d134e4d6be | |||
d4ac155c68 | |||
afb38f9af5 | |||
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 | |||
ddaaaee502 | |||
534fb1d92a | |||
cc5ddf27d3 | |||
6b6e44eed7 | |||
e7f6ecd239 | |||
c806410be6 | |||
2c9058dec7 | |||
9239666291 | |||
d13eeea588 | |||
79441cf69a | |||
904a7abd86 | |||
aa0d88424b | |||
201f7969b4 | |||
1da992d6a9 | |||
77a703a5f0 | |||
dc14d2f78f | |||
5fcc337038 | |||
076e98a963 | |||
daab816ad2 | |||
0b6b8c549a | |||
39ba04bcc9 | |||
6b98458710 | |||
729522369c | |||
70f9e6553d | |||
2f7f8950ae | |||
f2ba18d186 | |||
2309c3020a | |||
f8cc9f1dc0 | |||
0754cb97a1 | |||
2c7a55415a | |||
1425361a6f | |||
f8c59111aa | |||
a9f65bb798 | |||
d864679775 | |||
e68af4dc96 | |||
17c029e6da | |||
49fb4bfc56 | |||
c6fbe956a0 | |||
75c4e35fdc | |||
d2091494a1 | |||
53a0880c55 | |||
74431a2a29 | |||
af4c48f351 | |||
d0f8cb5acb | |||
29d5b20f5b | |||
86b13f5173 | |||
5bb3fefe52 | |||
41194ca08a | |||
314c476f8d | |||
a575f52005 | |||
2ab187c780 | |||
fa0496949c | |||
4d93ef1152 | |||
f8dffcdfc9 | |||
3813af2c46 | |||
3ab534ee8e | |||
3e7ebba6fd | |||
757c5aa4b4 | |||
7cc6776773 | |||
53ea6389c7 | |||
edfdd7ebbe | |||
08698ee886 | |||
e5b9bdb721 | |||
961ec2cf55 | |||
8bee86c002 | |||
9aec26f4d3 | |||
e16138583e | |||
10ef57b0bf | |||
9f4432a942 | |||
b775d55fba | |||
c33e57c9c0 | |||
d315ae3efa | |||
dc1d658c9a | |||
d4811c3d5c | |||
9be8b3f982 | |||
1dacafdb3a | |||
3bc7cdcd1c | |||
dfea411e1b | |||
3a899e4911 | |||
7a96f26f84 | |||
412a4a348a | |||
587bebbd6a | |||
3aa14e4bfb | |||
522d4903ba | |||
6d795652a7 | |||
9c86d34b87 | |||
34e93b09c5 | |||
48fd0e5ab0 | |||
7df97485e6 | |||
8adee68835 | |||
0bda378993 | |||
b03d265a94 | |||
d78e90ddc0 | |||
a0962d8d5c | |||
58d8268207 | |||
002b796cbc | |||
d6561a66b8 | |||
b82044555d | |||
45792787ba | |||
81f4817f03 | |||
e0618443c0 | |||
6eddad7e0e | |||
6f71101b9e | |||
a33d1606ae | |||
d7abc4e797 | |||
5bcd797e6c | |||
530d0938c6 | |||
7f5ef839cc | |||
a106a31c47 | |||
14c6816c47 | |||
643cf0de76 | |||
c18c6b9283 | |||
4a7c7f8730 | |||
173321f5c8 | |||
6f12cbca32 | |||
a460b974a8 | |||
09d647cc48 | |||
d56c143e80 | |||
b9fbcb4187 | |||
1f761ec62b | |||
c0f8f6c536 | |||
881d44158e | |||
e159776e9b | |||
44ca53a050 | |||
97ef147160 | |||
f57a8b4320 | |||
ba821b81fa | |||
29acafe918 | |||
dc96a2578a | |||
9fc920c1d2 | |||
eb16487d73 | |||
501f68c481 | |||
4c94cac3ef | |||
5be4670ba3 | |||
50b983536c | |||
f0ee736e40 | |||
bb6f4dc2b8 | |||
b960acb0ed | |||
7621f6ca48 | |||
b8e69af878 | |||
be64a42d68 | |||
3d35367302 | |||
80c7d2f3d7 | |||
8dfee166b4 | |||
7a007c95e7 | |||
fd685a6c16 | |||
75fa6e23f5 | |||
bafb0916e2 | |||
fda30ebdee | |||
eb5d4ccfbe | |||
ce72c6f265 | |||
cba529deb4 | |||
8dd58553e2 | |||
8f4ea66cd9 | |||
00837600bb | |||
b76f6439fa | |||
17946742ec | |||
ae0d26aaa7 | |||
9d7b701e96 | |||
77c00de69a | |||
a7e2cb0dd9 | |||
9100f64d81 | |||
1222837d4d | |||
2447d62dac | |||
ac3647aa7f | |||
429b5003d5 | |||
da5d7285a4 | |||
1bc7085e68 | |||
f22b8b4e0d | |||
617385b4b6 | |||
1484c646e8 | |||
dd95c52de6 | |||
f63556e713 | |||
16d85ae5ad | |||
2ae986ccf1 | |||
4f5d54ee19 | |||
a97ed32c9f | |||
94c237561c | |||
7e9599adce | |||
9dfffbb8bf | |||
b0acc1731d | |||
647991b5e9 | |||
78b847ddae | |||
1a3af07309 | |||
8f5680e50f | |||
add3c7a648 | |||
e7ee063021 | |||
d57908f82e | |||
659adca258 | |||
46d8bd45a8 | |||
3731d1d785 | |||
872abd7f73 | |||
2a94a84c44 | |||
1787a69143 | |||
ac96827ad1 | |||
2e0881b109 | |||
315770bf30 | |||
6de804561f | |||
f4815d95e5 | |||
7e1129a567 | |||
1e01a521e4 | |||
9541d9e0b8 | |||
fb744fe341 | |||
a6b881a196 | |||
1be456edb4 | |||
1066e8772b | |||
4ebcbdd886 | |||
73577441d1 | |||
1a8aa81a0b | |||
86409c9d98 | |||
22b171c858 | |||
155d496592 | |||
82702d786a |
43
.gitea/workflows/build.yaml
Normal file
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
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,7 +1,10 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
logs/*.log
|
logs/*.log
|
||||||
config.json
|
config.json
|
||||||
public/b/*
|
public/b
|
||||||
public/ca/*
|
public/ca
|
||||||
public/t/*
|
public/t
|
||||||
|
deleted/b
|
||||||
|
deleted/ca
|
||||||
|
deleted/t
|
||||||
tmp/*
|
tmp/*
|
||||||
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# how to install:
|
||||||
|
## dependencies
|
||||||
|
```bash
|
||||||
|
sudo pacman -S nodejs npm ffmpeg yt-dlp ffmpegthumbnailer postgresql imagemagick git mime-types
|
||||||
|
```
|
||||||
|
## postgres
|
||||||
|
```bash
|
||||||
|
sudo -u postgres initdb --locale en_US.UTF-8 -D '/var/lib/postgres/data'
|
||||||
|
#(if it fails: sudo localectl set-locale en_US.UTF-8)
|
||||||
|
sudo systemctl enable --now postgresql
|
||||||
|
#(if you're retarded or lazy, append postgresql and postgresql-libs to your ignorepkg)
|
||||||
|
sudo -u postgres createuser -S -D -R -e f0ck
|
||||||
|
sudo -u postgres createdb f0ck -O f0ck
|
||||||
|
```
|
||||||
|
## install f0ck
|
||||||
|
```bash
|
||||||
|
sudo useradd f0ck -m
|
||||||
|
sudo -iu f0ck
|
||||||
|
cd ~
|
||||||
|
git clone https://git.lat/f0ck/f0ckv2.git
|
||||||
|
cd f0ckv2
|
||||||
|
#(for developers: git checkout dev)
|
||||||
|
npm i
|
||||||
|
psql f0ck < f0ck.sql
|
||||||
|
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
|
||||||
|
npm start
|
||||||
|
```
|
76
config_example.json
Normal file
76
config_example.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"main": {
|
||||||
|
"url": {
|
||||||
|
"full": "https://f0ck.dev",
|
||||||
|
"domain": "f0ck.dev",
|
||||||
|
"regex": "f0ck\\.dev"
|
||||||
|
},
|
||||||
|
"maxfilesize": 83886080,
|
||||||
|
"adminmultiplier": 3.5,
|
||||||
|
"ignored": [
|
||||||
|
"f0ck.dev",
|
||||||
|
"f0ck.me"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"allowedModes": [ "sfw", "nsfw", "untagged", "all" ],
|
||||||
|
"allowedMimes": [ "audio", "image", "video" ],
|
||||||
|
"nsfp": [],
|
||||||
|
"websrv": {
|
||||||
|
"port": "8080",
|
||||||
|
"paths": {
|
||||||
|
"images": "/b",
|
||||||
|
"thumbnails": "/t",
|
||||||
|
"coverarts": "/ca"
|
||||||
|
},
|
||||||
|
"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": [{
|
||||||
|
"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": "f0ck",
|
||||||
|
"database": "f0ck",
|
||||||
|
"schema": "public",
|
||||||
|
"multipleStatements": true
|
||||||
|
},
|
||||||
|
"admins": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"mimes": {
|
||||||
|
"image/png": "png",
|
||||||
|
"video/webm": "webm",
|
||||||
|
"image/gif": "gif",
|
||||||
|
"image/jpeg": "jpg",
|
||||||
|
"image/webp": "webp",
|
||||||
|
"video/mp4": "mp4",
|
||||||
|
"video/quicktime": "mp4",
|
||||||
|
"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-matroska": "mkv"
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,7 @@ import lib from "../src/inc/lib.mjs";
|
|||||||
db({
|
db({
|
||||||
item_id: f.id,
|
item_id: f.id,
|
||||||
tag_id: tmp.nsfw ? 2 : 1,
|
tag_id: tmp.nsfw ? 2 : 1,
|
||||||
user_id: 7
|
user_id: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -46,8 +46,8 @@ import lib from "../src/inc/lib.mjs";
|
|||||||
insert into "tags_assign" ${
|
insert into "tags_assign" ${
|
||||||
db({
|
db({
|
||||||
item_id: f.id,
|
item_id: f.id,
|
||||||
tag_id: 8, // hentai
|
tag_id: 4, // hentai
|
||||||
user_id: 7 // autotagger
|
user_id: 1 // autotagger
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
110
debug/clean.mjs
110
debug/clean.mjs
@ -1,33 +1,95 @@
|
|||||||
import sql from "../src/inc/sql.mjs";
|
import cfg from "../src/inc/config.mjs";
|
||||||
import { promises as fs } from "fs";
|
import db from "../src/inc/sql.mjs";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import readline from 'node:readline/promises';
|
||||||
|
|
||||||
const opts = {
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||||
b: "public/b",
|
|
||||||
t: "public/t",
|
|
||||||
tmp: "tmp"
|
|
||||||
};
|
|
||||||
const count = {
|
|
||||||
b: 0, t: 0, tmp: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const rows = await sql('items').select('id', 'dest');
|
// npm run clean -- --dry-run
|
||||||
const ids = rows.map(r => r.id.toString() + ".webp");
|
const dry = !!process.argv.filter(a => a == '--dry-run').length;
|
||||||
const dests = rows.map(r => r.dest);
|
console.log(`dry run? ${dry}`);
|
||||||
|
|
||||||
|
const dirs = {
|
||||||
|
b: "./public/b",
|
||||||
|
t: "./public/t",
|
||||||
|
tmp: "./tmp"
|
||||||
|
};
|
||||||
const files = {
|
const files = {
|
||||||
b: (await fs.readdir(opts.b)).filter(f => f !== '.empty'),
|
b: (await fs.promises.readdir(dirs.b)) .filter(f => f !== '.empty'),
|
||||||
t: await fs.readdir(opts.t)
|
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 = {
|
// missing
|
||||||
b: files.b.filter(f => !dests.includes(f)),
|
for(const row of rows) {
|
||||||
t: files.t.filter(f => !ids.includes(f))
|
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;
|
// invalid
|
||||||
count.t = (await Promise.all(unused.t.map(f => fs.rm(`${opts.t}/${f}`)))).length;
|
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
|
// spare
|
||||||
const tmp = (await fs.readdir(opts.tmp)).filter(f => f !== '.empty');
|
for(const file of files.b)
|
||||||
count.tmp = (await Promise.all(tmp.map(f => fs.rm(`${opts.tmp}/${f}`)))).length;
|
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();
|
||||||
|
215
f0ck.sql
Normal file
215
f0ck.sql
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
\connect "f0ck";
|
||||||
|
|
||||||
|
SET statement_timeout = 0;
|
||||||
|
SET lock_timeout = 0;
|
||||||
|
SET idle_in_transaction_session_timeout = 0;
|
||||||
|
SET client_encoding = 'UTF8';
|
||||||
|
SET standard_conforming_strings = on;
|
||||||
|
SELECT pg_catalog.set_config('search_path', '', false);
|
||||||
|
SET check_function_bodies = false;
|
||||||
|
SET xmloption = content;
|
||||||
|
SET client_min_messages = warning;
|
||||||
|
SET row_security = off;
|
||||||
|
|
||||||
|
CREATE EXTENSION unaccent;
|
||||||
|
CREATE FUNCTION public.delete_unused_tags() RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
begin
|
||||||
|
delete from tags
|
||||||
|
where
|
||||||
|
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';
|
||||||
|
return OLD;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
ALTER FUNCTION public.delete_unused_tags() OWNER TO f0ck;
|
||||||
|
|
||||||
|
CREATE FUNCTION public.fill_normalized() RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
begin
|
||||||
|
NEW.normalized = slugify(NEW.tag);
|
||||||
|
return NEW;
|
||||||
|
end$$;
|
||||||
|
|
||||||
|
ALTER FUNCTION public.fill_normalized() OWNER TO f0ck;
|
||||||
|
|
||||||
|
CREATE FUNCTION public.slugify(v text) RETURNS text
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN trim(BOTH '-' FROM regexp_replace(lower(unaccent(trim(v))), '[\u0000-\u002f \u003a-\u0040\u005b-\u0060\u007b-\u00bf]+', '', 'gi'));
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
ALTER FUNCTION public.slugify(v text) OWNER TO f0ck;
|
||||||
|
|
||||||
|
CREATE TABLE public.favorites (
|
||||||
|
user_id integer NOT NULL,
|
||||||
|
item_id integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE public.favorites OWNER TO f0ck;
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.items_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER TABLE public.items_id_seq OWNER TO f0ck;
|
||||||
|
|
||||||
|
CREATE TABLE public.items (
|
||||||
|
id integer DEFAULT nextval('public.items_id_seq'::regclass) NOT NULL,
|
||||||
|
src character varying(255) NOT NULL,
|
||||||
|
dest character varying(40) NOT NULL,
|
||||||
|
mime character varying(100) NOT NULL,
|
||||||
|
size integer NOT NULL,
|
||||||
|
checksum character varying(255) NOT NULL,
|
||||||
|
username character varying(40) NOT NULL,
|
||||||
|
userchannel character varying(100) 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;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN public.items.src IS 'src-Link';
|
||||||
|
COMMENT ON COLUMN public.items.dest IS 'filename';
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.tags_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER TABLE public.tags_id_seq OWNER TO 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
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.user_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER TABLE public.user_id_seq OWNER TO 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
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE public."user" OWNER TO 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,
|
||||||
|
fullscreen integer DEFAULT 0 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;
|
||||||
|
|
||||||
|
ALTER TABLE public.user_sessions_id_seq OWNER TO f0ck;
|
||||||
|
|
||||||
|
CREATE TABLE public.user_sessions (
|
||||||
|
id integer DEFAULT nextval('public.user_sessions_id_seq'::regclass) NOT NULL,
|
||||||
|
user_id integer NOT NULL,
|
||||||
|
session character varying(32) NOT NULL,
|
||||||
|
browser character varying(255) NOT NULL,
|
||||||
|
created_at integer NOT NULL,
|
||||||
|
last_used integer NOT NULL,
|
||||||
|
last_action character varying(255) NOT NULL,
|
||||||
|
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
|
||||||
|
\.
|
||||||
|
|
||||||
|
COPY public.tags (id, tag, normalized) FROM stdin;
|
||||||
|
1 sfw sfw
|
||||||
|
2 nsfw nsfw
|
||||||
|
3 audio audio
|
||||||
|
4 hentai hentai
|
||||||
|
\.
|
||||||
|
|
||||||
|
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
|
||||||
|
\.
|
||||||
|
|
||||||
|
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();
|
||||||
|
CREATE TRIGGER tags_bi BEFORE INSERT ON public.tags FOR EACH ROW EXECUTE FUNCTION public.fill_normalized();
|
||||||
|
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;
|
BIN
nsfw_model.h5
BIN
nsfw_model.h5
Binary file not shown.
55
package-lock.json
generated
55
package-lock.json
generated
@ -1,26 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "f0ckv2",
|
"name": "f0ckv2",
|
||||||
"version": "2.2.0",
|
"version": "2.2.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "f0ckv2",
|
"name": "f0ckv2",
|
||||||
"version": "2.2.0",
|
"version": "2.2.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cuffeo": "^1.0.7-3",
|
"cuffeo": "^1.2.2",
|
||||||
"flumm-fetch": "^1.0.1",
|
"flumm-fetch": "^1.0.1",
|
||||||
"flummpress": "^2.0.5",
|
"flummpress": "^2.0.5",
|
||||||
"postgres": "^3.0.1"
|
"postgres": "^3.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cuffeo": {
|
"node_modules/cuffeo": {
|
||||||
"version": "1.0.7-3",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.0.7-3.tgz",
|
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.2.2.tgz",
|
||||||
"integrity": "sha512-Lz8AlLdFWeLLGsf6KBXTHnpseeMbuQH69BamhZr8O9Se9pzuHQgv/ed13n2XWreN0RTyRa49YPtsNVwoQnNrLw==",
|
"integrity": "sha512-Pd2AL/Zp5RCAbaSbT7rLUOyxSEVCFovihaIaje7uYzpQMzIwPbFapQ/mn2d2iz+Tbkjs9LF+FD5JzAvANh9Wzw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flumm-fetch-cookies": "^1.4.0"
|
"flumm-fetch": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flumm-fetch": {
|
"node_modules/flumm-fetch": {
|
||||||
@ -28,26 +28,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
|
||||||
"integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg=="
|
"integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg=="
|
||||||
},
|
},
|
||||||
"node_modules/flumm-fetch-cookies": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/flumm-fetch-cookies/-/flumm-fetch-cookies-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-OHGUak5iHl9GoDdbkAMsrL9ONkFQ8opd0jYQ9lMUuGVqIP+JPbyzQqElhpwJxLaV/WSE4LR/2dnHoguFHFSLFA==",
|
|
||||||
"dependencies": {
|
|
||||||
"flumm-fetch": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=11.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/flummpress": {
|
"node_modules/flummpress": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
|
||||||
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
|
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
|
||||||
},
|
},
|
||||||
"node_modules/postgres": {
|
"node_modules/postgres": {
|
||||||
"version": "3.1.0",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
|
||||||
"integrity": "sha512-yQbJtA7z6SsQuFiF01rmHlkG2gU5IEv9D/Pn2fu8Tz6x3/suNWZs4fIPd59rkL+dfVfhDqYQ7DU3YnUqzEDB5Q==",
|
"integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
"url": "https://github.com/sponsors/porsager"
|
"url": "https://github.com/sponsors/porsager"
|
||||||
@ -56,11 +45,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cuffeo": {
|
"cuffeo": {
|
||||||
"version": "1.0.7-3",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.0.7-3.tgz",
|
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.2.2.tgz",
|
||||||
"integrity": "sha512-Lz8AlLdFWeLLGsf6KBXTHnpseeMbuQH69BamhZr8O9Se9pzuHQgv/ed13n2XWreN0RTyRa49YPtsNVwoQnNrLw==",
|
"integrity": "sha512-Pd2AL/Zp5RCAbaSbT7rLUOyxSEVCFovihaIaje7uYzpQMzIwPbFapQ/mn2d2iz+Tbkjs9LF+FD5JzAvANh9Wzw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"flumm-fetch-cookies": "^1.4.0"
|
"flumm-fetch": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flumm-fetch": {
|
"flumm-fetch": {
|
||||||
@ -68,23 +57,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
|
||||||
"integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg=="
|
"integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg=="
|
||||||
},
|
},
|
||||||
"flumm-fetch-cookies": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/flumm-fetch-cookies/-/flumm-fetch-cookies-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-OHGUak5iHl9GoDdbkAMsrL9ONkFQ8opd0jYQ9lMUuGVqIP+JPbyzQqElhpwJxLaV/WSE4LR/2dnHoguFHFSLFA==",
|
|
||||||
"requires": {
|
|
||||||
"flumm-fetch": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flummpress": {
|
"flummpress": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
|
||||||
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
|
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
|
||||||
},
|
},
|
||||||
"postgres": {
|
"postgres": {
|
||||||
"version": "3.1.0",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
|
||||||
"integrity": "sha512-yQbJtA7z6SsQuFiF01rmHlkG2gU5IEv9D/Pn2fu8Tz6x3/suNWZs4fIPd59rkL+dfVfhDqYQ7DU3YnUqzEDB5Q=="
|
"integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
package.json
19
package.json
@ -1,22 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "f0ckv2",
|
"name": "f0ckv2",
|
||||||
"version": "2.2.0",
|
"version": "2.2.1",
|
||||||
"description": "f0ck, kennste?",
|
"description": "f0ck, kennste?",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --experimental-json-modules src/index.mjs",
|
"start": "node --trace-uncaught src/index.mjs",
|
||||||
"trigger": "node --experimental-json-modules debug/trigger.mjs",
|
"trigger": "node debug/trigger.mjs",
|
||||||
"autotagger": "node --experimental-json-modules debug/autotagger.mjs",
|
"autotagger": "node debug/autotagger.mjs",
|
||||||
"thumbnailer": "node --experimental-json-modules debug/thumbnailer.mjs",
|
"thumbnailer": "node debug/thumbnailer.mjs",
|
||||||
"test": "node --experimental-json-modules debug/test.mjs",
|
"test": "node debug/test.mjs",
|
||||||
"clean": "node --experimental-json-modules debug/clean.mjs"
|
"clean": "node debug/clean.mjs"
|
||||||
},
|
},
|
||||||
"author": "Flummi",
|
"author": "Flummi",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cuffeo": "^1.0.7-3",
|
"cuffeo": "^1.2.2",
|
||||||
"flumm-fetch": "^1.0.1",
|
"flumm-fetch": "^1.0.1",
|
||||||
"flummpress": "^2.0.5",
|
"flummpress": "^2.0.5",
|
||||||
"postgres": "^3.0.1"
|
"postgres": "^3.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
public/b/.empty
Executable file → Normal file
0
public/b/.empty
Executable file → Normal file
BIN
public/b/b761fa9339.png
Executable file
BIN
public/b/b761fa9339.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
File diff suppressed because it is too large
Load Diff
@ -72,7 +72,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
.v0ck_player_controls svg {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
<!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
||||||
<defs>
|
<defs>
|
||||||
<symbol style="fill: var(--accent)" 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_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 style="fill: var(--accent)" 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="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;stroke: var(--accent);"/></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>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.9 KiB |
@ -85,7 +85,7 @@ const flash = ({ type, msg }) => {
|
|||||||
[...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag));
|
[...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag));
|
||||||
_tags.reverse().forEach(tag => {
|
_tags.reverse().forEach(tag => {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = `/tag/${tag.tag}`;
|
a.href = `/tag/${tag.normalized}`;
|
||||||
a.style = "color: inherit !important";
|
a.style = "color: inherit !important";
|
||||||
a.innerHTML = tag.tag;
|
a.innerHTML = tag.tag;
|
||||||
a.addEventListener("click", editTagEvent); // tmp
|
a.addEventListener("click", editTagEvent); // tmp
|
||||||
@ -101,6 +101,7 @@ const flash = ({ type, msg }) => {
|
|||||||
delbutton.href = "#";
|
delbutton.href = "#";
|
||||||
delbutton.addEventListener("click", deleteEvent);
|
delbutton.addEventListener("click", deleteEvent);
|
||||||
span.insertAdjacentElement("beforeend", a);
|
span.insertAdjacentElement("beforeend", a);
|
||||||
|
span.innerHTML += ' ';
|
||||||
span.insertAdjacentElement("beforeend", delbutton);
|
span.insertAdjacentElement("beforeend", delbutton);
|
||||||
|
|
||||||
document.querySelector("#tags").insertAdjacentElement("afterbegin", span);
|
document.querySelector("#tags").insertAdjacentElement("afterbegin", span);
|
||||||
@ -287,6 +288,7 @@ const flash = ({ type, msg }) => {
|
|||||||
|
|
||||||
textfield.addEventListener("keyup", async e => {
|
textfield.addEventListener("keyup", async e => {
|
||||||
if(e.key === 'Enter') {
|
if(e.key === 'Enter') {
|
||||||
|
parent.removeChild(textfield);
|
||||||
// send
|
// send
|
||||||
let res = await fetch('/api/v2/admin/tags/' + encodeURIComponent(oldtag), {
|
let res = await fetch('/api/v2/admin/tags/' + encodeURIComponent(oldtag), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@ -303,7 +305,7 @@ const flash = ({ type, msg }) => {
|
|||||||
switch(status) {
|
switch(status) {
|
||||||
case 200: // success, change
|
case 200: // success, change
|
||||||
case 201:
|
case 201:
|
||||||
parent.removeChild(textfield);
|
//parent.removeChild(textfield);
|
||||||
parent.insertAdjacentElement('afterbegin', old);
|
parent.insertAdjacentElement('afterbegin', old);
|
||||||
parent.querySelector('a:last-child').style.display = '';
|
parent.querySelector('a:last-child').style.display = '';
|
||||||
old.href = `/tag/${res.tag}`;
|
old.href = `/tag/${res.tag}`;
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
|
||||||
|
window.requestAnimFrame = (function(){
|
||||||
|
return window.requestAnimationFrame
|
||||||
|
|| window.webkitRequestAnimationFrame
|
||||||
|
|| window.mozRequestAnimationFrame
|
||||||
|
|| function(callback) { window.setTimeout(callback, 1000 / 60);};
|
||||||
|
})();
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
let video;
|
let video;
|
||||||
if(elem = document.querySelector("#my-video")) {
|
if(elem = document.querySelector("#my-video")) {
|
||||||
@ -8,6 +16,42 @@
|
|||||||
document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
|
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;
|
let tt = false;
|
||||||
@ -29,6 +73,8 @@
|
|||||||
};
|
};
|
||||||
document.addEventListener("keydown", e => {
|
document.addEventListener("keydown", e => {
|
||||||
if(e.key in keybindings && e.target.tagName !== "INPUT") {
|
if(e.key in keybindings && e.target.tagName !== "INPUT") {
|
||||||
|
if(e.shiftKey || e.ctrlKey || e.metaKey || e.altKey)
|
||||||
|
return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
keybindings[e.key]();
|
keybindings[e.key]();
|
||||||
}
|
}
|
||||||
@ -44,19 +90,44 @@
|
|||||||
i.src = e.src;
|
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")) {
|
if(f0ckimage = document.querySelector("img#f0ck-image")) {
|
||||||
const f0ckimagescroll = document.querySelector("#image-scroll");
|
const f0ckimagescroll = document.querySelector("#image-scroll");
|
||||||
|
|
||||||
|
let isImageExpanded = false;
|
||||||
|
console.log("entry point - image unclicked")
|
||||||
|
|
||||||
f0ckimage.addEventListener("click", async e => {
|
f0ckimage.addEventListener("click", async e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const img = await imgSize(f0ckimage);
|
const img = await imgSize(f0ckimage);
|
||||||
if(img.width > img.height)
|
console.log("img clicked");
|
||||||
return;
|
if (isImageExpanded) {
|
||||||
f0ckimagescroll.hasAttribute("style")
|
isImageExpanded = false;
|
||||||
? f0ckimagescroll.removeAttribute("style")
|
f0ckimagescroll.removeAttribute("style");
|
||||||
: f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
|
f0ckimage.removeAttribute("style");
|
||||||
f0ckimage.hasAttribute("style")
|
console.log("image is not expanded")
|
||||||
? f0ckimage.removeAttribute("style")
|
window.addEventListener('wheel', wheelEventListener);
|
||||||
: f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0;");
|
} 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>
|
// </image-responsive>
|
||||||
@ -64,13 +135,13 @@
|
|||||||
// <scroller>
|
// <scroller>
|
||||||
let tts = 0;
|
let tts = 0;
|
||||||
const scroll_treshold = 1;
|
const scroll_treshold = 1;
|
||||||
if(document.querySelector("div#posts")) {
|
if([...document.querySelectorAll("div.posts")].length === 1) {
|
||||||
document.addEventListener("wheel", e => {
|
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(elem = document.querySelector(".pagination > .next:not(.disabled)")) {
|
||||||
if(tts < scroll_treshold) {
|
if(tts < scroll_treshold) {
|
||||||
document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--accent)";
|
document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--footbar-color)";
|
||||||
document.querySelector("div#footbar").style.color = "var(--accent)";
|
document.querySelector("div#footbar").style.color = "var(--footbar-color)";
|
||||||
tts++;
|
tts++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -80,7 +151,7 @@
|
|||||||
else if(window.scrollY <= 0 && e.deltaY < 0) { // up
|
else if(window.scrollY <= 0 && e.deltaY < 0) { // up
|
||||||
if(elem = document.querySelector(".pagination > .prev:not(.disabled)")) {
|
if(elem = document.querySelector(".pagination > .prev:not(.disabled)")) {
|
||||||
if(tts < scroll_treshold) {
|
if(tts < scroll_treshold) {
|
||||||
document.querySelector("nav.navbar").style.boxShadow = "0px 2px 0px var(--accent)";
|
document.querySelector("nav.navbar").style.boxShadow = "0px 2px 0px var(--loading-indicator-color)";
|
||||||
document.querySelector("nav.navbar").style.transition = ".2s ease-in-out";
|
document.querySelector("nav.navbar").style.transition = ".2s ease-in-out";
|
||||||
tts++;
|
tts++;
|
||||||
}
|
}
|
||||||
@ -150,10 +221,10 @@
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(Math.abs(swipeRT.yDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) {
|
if(Math.abs(swipeRT.yDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) {
|
||||||
if(navbar = document.querySelector("nav.navbar") && document.querySelector("div#posts")) {
|
if(navbar = document.querySelector("nav.navbar") && document.querySelector("div.posts")) {
|
||||||
if(swipeRT.yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up
|
if(swipeRT.yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up
|
||||||
elem = document.querySelector(".pagination > .next:not(.disabled)");
|
elem = document.querySelector(".pagination > .next:not(.disabled)");
|
||||||
else if(swipeRT.yDiff <= 0 && window.scrollY <= 0 && document.querySelector("div#posts")) // down
|
else if(swipeRT.yDiff <= 0 && window.scrollY <= 0 && document.querySelector("div.posts")) // down
|
||||||
elem = document.querySelector(".pagination > .prev:not(.disabled)");
|
elem = document.querySelector(".pagination > .prev:not(.disabled)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,4 +303,8 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// </mediakeys>
|
// </mediakeys>
|
||||||
})();
|
|
||||||
|
// <scroller>
|
||||||
|
|
||||||
|
// </scroller>
|
||||||
|
})();
|
@ -8,6 +8,7 @@ const Cookie = {
|
|||||||
opts['max-age'] = opts.days * 60 * 60 * 24;
|
opts['max-age'] = opts.days * 60 * 60 * 24;
|
||||||
delete opts.days;
|
delete opts.days;
|
||||||
}
|
}
|
||||||
|
opts.SameSite = 'Strict';
|
||||||
opts = Object.entries(opts).reduce((accumulatedStr, [k, v]) => `${accumulatedStr}; ${k}=${v}`, '');
|
opts = Object.entries(opts).reduce((accumulatedStr, [k, v]) => `${accumulatedStr}; ${k}=${v}`, '');
|
||||||
document.cookie = name + '=' + encodeURIComponent(value) + opts
|
document.cookie = name + '=' + encodeURIComponent(value) + opts
|
||||||
}
|
}
|
||||||
@ -44,4 +45,21 @@ const Cookie = {
|
|||||||
Cookie.set("theme", themes[i], { path: "/", days: 360 });
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -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" />
|
<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>
|
<button class="v0ck_player_button v0ck_playtime">00:00 / 00:00</button>
|
||||||
<span style="flex: 30"></span>
|
<span style="flex: 30"></span>
|
||||||
|
<button id="togglebg">💡</button>
|
||||||
<button data-skip="-10" class="v0ck_player_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>
|
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_backward" href="${svg}#backward"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
|
BIN
public/t/1.webp
Executable file
BIN
public/t/1.webp
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
32
src/inc/autotagger.mjs
Normal file
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;
|
||||||
|
};
|
||||||
|
};
|
318
src/inc/events/callback_query.mjs
Normal file
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
};
|
@ -20,7 +20,7 @@ export default async bot => {
|
|||||||
|
|
||||||
let trigger;
|
let trigger;
|
||||||
|
|
||||||
if(e.photo) {
|
if(e.media) {
|
||||||
trigger = [...bot._trigger.entries()].filter(t => t[1].name === "parser");
|
trigger = [...bot._trigger.entries()].filter(t => t[1].name === "parser");
|
||||||
if(!e.message)
|
if(!e.message)
|
||||||
e.message = "";
|
e.message = "";
|
||||||
@ -40,7 +40,7 @@ export default async bot => {
|
|||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
console.error(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)}`);
|
logger.error(`${e.network} -> ${e.channel} -> ${e.user.nick}: ${err.toString ? err : JSON.stringify(err)}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -63,7 +63,7 @@ export default new class {
|
|||||||
const link = [];
|
const link = [];
|
||||||
if(env.tag) link.push("tag", env.tag);
|
if(env.tag) link.push("tag", env.tag);
|
||||||
if(env.user) link.push("user", env.user, env.type ?? 'f0cks');
|
if(env.user) link.push("user", env.user, env.type ?? 'f0cks');
|
||||||
if(env.mime.length > 2) link.push(env.mime);
|
if(env.mime?.length > 2) link.push(env.mime);
|
||||||
|
|
||||||
let tmp = link.length === 0 ? '/' : link.join('/');
|
let tmp = link.length === 0 ? '/' : link.join('/');
|
||||||
if(!tmp.endsWith('/'))
|
if(!tmp.endsWith('/'))
|
||||||
@ -84,32 +84,43 @@ export default new class {
|
|||||||
|
|
||||||
// async funcs
|
// async funcs
|
||||||
async countf0cks() {
|
async countf0cks() {
|
||||||
const tagged = (await db`
|
const tagged = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
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;
|
`)[0].total;
|
||||||
const untagged = (await db`
|
const untagged = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
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;
|
`)[0].total;
|
||||||
const sfw = (await db`
|
const sfw = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
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;
|
`)[0].total;
|
||||||
const nsfw = (await db`
|
const nsfw = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
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;
|
`)[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 {
|
return {
|
||||||
tagged,
|
tagged,
|
||||||
untagged,
|
untagged,
|
||||||
total: +tagged + +untagged,
|
total: tagged + untagged,
|
||||||
|
deleted,
|
||||||
|
untracked: lastf0ck - (tagged + untagged + deleted),
|
||||||
sfw,
|
sfw,
|
||||||
nsfw
|
nsfw,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
async hash(str) {
|
async hash(str) {
|
||||||
@ -161,15 +172,25 @@ export default new class {
|
|||||||
}
|
}
|
||||||
return tags;
|
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) {
|
async detectNSFW(dest) {
|
||||||
|
return false;
|
||||||
const { stdout, stderr } = await exec(
|
const { stdout, stderr } = await exec(
|
||||||
`python -c "import sys\nfrom nsfw_detector import predict\nmodel = predict.load_model('./nsfw_model.h5')\nprint(predict.classify(model, './public/b/${dest}'))"`
|
`python -c "import sys\nfrom nsfw_detector import predict\nmodel = predict.load_model('./nsfw_model.h5')\nprint(predict.classify(model, './public/b/${dest}'))"`
|
||||||
);
|
);
|
||||||
const res = JSON.parse(stdout.replace(/\'/g, '"').split('\n').slice(1, -1));
|
const res = JSON.parse(stdout.replace(/\'/g, '"').split('\n').slice(1, -1));
|
||||||
const tmp = Object.values(res)[0];
|
const tmp = Object.values(res)[0];
|
||||||
|
|
||||||
tmp.sexy = tmp.sexy / 2;
|
|
||||||
|
|
||||||
let nsfw = false;
|
let nsfw = false;
|
||||||
if(tmp.neutral >= .7)
|
if(tmp.neutral >= .7)
|
||||||
nsfw = false;
|
nsfw = false;
|
||||||
@ -187,5 +208,15 @@ export default new class {
|
|||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
async getDefaultAvatar() {
|
||||||
|
return (await db`
|
||||||
|
select column_default as avatar
|
||||||
|
from "information_schema"."columns"
|
||||||
|
where
|
||||||
|
TABLE_SCHEMA='public' and
|
||||||
|
TABLE_NAME='user_options' and
|
||||||
|
COLUMN_NAME = 'avatar'
|
||||||
|
`)[0].avatar;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
122
src/inc/queue.mjs
Normal file
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(`convert "./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(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
|
||||||
|
await this.exec(`convert ./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(`convert ./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(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.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(_=>{});
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
@ -4,166 +4,82 @@ import cfg from "../config.mjs";
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import url from "url";
|
import url from "url";
|
||||||
|
|
||||||
|
const globalfilter = cfg.nsfp.map(n => `tag_id = ${n}`).join(' or ');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getf0cks: async (o = { user, tag, mime, page, mode, fav }) => {
|
getf0cks: async (o = { user, tag, mime, page, mode, fav, session }) => {
|
||||||
const user = o.user ? decodeURI(o.user) : null;
|
const user = o.user ? decodeURI(o.user) : null;
|
||||||
const tag = lib.parseTag(o.tag ?? null);
|
const tag = lib.parseTag(o.tag ?? null);
|
||||||
const mime = (o.mime ?? "");
|
const mime = o.mime ?? null;
|
||||||
const page = +(o.page ?? 1);
|
const page = +(o.page ?? 1);
|
||||||
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
|
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
|
||||||
|
|
||||||
const tmp = { user, tag, mime, smime, page };
|
const tmp = { user, tag, mime, smime, page, mode: o.mode };
|
||||||
|
|
||||||
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
||||||
|
|
||||||
let data;
|
|
||||||
let total;
|
|
||||||
|
|
||||||
if(tag) {
|
const total = (await db`
|
||||||
if(tag.match(/sfw/) || tag.length <= 2)
|
select distinct on (items.id)
|
||||||
return {
|
count(items.id) as total
|
||||||
success: false,
|
from items
|
||||||
message: "nope."
|
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) }
|
||||||
|
and items.active = 'true'
|
||||||
|
${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
|
||||||
|
${ 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 && 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;
|
||||||
|
|
||||||
total = await db`
|
if(!total || total === 0) {
|
||||||
select count(*) as total
|
|
||||||
from items
|
|
||||||
inner join (
|
|
||||||
select tags_assign.item_id, tags.tag
|
|
||||||
from tags
|
|
||||||
left join tags_assign on tags_assign.tag_id = tags.id
|
|
||||||
where tags.tag ilike ${'%' + (tag ? tag : '') + '%'}
|
|
||||||
group by tags_assign.item_id, tags.tag
|
|
||||||
) as st on st.item_id = items.id
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
group by st.tag, st.item_id`;
|
|
||||||
|
|
||||||
total = total?.length;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(!o.fav) {
|
|
||||||
total = await db`
|
|
||||||
select count(*) as total
|
|
||||||
from items
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
and items.mime ilike ${smime}
|
|
||||||
and items.username ilike ${user ? user : '%'}
|
|
||||||
`;
|
|
||||||
total = total[0].total;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
total = await db`
|
|
||||||
select count(*) as total
|
|
||||||
from "favorites"
|
|
||||||
left join "user" on "user".id = "favorites".user_id
|
|
||||||
left join "tags_assign" on "tags_assign".item_id = "favorites".item_id
|
|
||||||
left join "tags" on "tags".id = "tags_assign".tag_id
|
|
||||||
left join "items" on "items".id = "favorites".item_id
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
and "items".mime ilike ${smime}
|
|
||||||
and "user".user ilike ${user}
|
|
||||||
group by "items".id
|
|
||||||
`;
|
|
||||||
total = total[0].total;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!total || total.length === 0)
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "404 - no f0cks given"
|
message: "404 - no f0cks given"
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const pages = +Math.ceil(total / cfg.websrv.eps);
|
const pages = +Math.ceil(total / cfg.websrv.eps);
|
||||||
const act_page = Math.min(pages, page || 1);
|
const act_page = Math.min(pages, page || 1);
|
||||||
const offset = Math.max(0, (act_page - 1) * cfg.websrv.eps);
|
const offset = Math.max(0, (act_page - 1) * cfg.websrv.eps);
|
||||||
|
|
||||||
let rows;
|
const rows = await db`
|
||||||
|
select distinct on (items.id)
|
||||||
|
items.id,
|
||||||
|
items.mime,
|
||||||
|
tags.tag,
|
||||||
|
ta.tag_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
|
||||||
|
left join tags_assign ta on ta.item_id = items.id and (ta.tag_id = 1 or ta.tag_id = 2)
|
||||||
|
where
|
||||||
|
${ 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`` }
|
||||||
|
${ !o.fav && 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, ta.tag_id
|
||||||
|
order by items.id desc
|
||||||
|
offset ${offset}
|
||||||
|
limit ${cfg.websrv.eps}
|
||||||
|
`;
|
||||||
|
|
||||||
if(!o.fav) {
|
|
||||||
rows = db`
|
|
||||||
select "items".id, "items".mime, "tags_assign".tag_id
|
|
||||||
from "items"
|
|
||||||
left join "tags_assign" on "tags_assign".item_id = "items".id and ("tags_assign".tag_id = 1 or "tags_assign".tag_id = 2)
|
|
||||||
${tag
|
|
||||||
? db`
|
|
||||||
inner join (
|
|
||||||
select "tags_assign".item_id, "tags".tag
|
|
||||||
from "tags"
|
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
|
||||||
group by "tags_assign".item_id, "tags".tag
|
|
||||||
) as st on st.item_id = "items".id
|
|
||||||
`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
and "items".mime ilike ${smime}
|
|
||||||
and "items".username ilike ${user ? user : '%'}
|
|
||||||
${tag
|
|
||||||
? db`group by st.item_id, "items".id, "tags_assign".tag_id`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
order by "items".id desc
|
|
||||||
offset ${offset}
|
|
||||||
limit ${cfg.websrv.eps}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rows = db`
|
|
||||||
select "items".id, "items".mime, ta.tag_id
|
|
||||||
from "favorites"
|
|
||||||
left join "user" on "user".id = "favorites".user_id
|
|
||||||
left join "tags_assign" on "tags_assign".item_id = "favorites".item_id
|
|
||||||
left join "tags" on "tags".id = "tags_assign".tag_id
|
|
||||||
left join "items" on "items".id = "favorites".item_id
|
|
||||||
left join "tags_assign" as ta on ta.item_id = "items".id and (ta.tag_id = 1 or ta.tag_id = 2)
|
|
||||||
${ tag
|
|
||||||
? db`
|
|
||||||
inner join (
|
|
||||||
select "tags_assign".item_id, "tags".tag
|
|
||||||
from "tags"
|
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
|
||||||
group by "tags_assign".item_id, "tags".tag
|
|
||||||
) as st on st.item_id = "items".id`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
and "items".mime ilike ${smime}
|
|
||||||
and "user".user ilike ${user}
|
|
||||||
${tag
|
|
||||||
? db`group by st.item_id, "items".id, "tags_assign".tag_id`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
group by "items".id, ta.tag_id
|
|
||||||
order by "items".id desc
|
|
||||||
offset ${offset}
|
|
||||||
limit ${cfg.websrv.eps}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
rows = await rows;
|
|
||||||
|
|
||||||
if(rows.length === 0)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "oopsi woopsi"
|
|
||||||
};
|
|
||||||
|
|
||||||
/*rows.forEach(e => {
|
|
||||||
if(!fs.existsSync(`public/t/${e.id}.png`))
|
|
||||||
fs.copyFileSync("public/s/img/broken.png", `public/t/${e.id}.png`);
|
|
||||||
});*/
|
|
||||||
|
|
||||||
const cheat = [];
|
const cheat = [];
|
||||||
for(let i = Math.max(1, act_page - 3); i <= Math.min(act_page + 3, pages); i++)
|
for(let i = Math.max(1, act_page - 3); i <= Math.min(act_page + 3, pages); i++)
|
||||||
cheat.push(i);
|
cheat.push(i);
|
||||||
|
|
||||||
const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks', path: 'p/' });
|
const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks', path: 'p/' });
|
||||||
|
|
||||||
data = {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
items: rows,
|
items: rows,
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -177,9 +93,8 @@ export default {
|
|||||||
link,
|
link,
|
||||||
tmp
|
tmp
|
||||||
};
|
};
|
||||||
return data;
|
|
||||||
},
|
},
|
||||||
getf0ck: async (o = ({ user, tag, mime, itemid, mode })) => {
|
getf0ck: async (o = ({ user, tag, mime, itemid, mode, session })) => {
|
||||||
const user = o.user ? decodeURI(o.user) : null;
|
const user = o.user ? decodeURI(o.user) : null;
|
||||||
const tag = lib.parseTag(o.tag ?? null);
|
const tag = lib.parseTag(o.tag ?? null);
|
||||||
const mime = (o.mime ?? "");
|
const mime = (o.mime ?? "");
|
||||||
@ -197,75 +112,27 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let items;
|
const items = await db`
|
||||||
|
select distinct on (items.id)
|
||||||
if(o.fav) {
|
items.*
|
||||||
items = db`
|
from items
|
||||||
select "items".*
|
left join tags_assign on tags_assign.item_id = items.id
|
||||||
from "favorites"
|
left join tags on tags.id = tags_assign.tag_id
|
||||||
left join "items" on "items".id = "favorites".item_id
|
left join favorites on favorites.item_id = items.id
|
||||||
left join "user" on "user".id = "favorites".user_id
|
left join "user" on "user".id = favorites.user_id
|
||||||
${ tag
|
left join tags_assign ta on ta.item_id = items.id and (ta.tag_id = 1 or ta.tag_id = 2)
|
||||||
? db`
|
where
|
||||||
inner join (
|
${ db.unsafe(modequery) }
|
||||||
select "tags_assign".item_id, "tags".tag
|
and items.active = 'true'
|
||||||
from "tags"
|
${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||||
group by "tags_assign".item_id, "tags".tag
|
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||||
) as st on st.item_id = "items".id`
|
${ !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`` }
|
||||||
: db``
|
group by items.id, tags.tag, ta.tag_id
|
||||||
}
|
order by items.id desc
|
||||||
where ${db.unsafe(modequery)}
|
`;
|
||||||
and "user".user ilike ${user}
|
|
||||||
${ mime
|
|
||||||
? db`and "items".mime ilike ${mime + '/%'}`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
${ tag
|
|
||||||
? db`group by st.tag, st.item_id, "items".id`
|
|
||||||
: db`group by "items".id, "favorites".user_id, "favorites".item_id, "user".id`
|
|
||||||
}
|
|
||||||
order by "items".id desc
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
items = db`
|
|
||||||
select "items".*
|
|
||||||
from "items"
|
|
||||||
${ tag
|
|
||||||
? db`
|
|
||||||
inner join (
|
|
||||||
select "tags_assign".item_id, "tags_assign".tag_id, "tags".tag
|
|
||||||
from "tags"
|
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
|
||||||
group by "tags_assign".item_id, "tags".tag, "tags_assign".tag_id
|
|
||||||
) as st on st.item_id = "items".id`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
${ user
|
|
||||||
? db`and "items".username ilike ${'%' + user + '%'}`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
${ mime
|
|
||||||
? db`and "items".mime ilike ${mime + '/%'}`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
${ tag
|
|
||||||
? db`group by st.item_id, "items".id, st.tag_id`
|
|
||||||
: db`group by "items".id`
|
|
||||||
}
|
|
||||||
order by "items".id desc
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
items = await items;
|
|
||||||
|
|
||||||
if(tag)
|
|
||||||
items = items.filter((v, i, s) => i === s.findIndex(t => t.id === v.id));
|
|
||||||
|
|
||||||
const item = items.findIndex(i => i.id === itemid);
|
const item = items.findIndex(i => i.id === itemid);
|
||||||
const actitem = items[item];
|
const actitem = items[item];
|
||||||
|
|
||||||
@ -334,71 +201,34 @@ export default {
|
|||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
getRandom: async (o = ({ user, tag, mime, mode })) => {
|
getRandom: async (o = ({ user, tag, mime, mode, session })) => {
|
||||||
const user = o.user ? decodeURI(o.user) : null;
|
const user = o.user ? decodeURI(o.user) : null;
|
||||||
const tag = lib.parseTag(o.tag ?? null);
|
const tag = lib.parseTag(o.tag ?? null);
|
||||||
const mime = (o.mime ?? "");
|
const mime = (o.mime ?? "");
|
||||||
|
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
|
||||||
|
|
||||||
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
||||||
|
|
||||||
let item;
|
const item = await db`
|
||||||
if(o.fav) { // dood lol
|
select
|
||||||
item = db`
|
items.id
|
||||||
select "items".*
|
from items
|
||||||
from "favorites"
|
left join tags_assign on tags_assign.item_id = items.id
|
||||||
left join "items" on "items".id = "favorites".item_id
|
left join tags on tags.id = tags_assign.tag_id
|
||||||
left join "user" on "user".id = "favorites".user_id
|
left join favorites on favorites.item_id = items.id
|
||||||
${ tag
|
left join "user" on "user".id = favorites.user_id
|
||||||
? db`
|
where
|
||||||
inner join (
|
${ db.unsafe(modequery) }
|
||||||
select "tags_assign".item_id, "tags".tag
|
and items.active = 'true'
|
||||||
from "tags"
|
${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
${ user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||||
group by "tags_assign".item_id, "tags".tag
|
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||||
) as st on st.item_id = "items".id`
|
${ !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`` }
|
||||||
: db``
|
group by items.id, tags.tag
|
||||||
}
|
order by random()
|
||||||
where ${db.unsafe(modequery)}
|
limit 1
|
||||||
and "user".user ilike ${user}
|
`;
|
||||||
${ mime
|
|
||||||
? db`and "items".mime ilike ${mime + '/%'}`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
order by random()
|
|
||||||
limit 1
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
item = db`
|
|
||||||
select *
|
|
||||||
from "items"
|
|
||||||
${ tag
|
|
||||||
? db`
|
|
||||||
inner join (
|
|
||||||
select "tags_assign".item_id, "tags".tag
|
|
||||||
from "tags"
|
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
|
||||||
group by "tags_assign".item_id, "tags".tag
|
|
||||||
) as st on st.item_id = "items".id`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
where ${db.unsafe(modequery)}
|
|
||||||
${ user
|
|
||||||
? db`and "items".username ilike ${'%' + user + '%'}`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
${ mime
|
|
||||||
? db`and "items".mime ilike ${mime + '/%'}`
|
|
||||||
: db``
|
|
||||||
}
|
|
||||||
order by random()
|
|
||||||
limit 1
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = await item;
|
|
||||||
|
|
||||||
if(item.length === 0) {
|
if(item.length === 0) {
|
||||||
return {
|
return {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import db from "../sql.mjs";
|
import db from "../sql.mjs";
|
||||||
import lib from "../lib.mjs";
|
import lib from "../lib.mjs";
|
||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
|
||||||
const auth = async (req, res, next) => {
|
const auth = async (req, res, next) => {
|
||||||
if(!req.session) {
|
if(!req.session) {
|
||||||
@ -15,8 +16,14 @@ const auth = async (req, res, next) => {
|
|||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
|
|
||||||
router.get(/^\/login(\/)?$/, async (req, res) => {
|
router.get(/^\/login(\/)?$/, async (req, res) => {
|
||||||
if(req.cookies.session)
|
if(req.cookies.session) {
|
||||||
return res.reply({ body: "du bist schon eingeloggt lol" });
|
return res.reply({
|
||||||
|
body: tpl.render('error', {
|
||||||
|
message: "you're already logged in lol",
|
||||||
|
tmp: null
|
||||||
|
}, req)
|
||||||
|
});
|
||||||
|
}
|
||||||
res.reply({
|
res.reply({
|
||||||
body: tpl.render("login", { theme: req.cookies.theme ?? "f0ck" })
|
body: tpl.render("login", { theme: req.cookies.theme ?? "f0ck" })
|
||||||
});
|
});
|
||||||
@ -99,7 +106,11 @@ export default (router, tpl) => {
|
|||||||
router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage
|
router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage
|
||||||
|
|
||||||
res.reply({
|
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)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -115,7 +126,8 @@ export default (router, tpl) => {
|
|||||||
body: tpl.render("admin/sessions", {
|
body: tpl.render("admin/sessions", {
|
||||||
session: req.session,
|
session: req.session,
|
||||||
sessions: rows,
|
sessions: rows,
|
||||||
totals: await lib.countf0cks()
|
totals: await lib.countf0cks(),
|
||||||
|
tmp: null
|
||||||
}, req)
|
}, req)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -124,11 +136,73 @@ export default (router, tpl) => {
|
|||||||
exec("journalctl -qeu f0ck --no-pager", (err, stdout) => {
|
exec("journalctl -qeu f0ck --no-pager", (err, stdout) => {
|
||||||
res.reply({
|
res.reply({
|
||||||
body: tpl.render("admin/log", {
|
body: tpl.render("admin/log", {
|
||||||
log: stdout.split("\n").slice(0, -1)
|
log: stdout.split("\n").slice(0, -1),
|
||||||
|
tmp: null
|
||||||
}, req)
|
}, 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`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(_=>{});
|
||||||
|
|
||||||
|
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>`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
tmp: null
|
||||||
|
}, req)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { promises as fs } from "fs";
|
||||||
import db from '../../sql.mjs';
|
import db from '../../sql.mjs';
|
||||||
import lib from '../../lib.mjs';
|
import lib from '../../lib.mjs';
|
||||||
import search from '../../routeinc/search.mjs';
|
import search from '../../routeinc/search.mjs';
|
||||||
@ -16,8 +17,10 @@ export default router => {
|
|||||||
const rows = await db`
|
const rows = await db`
|
||||||
select *
|
select *
|
||||||
from "items"
|
from "items"
|
||||||
where mime ilike ${mime}
|
where
|
||||||
and username ilike ${user}
|
mime ilike ${mime} and
|
||||||
|
username ilike ${user} and
|
||||||
|
active = 'true'
|
||||||
order by random()
|
order by random()
|
||||||
limit 1
|
limit 1
|
||||||
`;
|
`;
|
||||||
@ -28,27 +31,47 @@ export default router => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group.get(/\/p\/([0-9]+)/, async (req, res) => { // legacy
|
group.get(/\/items\/get/, async (req, res) => {
|
||||||
let eps = 100;
|
let eps = 150;
|
||||||
let id = +req.url.split[3];
|
|
||||||
|
const opt = {
|
||||||
const rows = await db`
|
older: req.url.qs.older ?? null,
|
||||||
select *
|
newer: req.url.qs.newer ?? null,
|
||||||
from "items"
|
mode: +req.url.qs.mode ?? 0 // 0 sfw, 1 nsfw, 2 untagged, 3 all
|
||||||
where id < ${+id}
|
|
||||||
order by id desc
|
|
||||||
limit ${+eps}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const items = {
|
|
||||||
items: rows,
|
|
||||||
last: rows[rows.length - 1].id
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const newest = (await db`select max(id) as id from "items"`)[0].id;
|
||||||
|
const oldest = (await db`select min(id) as id from "items"`)[0].id;
|
||||||
|
const modequery = lib.getMode(opt.mode);
|
||||||
|
|
||||||
|
const rows = (await db`
|
||||||
|
select "items".id, "items".mime, coalesce("tags_assign".tag_id, 0) as tag_id
|
||||||
|
from "items"
|
||||||
|
left join "tags_assign" on "tags_assign".item_id = "items".id and ("tags_assign".tag_id = 1 or "tags_assign".tag_id = 2)
|
||||||
|
where
|
||||||
|
${db.unsafe(modequery)} and
|
||||||
|
active = 'true'
|
||||||
|
${
|
||||||
|
opt.older
|
||||||
|
? db`and id <= ${opt.older}`
|
||||||
|
: opt.newer
|
||||||
|
? db`and id >= ${opt.newer}`
|
||||||
|
: db``
|
||||||
|
}
|
||||||
|
order by id ${
|
||||||
|
opt.newer
|
||||||
|
? db`asc`
|
||||||
|
: db`desc`
|
||||||
|
}
|
||||||
|
limit ${eps}
|
||||||
|
`).sort((a, b) => b.id - a.id);
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
|
atEnd: rows[0].id === newest,
|
||||||
|
atStart: rows[rows.length - 1].id === oldest,
|
||||||
success: true,
|
success: true,
|
||||||
items
|
items: rows
|
||||||
});
|
}, 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
group.get(/\/item\/[0-9]+$/, async (req, res) => {
|
group.get(/\/item\/[0-9]+$/, async (req, res) => {
|
||||||
@ -57,20 +80,20 @@ export default router => {
|
|||||||
const item = await db`
|
const item = await db`
|
||||||
select *
|
select *
|
||||||
from "items"
|
from "items"
|
||||||
where id = ${+id}
|
where id = ${+id} and active = 'true'
|
||||||
limit 1
|
limit 1
|
||||||
`;
|
`;
|
||||||
const next = await db`
|
const next = await db`
|
||||||
select id
|
select id
|
||||||
from "items"
|
from "items"
|
||||||
where id > ${+id}
|
where id > ${+id} and active = 'true'
|
||||||
order by id
|
order by id
|
||||||
limit 1
|
limit 1
|
||||||
`;
|
`;
|
||||||
const prev = await db`
|
const prev = await db`
|
||||||
select id
|
select id
|
||||||
from "items"
|
from "items"
|
||||||
where id < ${+id}
|
where id < ${+id} and active = 'true'
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1
|
limit 1
|
||||||
`;
|
`;
|
||||||
@ -103,7 +126,7 @@ export default router => {
|
|||||||
const rows = db`
|
const rows = db`
|
||||||
select id, mime, size, src, stamp, userchannel, username, usernetwork
|
select id, mime, size, src, stamp, userchannel, username, usernetwork
|
||||||
from "items"
|
from "items"
|
||||||
where username = ${user}
|
where username = ${user} and active = 'true'
|
||||||
order by stamp desc
|
order by stamp desc
|
||||||
limit ${+eps}
|
limit ${+eps}
|
||||||
`;
|
`;
|
||||||
@ -152,13 +175,13 @@ export default router => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const q = (await db`
|
const q = (await db`
|
||||||
update "tags" set ${
|
update "tags" set ${
|
||||||
db({
|
db({
|
||||||
tag: newtag
|
tag: newtag
|
||||||
}, 'tag')
|
}, 'tag')
|
||||||
}
|
}
|
||||||
where tag = ${tagname}
|
where tag = ${tagname}
|
||||||
returning *
|
returning *
|
||||||
`)?.[0];
|
`)?.[0];
|
||||||
|
|
||||||
return res.json(q, tagname === newtag ? 200 : 201); // created (modified)
|
return res.json(q, tagname === newtag ? 200 : 201); // created (modified)
|
||||||
@ -203,13 +226,42 @@ export default router => {
|
|||||||
msg: 'no postid'
|
msg: 'no postid'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const postid = +req.post.postid;
|
const id = +req.post.postid;
|
||||||
|
|
||||||
await db`
|
if(id <= 1) {
|
||||||
delete from "items"
|
return res.json({
|
||||||
where id = ${+postid}
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const f0ck = await db`
|
||||||
|
select dest, mime
|
||||||
|
from "items"
|
||||||
|
where
|
||||||
|
id = ${id} and
|
||||||
|
active = 'true'
|
||||||
|
limit 1
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
if(f0ck.length === 0) {
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
msg: `f0ck ${id}: f0ck not found`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await db`update "items" set active = 'false' where id = ${id}`;
|
||||||
|
|
||||||
|
await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{});
|
||||||
|
await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{});
|
||||||
|
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
|
||||||
|
await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
|
||||||
|
|
||||||
|
if(f0ck[0].mime.startsWith('audio')) {
|
||||||
|
await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{});
|
||||||
|
await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{});
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true
|
success: true
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@ export default router => {
|
|||||||
const itemid = (await db`
|
const itemid = (await db`
|
||||||
select id
|
select id
|
||||||
from "items"
|
from "items"
|
||||||
where id = ${+avatar}
|
where id = ${+avatar} and active = 'true'
|
||||||
`)?.[0]?.id;
|
`)?.[0]?.id;
|
||||||
|
|
||||||
if(!itemid) {
|
if(!itemid) {
|
||||||
|
@ -30,7 +30,7 @@ export default router => {
|
|||||||
const postid = +req.params.postid;
|
const postid = +req.params.postid;
|
||||||
const tagname = req.post.tagname?.trim();
|
const tagname = req.post.tagname?.trim();
|
||||||
|
|
||||||
if(tagname.length >= 45) {
|
if(tagname.length > 70) {
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
msg: 'tag is too long!'
|
msg: 'tag is too long!'
|
||||||
@ -131,6 +131,14 @@ export default router => {
|
|||||||
const postid = +req.params.postid;
|
const postid = +req.params.postid;
|
||||||
const tagname = decodeURIComponent(req.params.tagname);
|
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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const tags = await lib.getTags(postid);
|
const tags = await lib.getTags(postid);
|
||||||
|
|
||||||
const tagid = tags.filter(t => t.tag === tagname)[0]?.id ?? null;
|
const tagid = tags.filter(t => t.tag === tagname)[0]?.id ?? null;
|
||||||
|
@ -10,6 +10,65 @@ const auth = async (req, res, next) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
|
router.get(/\/user\/(?<user>.*)/, async (req, res) => {
|
||||||
|
const user = decodeURIComponent(req.params.user);
|
||||||
|
|
||||||
|
const query = await db`
|
||||||
|
select "user".user, "user".created_at, user_options.*
|
||||||
|
from user_options
|
||||||
|
left join "user" on "user".id = user_options.user_id
|
||||||
|
where "user".user ilike ${user}
|
||||||
|
limit 1
|
||||||
|
`;
|
||||||
|
|
||||||
|
if(!query.length) {
|
||||||
|
return res.reply({
|
||||||
|
code: 404,
|
||||||
|
body: tpl.render('error', {
|
||||||
|
message: 'this user does not exists',
|
||||||
|
tmp: null
|
||||||
|
}, req)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const f0cks = await f0cklib.getf0cks({
|
||||||
|
user: user,
|
||||||
|
mode: req.session.mode,
|
||||||
|
fav: false,
|
||||||
|
session: !!req.session
|
||||||
|
});
|
||||||
|
const favs = await f0cklib.getf0cks({
|
||||||
|
user: user,
|
||||||
|
mode: req.session.mode,
|
||||||
|
fav: true,
|
||||||
|
session: !!req.session
|
||||||
|
});
|
||||||
|
|
||||||
|
const count = {
|
||||||
|
f0cks: 0,
|
||||||
|
favs: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if('items' in f0cks) {
|
||||||
|
count.f0cks = f0cks.items.length;
|
||||||
|
f0cks.items = f0cks.items.slice(0, 50);
|
||||||
|
}
|
||||||
|
if('items' in favs) {
|
||||||
|
count.favs = favs.items.length;
|
||||||
|
favs.items = favs.items.slice(0, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
user: query[0],
|
||||||
|
f0cks,
|
||||||
|
count,
|
||||||
|
favs,
|
||||||
|
tmp: null
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.reply({ body: tpl.render('user', data, req) });
|
||||||
|
});
|
||||||
|
|
||||||
router.get(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/, async (req, res) => {
|
router.get(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/, async (req, res) => {
|
||||||
const mode = req.params.itemid ? 'item' : 'index';
|
const mode = req.params.itemid ? 'item' : 'index';
|
||||||
const data = await (req.params.itemid ? f0cklib.getf0ck : f0cklib.getf0cks)({
|
const data = await (req.params.itemid ? f0cklib.getf0ck : f0cklib.getf0cks)({
|
||||||
@ -19,7 +78,8 @@ export default (router, tpl) => {
|
|||||||
page: req.params.page,
|
page: req.params.page,
|
||||||
itemid: req.params.itemid,
|
itemid: req.params.itemid,
|
||||||
fav: req.params.mode == 'favs',
|
fav: req.params.mode == 'favs',
|
||||||
mode: req.session.mode
|
mode: req.session.mode,
|
||||||
|
session: !!req.session
|
||||||
});
|
});
|
||||||
if(!data.success) {
|
if(!data.success) {
|
||||||
return res.reply({
|
return res.reply({
|
||||||
@ -44,7 +104,7 @@ export default (router, tpl) => {
|
|||||||
const mode = +req.url.split[1];
|
const mode = +req.url.split[1];
|
||||||
let referertmp = req.headers.referer;
|
let referertmp = req.headers.referer;
|
||||||
let referer = "";
|
let referer = "";
|
||||||
|
|
||||||
if(referertmp?.match(/f0ck\.me/))
|
if(referertmp?.match(/f0ck\.me/))
|
||||||
referer = referertmp.split("/").slice(3).join("/");
|
referer = referertmp.split("/").slice(3).join("/");
|
||||||
|
|
||||||
@ -68,46 +128,5 @@ export default (router, tpl) => {
|
|||||||
res.redirect(`/${referer}`);
|
res.redirect(`/${referer}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get(/^\/ranking$/, async (req, res) => {
|
|
||||||
try {
|
|
||||||
const list = await db`
|
|
||||||
select
|
|
||||||
"user".user,
|
|
||||||
coalesce("user_options".avatar, 47319) 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;
|
return router;
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import cfg from "../../inc/config.mjs";
|
||||||
import f0cklib from "../routeinc/f0cklib.mjs";
|
import f0cklib from "../routeinc/f0cklib.mjs";
|
||||||
|
|
||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
@ -5,8 +6,8 @@ export default (router, tpl) => {
|
|||||||
let referer = req.headers.referer ?? '';
|
let referer = req.headers.referer ?? '';
|
||||||
let opts = {};
|
let opts = {};
|
||||||
|
|
||||||
if(referer.match(/f0ck\.me/)) { // parse referer
|
if(referer.match(new RegExp(cfg.main.url.regex))) { // parse referer
|
||||||
referer = referer.split("f0ck.me")[1];
|
referer = referer.split(cfg.main.url.domain)[1];
|
||||||
const tmp = referer.match(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/);
|
const tmp = referer.match(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/);
|
||||||
if(tmp)
|
if(tmp)
|
||||||
opts = tmp.groups;
|
opts = tmp.groups;
|
||||||
@ -18,7 +19,8 @@ export default (router, tpl) => {
|
|||||||
mime: opts.mime,
|
mime: opts.mime,
|
||||||
page: opts.page,
|
page: opts.page,
|
||||||
fav: opts.mode == 'favs',
|
fav: opts.mode == 'favs',
|
||||||
mode: req.session.mode
|
mode: req.session.mode,
|
||||||
|
session: !!req.session
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!data.success) {
|
if(!data.success) {
|
||||||
@ -31,7 +33,7 @@ export default (router, tpl) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.redirect(`/${data.link}${data.link.length != 0 ? "/" : ""}${data.itemid}`);
|
res.redirect(`${data.link.main}${data.link.path}${data.itemid}`);
|
||||||
});
|
});
|
||||||
return router;
|
return router;
|
||||||
};
|
};
|
||||||
|
97
src/inc/routes/ranking.mjs
Normal file
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,
|
||||||
|
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
|
||||||
|
`;
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -17,8 +17,8 @@ export default (router, tpl) => {
|
|||||||
total = (await db`
|
total = (await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
from "items"
|
||||||
where src ilike ${'%' + tag.substring(4) + '%'}
|
where src ilike ${'%' + tag.substring(4) + '%'} and active = 'true'
|
||||||
group by "items".id, "tags".tag
|
group by "items".id
|
||||||
`).length;
|
`).length;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -40,8 +40,11 @@ export default (router, tpl) => {
|
|||||||
ret = await db`
|
ret = await db`
|
||||||
select *
|
select *
|
||||||
from "items"
|
from "items"
|
||||||
where src ilike ${'%' + tag.substring(4) + '%'}
|
where
|
||||||
group by "items".id, "tags".tag
|
src ilike ${'%' + tag.substring(4) + '%'} and
|
||||||
|
active = 'true'
|
||||||
|
group by "items".id
|
||||||
|
order by "items".id desc
|
||||||
offset ${offset}
|
offset ${offset}
|
||||||
limit ${_eps}
|
limit ${_eps}
|
||||||
`;
|
`;
|
||||||
@ -52,7 +55,7 @@ export default (router, tpl) => {
|
|||||||
from "tags"
|
from "tags"
|
||||||
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
left join "tags_assign" on "tags_assign".tag_id = "tags".id
|
||||||
left join "items" on "items".id = "tags_assign".item_id
|
left join "items" on "items".id = "tags_assign".item_id
|
||||||
where "tags".tag ilike ${'%' + tag + '%'}
|
where "tags".tag ilike ${'%' + tag + '%'} and "items".active = 'true'
|
||||||
group by "items".id, "tags".tag
|
group by "items".id, "tags".tag
|
||||||
offset ${offset}
|
offset ${offset}
|
||||||
limit ${_eps}
|
limit ${_eps}
|
||||||
|
@ -12,5 +12,19 @@ export default (router, tpl) => {
|
|||||||
"Location": req.headers.referer ?? "/"
|
"Location": req.headers.referer ?? "/"
|
||||||
}).end();
|
}).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;
|
return router;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//import knex from "knex";
|
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
import cfg from "./config.mjs";
|
import cfg from "./config.mjs";
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { getLevel } from "../admin.mjs";
|
import { getLevel } from "../admin.mjs";
|
||||||
|
import lib from "../lib.mjs";
|
||||||
import fetch from "flumm-fetch";
|
import fetch from "flumm-fetch";
|
||||||
import vm from "vm";
|
import vm from "vm";
|
||||||
|
|
||||||
@ -7,7 +8,9 @@ let context = vm.createContext({
|
|||||||
e: null,
|
e: null,
|
||||||
bot: null,
|
bot: null,
|
||||||
admins: null,
|
admins: null,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
|
lib,
|
||||||
|
console,
|
||||||
|
|
||||||
a: null,
|
a: null,
|
||||||
resolve: null
|
resolve: null
|
||||||
@ -19,16 +22,16 @@ export default async bot => {
|
|||||||
name: "level",
|
name: "level",
|
||||||
call: /^!level (.*)/i,
|
call: /^!level (.*)/i,
|
||||||
active: true,
|
active: true,
|
||||||
f: e => {
|
f: async e => {
|
||||||
const user = e.message.trim().substring(7);
|
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",
|
name: "self",
|
||||||
call: /^!self$/i,
|
call: /^!self$/i,
|
||||||
active: true,
|
active: true,
|
||||||
f: e => {
|
f: async e => {
|
||||||
e.reply( JSON.stringify( e.user ) );
|
await e.reply( JSON.stringify( e.user ) );
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: "sandbox_debug",
|
name: "sandbox_debug",
|
||||||
@ -41,6 +44,7 @@ export default async bot => {
|
|||||||
context.e = e;
|
context.e = e;
|
||||||
context.bot = bot;
|
context.bot = bot;
|
||||||
context.level = getLevel;
|
context.level = getLevel;
|
||||||
|
context.hasTag = lib.hasTag;
|
||||||
context.a = null;
|
context.a = null;
|
||||||
|
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
@ -52,9 +56,9 @@ export default async bot => {
|
|||||||
|
|
||||||
let output = JSON.stringify(context.a);
|
let output = JSON.stringify(context.a);
|
||||||
if(output.length > maxoutput)
|
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
|
else
|
||||||
return e.reply(output);
|
return await e.reply(output);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import db from "../sql.mjs";
|
import db from "../sql.mjs";
|
||||||
|
import { getLevel } from "../../inc/admin.mjs";
|
||||||
|
|
||||||
export default async bot => {
|
export default async bot => {
|
||||||
|
|
||||||
@ -7,37 +8,104 @@ export default async bot => {
|
|||||||
name: "delete",
|
name: "delete",
|
||||||
call: /^\!(del|rm) .*/i,
|
call: /^\!(del|rm) .*/i,
|
||||||
active: true,
|
active: true,
|
||||||
level: 100,
|
|
||||||
f: async e => {
|
f: async e => {
|
||||||
const ret = (await Promise.all(e.args.map(async id => {
|
let deleted = [];
|
||||||
id = +id;
|
|
||||||
if(id <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
for(let id of e.args) {
|
||||||
|
id = +id;
|
||||||
|
if(id <= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
const f0ck = await db`
|
const f0ck = await db`
|
||||||
select dest
|
select dest, mime, username, userchannel, usernetwork
|
||||||
from "items"
|
from "items"
|
||||||
where id = ${+id}
|
where
|
||||||
|
id = ${id} and
|
||||||
|
active = 'true'
|
||||||
limit 1
|
limit 1
|
||||||
`;
|
`;
|
||||||
if(f0ck.length === 0)
|
const level = getLevel(e.user).level;
|
||||||
return false;
|
|
||||||
|
|
||||||
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
|
|
||||||
await fs.unlink(`./public/t/${id}`).catch(_=>{});
|
|
||||||
|
|
||||||
await db`
|
if(f0ck.length === 0) {
|
||||||
delete from "items"
|
await e.reply(`f0ck ${id}: f0ck not found`);
|
||||||
where id = ${+id}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
(f0ck[0].username !== (e.user.nick || e.user.username) ||
|
||||||
|
f0ck[0].userchannel !== e.channel ||
|
||||||
|
f0ck[0].usernetwork !== e.network) &&
|
||||||
|
level < 100
|
||||||
|
) {
|
||||||
|
await e.reply(`f0ck ${id}: insufficient permissions`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(~~(new Date() / 1e3) >= (f0ck[0].stamp + 600) && level < 100) {
|
||||||
|
await e.reply(`f0ck ${id}: too late lol`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(_=>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
await e.reply(`deleted ${deleted.length}/${e.args.length} f0cks (${deleted.join(",")})`);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: "recover",
|
||||||
|
call: /^\!(recover) .*/i,
|
||||||
|
active: true,
|
||||||
|
level: 100,
|
||||||
|
f: async e => {
|
||||||
|
let recovered = [];
|
||||||
|
|
||||||
|
for(let id of e.args) {
|
||||||
|
id = +id;
|
||||||
|
if(id <= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const f0ck = await db`
|
||||||
|
select dest, mime
|
||||||
|
from "items"
|
||||||
|
where
|
||||||
|
id = ${id} and
|
||||||
|
active = 'false'
|
||||||
|
limit 1
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return id;
|
if(f0ck.length === 0) {
|
||||||
}))).filter(d => d);
|
await e.reply(`f0ck ${id}: f0ck not found`);
|
||||||
|
continue;
|
||||||
if(ret.length > 0)
|
}
|
||||||
e.reply(`deleted ${ret.length}/${e.args.length} (${ret.join(",")}) f0cks`);
|
|
||||||
else
|
await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
|
||||||
e.reply(`oof`);
|
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}`;
|
||||||
|
|
||||||
|
recovered.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 db from "../sql.mjs";
|
||||||
import lib from "../lib.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 => {
|
export default async bot => {
|
||||||
|
|
||||||
return [{
|
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)),
|
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)),
|
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":
|
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":
|
case "thumb":
|
||||||
const rows = await db`
|
const rows = await db`
|
||||||
select id
|
select id
|
||||||
from "items"
|
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 = [];
|
const tmp = [];
|
||||||
for(let row of rows)
|
for(let row of rows)
|
||||||
!dir.includes(row.id) ? tmp.push(row.id) : null;
|
!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;
|
break;
|
||||||
case "cache":
|
case "cache":
|
||||||
cfg.websrv.cache = !cfg.websrv.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":
|
case "uptime":
|
||||||
exec('sudo systemctl status f0ck', (err, stdout) => {
|
exec('sudo systemctl status f0ck', async (err, stdout) => {
|
||||||
if(!err)
|
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;
|
break;
|
||||||
case "restart":
|
case "restart":
|
||||||
e.reply("hay hay patron, hemen!");
|
await e.reply("hay hay patron, hemen!");
|
||||||
exec("sudo systemctl restart f0ck");
|
exec("sudo systemctl restart f0ck");
|
||||||
break;
|
break;
|
||||||
/*case "cleanTags":
|
|
||||||
const tags = await cleanTags();
|
|
||||||
e.reply(tags + " tags removed");
|
|
||||||
break;*/
|
|
||||||
case "clearTmp":
|
case "clearTmp":
|
||||||
await Promise.all((await fs.readdir("./tmp")).filter(d => d !== ".empty").map(async d => fs.unlink(`./tmp/${d}`)));
|
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;
|
break;
|
||||||
case "status":
|
case "status":
|
||||||
const tmpc = await lib.countf0cks();
|
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;
|
break;
|
||||||
case "autotagger":
|
/*case "autotagger":
|
||||||
const body = { headers: { Authorization: `Basic ${cfg.tagger.btoa}` } };
|
const body = { headers: { Authorization: `Basic ${cfg.tagger.btoa}` } };
|
||||||
const res = await (await fetch(`${cfg.tagger.endpoint}/usage`, body)).json();
|
const res = await (await fetch(`${cfg.tagger.endpoint}/usage`, body)).json();
|
||||||
if(res) {
|
if(res) {
|
||||||
const processed = res.result.monthly_processed;
|
const processed = res.result.monthly_processed;
|
||||||
const limit = res.result.monthly_limit;
|
const limit = res.result.monthly_limit;
|
||||||
return e.reply(`autotagger: usage/limit: ${processed}/${limit}`);
|
return await e.reply(`autotagger: usage/limit: ${processed}/${limit}`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
break;
|
break;*/
|
||||||
/*case "renameTag":
|
/*case "renameTag":
|
||||||
const origTag = e.args.slice(1).join(' ');
|
const origTag = e.args.slice(1).join(' ');
|
||||||
|
|
||||||
if(origTag.length <= 1)
|
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;
|
const origTagID = (await sql('tags').where('tag', origTag))[0].id;
|
||||||
|
|
||||||
@ -116,10 +89,10 @@ export default async bot => {
|
|||||||
.del()
|
.del()
|
||||||
);
|
);
|
||||||
|
|
||||||
e.reply(JSON.stringify({ affected, deleted }));
|
await e.reply(JSON.stringify({ affected, deleted }));
|
||||||
break;*/
|
break;*/
|
||||||
case "help":
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
@ -22,10 +22,10 @@ export default async bot => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
if(rows.length === 0)
|
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}/${rows[0].id}`,
|
`${cfg.main.url.full}/${rows[0].id}`,
|
||||||
`user: ${rows[0].username} @ ${rows[0].usernetwork} ${rows[0].userchannel}`,
|
`user: ${rows[0].username} @ ${rows[0].usernetwork} ${rows[0].userchannel}`,
|
||||||
`~${lib.formatSize(rows[0].size)}`,
|
`~${lib.formatSize(rows[0].size)}`,
|
||||||
rows[0].mime,
|
rows[0].mime,
|
||||||
|
@ -10,6 +10,7 @@ export default async bot => {
|
|||||||
active: false,
|
active: false,
|
||||||
f: async e => {
|
f: async e => {
|
||||||
let args = e.args.slice(1);
|
let args = e.args.slice(1);
|
||||||
|
|
||||||
/*let rows = sql("items").select("id", "username", "mime", "size");
|
/*let rows = sql("items").select("id", "username", "mime", "size");
|
||||||
|
|
||||||
for(let i = 0; i < args.length; i++) {
|
for(let i = 0; i < args.length; i++) {
|
||||||
@ -21,10 +22,21 @@ export default async bot => {
|
|||||||
|
|
||||||
rows = await rows.orderByRaw("random()").limit(1);*/
|
rows = await rows.orderByRaw("random()").limit(1);*/
|
||||||
|
|
||||||
if(rows.length === 0)
|
const rows = await db`
|
||||||
return e.reply("nothing found, f0cker");
|
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}/${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,133 +2,223 @@ import cfg from "../config.mjs";
|
|||||||
import db from "../sql.mjs";
|
import db from "../sql.mjs";
|
||||||
import lib from "../lib.mjs";
|
import lib from "../lib.mjs";
|
||||||
import { getLevel } from "../admin.mjs";
|
import { getLevel } from "../admin.mjs";
|
||||||
|
import queue from "../queue.mjs";
|
||||||
|
import autotagger from "../autotagger.mjs";
|
||||||
import fetch from "flumm-fetch";
|
import fetch from "flumm-fetch";
|
||||||
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { exec as _exec } from "child_process";
|
import path from "path";
|
||||||
|
|
||||||
const exec = cmd => new Promise((resolve, reject) => {
|
const regex = {
|
||||||
_exec(cmd, { maxBuffer: 5e3 * 1024 }, (err, stdout, stderr) => {
|
all: /https?:\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gi,
|
||||||
if(err)
|
yt: /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\/?\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})/gi,
|
||||||
return reject(err);
|
imgur: /(?:https?:)?\/\/(\w+\.)?imgur\.com\/(\S*)(\.[a-zA-Z]{3})/gm,
|
||||||
if(stderr)
|
instagram: /(?:https?:\/\/www\.)?instagram\.com\S*?\/(?:p|reel)\/(\w{11})\/?/im
|
||||||
console.error(stderr);
|
};
|
||||||
resolve({ stdout: stdout });
|
const mediagroupids = new Set();
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//const regex = /https?:\/\/[\w\S(\.|:|/)]+/gi;
|
|
||||||
const regex = /https?:\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gi;
|
|
||||||
|
|
||||||
export default async bot => {
|
export default async bot => {
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
name: "parser",
|
name: "parser",
|
||||||
call: regex,
|
call: regex.all,
|
||||||
active: true,
|
active: true,
|
||||||
f: e => {
|
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.photo)
|
if(e.media)
|
||||||
links.push(e.photo);
|
links.push(e.media);
|
||||||
|
|
||||||
if(links.length === 0)
|
if(links.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(e.message.match(/(!|-)ignore/))
|
if(e.message.match(/\!i(gnore)?\b/))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!e.channel.includes("f0ck") && !e.message.match(/(!|-)f0ck/i))
|
if(!e.channel.includes("f0ck") && (!e.message.match(/\!f(0ck)?\b/i) && (typeof e.raw.forward_from == 'undefined')))
|
||||||
return false;
|
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" : ""}...`);
|
console.log(`parsing ${links.length} link${links.length > 1 ? "s" : ""}...`);
|
||||||
|
|
||||||
links.forEach(async link => {
|
links.forEach(async link => {
|
||||||
|
if(regex.imgur.test(link))
|
||||||
|
await e.reply(`imgur schmimigur`);
|
||||||
|
|
||||||
|
if(regex.instagram.test(link))
|
||||||
|
await e.reply(`insta schminsta`);
|
||||||
|
|
||||||
// check repost (link)
|
// check repost (link)
|
||||||
const q_repost = await db`
|
repost = await queue.checkrepostlink(link);
|
||||||
select id
|
if(repost)
|
||||||
from "items"
|
return await e.reply(`repost motherf0cker (link): ${cfg.main.url.full}/${repost}`);
|
||||||
where src = ${link}
|
|
||||||
`;
|
|
||||||
if(q_repost.length > 0)
|
|
||||||
return e.reply(`repost motherf0cker (link): ${cfg.main.url}/${q_repost[0].id}`);
|
|
||||||
|
|
||||||
// generate uuid
|
// generate uuid
|
||||||
const uuid = (await db`
|
const uuid = await queue.genuuid();
|
||||||
select gen_random_uuid() as uuid
|
|
||||||
`)[0].uuid.substring(0, 8);
|
|
||||||
|
|
||||||
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
|
// read metadata
|
||||||
try {
|
let ext;
|
||||||
meta = JSON.parse((await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
|
if(regex.imgur.test(link)) {
|
||||||
|
// is imgur
|
||||||
|
try {
|
||||||
|
// will die extension von der url
|
||||||
|
ext = link.split(".").slice(-1).join(".");
|
||||||
|
} 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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(err) {
|
else {
|
||||||
//e.reply("[error] f0ck has no bock :(");
|
// is not instagram
|
||||||
//console.error(err);
|
try {
|
||||||
return;
|
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(!Object.values(cfg.mimes).includes(meta.ext.toLowerCase())) {
|
if(!Object.values(cfg.mimes).includes(ext?.toLowerCase())) {
|
||||||
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
return console.log('mime schmime ' + ext);
|
||||||
if(!Object.keys(cfg.mimes).includes(tmphead))
|
|
||||||
return;
|
|
||||||
meta.ext = cfg.mimes[tmphead];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = `${uuid}.${meta.ext}`;
|
const msg = await e.reply(`[charging the f0cker] downloading: ${uuid}`, {
|
||||||
|
disable_notification: true
|
||||||
e.reply(`[charging the f0cker] downloading: ${uuid}`);
|
});
|
||||||
|
|
||||||
// download data
|
// download data
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
let source;
|
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();
|
try {
|
||||||
//change 720 to any other available resolution, higher = better quality but bigger filesize
|
if(regex.instagram.test(link))
|
||||||
}
|
try {
|
||||||
else {
|
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited
|
||||||
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();
|
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();
|
||||||
//change 720 to any other available resolution, higher = better quality but bigger filesize
|
} 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(regex.imgur.test(link)) {
|
||||||
|
console.log("penis123");
|
||||||
|
try {
|
||||||
|
await queue.exec(`torsocks wget "${link}" -O "./tmp/${uuid}.${ext}"`);
|
||||||
|
source = "./tmp/"+uuid+"."+ext;
|
||||||
|
console.log(source);
|
||||||
|
} catch(err) {
|
||||||
|
console.log(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) {
|
||||||
|
if(e.type == 'tg')
|
||||||
|
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, err);
|
||||||
|
return await e.reply(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(source.match(/larger than/))
|
// this is how it was before I fucked it up :>
|
||||||
return e.reply("too large lol");
|
// 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) {
|
||||||
|
// 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) {
|
||||||
|
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);
|
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
|
// filesize check
|
||||||
const mime = (await exec(`file --mime-type -b ./tmp/${filename}`)).stdout.trim();
|
const size = fs.statSync(source).size;
|
||||||
if(!Object.keys(cfg.mimes).includes(mime))
|
if(size > maxfilesize) {
|
||||||
return e.reply(`lol, go f0ck yourself (${mime})`);
|
await fs.promises.unlink(source).catch(_=>{});
|
||||||
|
if(e.type == 'tg')
|
||||||
if(!Object.values(cfg.mimes).includes(meta.ext.toLowerCase())) {
|
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `too large lol. (${lib.formatSize(size)} / ${lib.formatSize(maxfilesize)})`);
|
||||||
let tmpext = cfg.mimes[meta.ext.toLowerCase()];
|
return await e.reply(`too large lol. (${lib.formatSize(size)} / ${lib.formatSize(maxfilesize)})`);
|
||||||
fs.renameSync(`./tmp/${filename}`, `./tmp/${uuid}.${tmpext}`);
|
|
||||||
filename = `${uuid}.${tmpext}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
// check repost (checksum)
|
||||||
const q_repostc = await db`
|
repost = await queue.checkrepostsum(checksum);
|
||||||
select id
|
if(repost) {
|
||||||
from "items"
|
await fs.promises.unlink(source).catch(_=>{});
|
||||||
where checksum = ${checksum}
|
if(e.type == 'tg')
|
||||||
`;
|
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `repost motherf0cker (checksum): ${cfg.main.url.full}/${repost}`);
|
||||||
if(q_repostc.length > 0)
|
return await e.reply(`repost motherf0cker (checksum): ${cfg.main.url.full}/${repost}`);
|
||||||
return e.reply(`repost motherf0cker (checksum): ${cfg.main.url}/${q_repostc[0].id}`);
|
}
|
||||||
|
|
||||||
await fs.promises.copyFile(`./tmp/${filename}`, `./public/b/${filename}`);
|
const filename = path.basename(source);
|
||||||
await fs.promises.unlink(`./tmp/${filename}`).catch(_=>{});
|
|
||||||
|
await fs.promises.copyFile(source, `./public/b/${filename}`);
|
||||||
|
await fs.promises.unlink(source).catch(_=>{});
|
||||||
|
|
||||||
await db`
|
await db`
|
||||||
insert into items ${
|
insert into items ${
|
||||||
db({
|
db({
|
||||||
src: e.photo ? "" : link,
|
src: e.media ? "" : link,
|
||||||
dest: filename,
|
dest: filename,
|
||||||
mime: mime,
|
mime: mime,
|
||||||
size: size,
|
size: size,
|
||||||
@ -137,54 +227,18 @@ export default async bot => {
|
|||||||
userchannel: e.channel,
|
userchannel: e.channel,
|
||||||
usernetwork: e.network,
|
usernetwork: e.network,
|
||||||
stamp: ~~(new Date() / 1000),
|
stamp: ~~(new Date() / 1000),
|
||||||
active: 1
|
active: true
|
||||||
}, 'src', 'dest', 'mime', 'size', 'checksum', 'username', 'userchannel', 'usernetwork', 'stamp', 'active')
|
}, 'src', 'dest', 'mime', 'size', 'checksum', 'username', 'userchannel', 'usernetwork', 'stamp', 'active')
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const itemid = (await db`
|
const itemid = await queue.getItemID(filename);
|
||||||
select *
|
|
||||||
from "items"
|
|
||||||
where dest = ${filename}
|
|
||||||
limit 1
|
|
||||||
`)[0].id;
|
|
||||||
|
|
||||||
// generate thumbnail
|
// generate thumbnail
|
||||||
try {
|
try {
|
||||||
if(mime.startsWith('video/') || mime == 'image/gif')
|
await queue.genThumbnail(filename, mime, itemid, link);
|
||||||
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 => {});
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
await exec(`convert ./mugge.png ./public/t/${itemid}.webp`);
|
await queue.exec(`convert ./mugge.png ./public/t/${itemid}.webp`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let speed = lib.calcSpeed(size, end);
|
let speed = lib.calcSpeed(size, end);
|
||||||
@ -192,58 +246,42 @@ export default async bot => {
|
|||||||
|
|
||||||
// autotagger
|
// autotagger
|
||||||
let tags = [];
|
let tags = [];
|
||||||
try {
|
/*if(cfg.apis?.nsfw1 && mime.startsWith('image')) {
|
||||||
if(mime.startsWith('image')) {
|
const nsfw = await autotagger.isNSFW(filename, size);
|
||||||
const res = await lib.detectNSFW(filename);
|
tags.push(nsfw ? 'nsfw' : 'sfw');
|
||||||
|
if(nsfw)
|
||||||
|
await queue.tagNSFW(itemid);
|
||||||
|
else
|
||||||
|
await queue.tagSFW(itemid);
|
||||||
|
}*/
|
||||||
|
|
||||||
await db`
|
let outputmsgirc = `[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}`;
|
||||||
insert into "tags_assign" ${
|
let outputmsgtg = `[f0cked] size: ${lib.formatSize(size)} | speed: ${speed}`;
|
||||||
db({
|
|
||||||
item_id: itemid,
|
|
||||||
tag_id: res.nsfw ? 2 : 1,
|
|
||||||
user_id: 7
|
|
||||||
})
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
tags.push(res.nsfw ? 'nsfw' : 'sfw');
|
|
||||||
|
|
||||||
if(res.hentai >= .7) {
|
if(tags?.length > 0) {
|
||||||
await db`
|
const tagstr = tags.join(', ');
|
||||||
insert into "tags_assign" ${
|
outputmsgirc += ` | tags: ${tagstr}`;
|
||||||
db({
|
outputmsgtg += ` | tags: ${tagstr}`;
|
||||||
item_id: f.id,
|
|
||||||
tag_id: 8, // hentai
|
|
||||||
user_id: 7 // autotagger
|
|
||||||
})
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
tags.push('hentai');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(mime.startsWith('audio')) {
|
|
||||||
await db`
|
|
||||||
insert into "tags_assign" ${
|
|
||||||
db([{
|
|
||||||
item_id: itemid,
|
|
||||||
tag_id: 1,
|
|
||||||
user_id: 7
|
|
||||||
}, {
|
|
||||||
item_id: itemid,
|
|
||||||
tag_id: 7178,
|
|
||||||
user_id: 7
|
|
||||||
}])
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
tags.push('sfw', 'audio');
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.reply([
|
if(e.type == 'tg') {
|
||||||
`[f0cked] link: ${cfg.main.url}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}` + (tags.length > 0 ? ` | tags: ${tags.join(', ')}` : '')
|
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 => {
|
f: async e => {
|
||||||
const id = +e.args[1];
|
const id = +e.args[1];
|
||||||
if(!id)
|
if(!id)
|
||||||
return e.reply("lol no");
|
return await e.reply("lol no");
|
||||||
const tags = (await lib.getTags(id)).map(t => t.tag);
|
const tags = (await lib.getTags(id)).map(t => t.tag);
|
||||||
if(tags.length === 0)
|
if(tags.length === 0)
|
||||||
return e.reply(`item ${cfg.main.url}/${id} has no tags!`);
|
return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||||
return e.reply(`item ${cfg.main.url}/${id} is tagged as: ${tags.join(', ')}`);
|
return await e.reply(`item ${cfg.main.url.full}/${id} is tagged as: ${tags.join(', ')}`);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: "tags add",
|
name: "tags add",
|
||||||
@ -26,7 +26,7 @@ export default async bot => {
|
|||||||
f: async e => {
|
f: async e => {
|
||||||
const id = +e.args[1];
|
const id = +e.args[1];
|
||||||
if(!id)
|
if(!id)
|
||||||
return e.reply("lol no");
|
return await e.reply("lol no");
|
||||||
|
|
||||||
const tags = (await lib.getTags(id)).map(t => t.tag);
|
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);
|
: e.args.splice(2)).filter(t => !tags.includes(t) && t.length > 0);
|
||||||
|
|
||||||
if(newtags.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 => {
|
await Promise.all(newtags.map(async ntag => {
|
||||||
try {
|
try {
|
||||||
@ -73,8 +73,8 @@ export default async bot => {
|
|||||||
|
|
||||||
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
||||||
if(ntags.length === 0)
|
if(ntags.length === 0)
|
||||||
return e.reply(`item ${cfg.main.url}/${id} has no tags!`);
|
return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||||
return e.reply(`item ${cfg.main.url}/${id} is now tagged as: ${ntags.join(', ')}`);
|
return await e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: "tags remove",
|
name: "tags remove",
|
||||||
@ -84,7 +84,7 @@ export default async bot => {
|
|||||||
f: async e => {
|
f: async e => {
|
||||||
const id = +e.args[1];
|
const id = +e.args[1];
|
||||||
if(!id)
|
if(!id)
|
||||||
return e.reply("lol no");
|
return await e.reply("lol no");
|
||||||
|
|
||||||
const tags = await lib.getTags(id);
|
const tags = await lib.getTags(id);
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export default async bot => {
|
|||||||
: e.args.splice(2)).filter(t => t.length > 0);
|
: e.args.splice(2)).filter(t => t.length > 0);
|
||||||
|
|
||||||
if(removetags.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 res = await Promise.all(removetags.map(async rtag => {
|
||||||
const tagid = tags.filter(t => t.tag === rtag)[0]?.id ?? null;
|
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);
|
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
||||||
if(ntags.length === 0)
|
if(ntags.length === 0)
|
||||||
return e.reply(`item ${cfg.main.url}/${id} has no tags!`);
|
return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||||
return e.reply(`item ${cfg.main.url}/${id} is now tagged as: ${ntags.join(', ')}`);
|
return await e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
42
src/inc/trigger/thumbnails.mjs
Normal file
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 { promises as fs } from "fs";
|
||||||
import flummpress from "flummpress";
|
import flummpress from "flummpress";
|
||||||
|
|
||||||
|
process.on('unhandledRejection', err => {
|
||||||
|
console.error(err);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const self = {
|
const self = {
|
||||||
_trigger: new Map(),
|
_trigger: new Map(),
|
||||||
@ -61,7 +66,8 @@ import flummpress from "flummpress";
|
|||||||
req.session = false;
|
req.session = false;
|
||||||
if(req.url.pathname.match(/^\/(s|b|t|ca)\//))
|
if(req.url.pathname.match(/^\/(s|b|t|ca)\//))
|
||||||
return;
|
return;
|
||||||
req.theme = req.cookies.theme ?? 'f0ck';
|
req.theme = req.cookies.theme || 'f0ck';
|
||||||
|
req.fullscreen = req.cookies.fullscreen || 0;
|
||||||
|
|
||||||
if(req.cookies.session) {
|
if(req.cookies.session) {
|
||||||
const user = await db`
|
const user = await db`
|
||||||
@ -95,18 +101,23 @@ import flummpress from "flummpress";
|
|||||||
where id = ${+user[0].sess_id}
|
where id = ${+user[0].sess_id}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
req.session.theme = req.cookies.theme;
|
||||||
|
req.session.fullscreen = req.cookies.fullscreen;
|
||||||
|
|
||||||
// update userprofile
|
// update userprofile
|
||||||
await db`
|
await db`
|
||||||
insert into "user_options" ${
|
insert into "user_options" ${
|
||||||
db({
|
db({
|
||||||
user_id: +user[0].id,
|
user_id: +user[0].id,
|
||||||
mode: user[0].mode ?? 0,
|
mode: user[0].mode ?? 0,
|
||||||
theme: req.session.theme ?? 'f0ck'
|
theme: req.session.theme ?? 'f0ck',
|
||||||
}, 'user_id', 'mode', 'theme')
|
fullscreen: req.session.fullscreen || 0
|
||||||
|
}, 'user_id', 'mode', 'theme', 'fullscreen')
|
||||||
}
|
}
|
||||||
on conflict ("user_id") do update set
|
on conflict ("user_id") do update set
|
||||||
mode = excluded.mode,
|
mode = excluded.mode,
|
||||||
theme = excluded.theme,
|
theme = excluded.theme,
|
||||||
|
fullscreen = excluded.fullscreen,
|
||||||
user_id = excluded.user_id
|
user_id = excluded.user_id
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,51 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<div class="about">
|
<div id="main">
|
||||||
<div>
|
<div class="about">
|
||||||
<a href="/48908"><img src="/s/img/loool.webp" /></a>
|
<div>
|
||||||
<p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif <3</p>
|
<a href="//f0ck.me/48908"><img src="//f0ck.me/s/img/loool.webp" /></a>
|
||||||
</div>
|
<p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif <3</p>
|
||||||
<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: No for Tor - Yes for cloudflare and cloudflare probably sells your soul to the devil, however our webserver doesn't log cloudflare connecting to our webserver, if you want to lurk without being flared by the cloud, see the above tor section my man</p>
|
|
||||||
</div>
|
</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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@include(snippets/footer)
|
@include(snippets/footer)
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
@include(snippets/header_admin)
|
@include(snippets/header)
|
||||||
|
<div id="main">
|
||||||
@include(snippets/footer)
|
<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)
|
@include(snippets/header)
|
||||||
@if(log)
|
<div id="main">
|
||||||
<h1>last {{ log.length }} entries:</h1>
|
@if(log)
|
||||||
<div class="logwrap">
|
<h1>last {{ log.length }} entries:</h1>
|
||||||
@each(log as line)
|
<div class="logwrap">
|
||||||
<p>{{ line }}</p>
|
@each(log as line)
|
||||||
@endeach
|
<p>{{ line }}</p>
|
||||||
|
@endeach
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const d = document.querySelector("div.logwrap");
|
||||||
|
d.scrollTop = d.scrollHeight;
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<script>
|
|
||||||
(() => {
|
|
||||||
const d = document.querySelector("div.logwrap");
|
|
||||||
d.scrollTop = d.scrollHeight;
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@endif
|
|
||||||
@include(snippets/footer)
|
@include(snippets/footer)
|
||||||
|
26
views/admin/recover.html
Normal file
26
views/admin/recover.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
@include(snippets/footer)
|
@ -1,26 +1,32 @@
|
|||||||
@include(snippets/header_admin)
|
@include(snippets/header)
|
||||||
<table style="width: 100%;">
|
<div id="main">
|
||||||
<tr>
|
<table class="table" style="width: 100%">
|
||||||
<td></td>
|
<thead>
|
||||||
<td>ID</td>
|
<tr>
|
||||||
<td>userid</td>
|
<td></td>
|
||||||
<td>user</td>
|
<td>ID</td>
|
||||||
<td>browser</td>
|
<td>userid</td>
|
||||||
<td>created_at</td>
|
<td>user</td>
|
||||||
<td>last_used</td>
|
<td>browser</td>
|
||||||
<td>last_action</td>
|
<td>created_at</td>
|
||||||
</tr>
|
<td>last_used</td>
|
||||||
@each(sessions as session)
|
<td>last_action</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>{{ session.kmsi ? '⚓' : '' }}</td>
|
</thead>
|
||||||
<td>{{ session.id }}</td>
|
<tbody>
|
||||||
<td>{{ session.user_id }}</td>
|
@each(sessions as session)
|
||||||
<td>{{ session.user }}</td>
|
<tr>
|
||||||
<td>{{ session.browser }}</td>
|
<td>{{ session.kmsi ? '⚓' : '' }}</td>
|
||||||
<td>{{ new Date(session.created_at * 1e3).toLocaleString("de-DE") }}</td>
|
<td>{{ session.id }}</td>
|
||||||
<td>{{ new Date(session.last_used * 1e3).toLocaleString("de-DE") }}</td>
|
<td>{{ session.user_id }}</td>
|
||||||
<td>{{ session.last_action }}</td>
|
<td>{{ session.user }}</td>
|
||||||
</tr>
|
<td>{{ session.browser }}</td>
|
||||||
@endeach
|
<td>{{ new Date(session.created_at * 1e3).toLocaleString("de-DE") }}</td>
|
||||||
</table>
|
<td>{{ new Date(session.last_used * 1e3).toLocaleString("de-DE") }}</td>
|
||||||
@include(snippets/footer)
|
<td>{{ session.last_action }}</td>
|
||||||
|
</tr>
|
||||||
|
@endeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@include(snippets/footer)
|
||||||
|
@ -1,8 +1,20 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<div class="container">
|
<div id="main">
|
||||||
<div class="err">
|
<div class="container">
|
||||||
<span>{{ message }}</span>
|
<div class="_error_wrapper">
|
||||||
<img src="https://f0ck.me/s/img/favicon.gif" alt="f0ck?!">
|
<div class="err">
|
||||||
|
<div class="_error_topbar">
|
||||||
|
<span>x.x</span>
|
||||||
|
</div>
|
||||||
|
<div class="_error_content">
|
||||||
|
<img src="/s/img/favicon.gif" alt="f0ck?!">
|
||||||
|
<div class="_error_message">
|
||||||
|
<span>Error</span>
|
||||||
|
<code>{{ message }}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@include(snippets/footer)
|
@include(snippets/footer)
|
@ -1,14 +1,18 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<div class="index-container">
|
<div class="pagewrapper">
|
||||||
@if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
|
<div id="main">
|
||||||
@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="index-container">
|
||||||
<div id="posts">
|
@if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
|
||||||
@each(items as item)
|
@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
|
||||||
<a href="{{ 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>
|
<div class="posts">
|
||||||
@endeach
|
@each(items as item)
|
||||||
</div>
|
<a href="{{ 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>
|
||||||
<div id="footbar">
|
@endeach
|
||||||
▼
|
</div>
|
||||||
|
<div id="footbar">
|
||||||
|
▼
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@include(snippets/footer)
|
</div>
|
||||||
|
@include(snippets/footer)
|
176
views/item.html
176
views/item.html
@ -1,88 +1,106 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<div class="container">
|
<canvas class="hidden-xs" id="bg"></canvas>
|
||||||
<div class="content">
|
<div class="wrapper">
|
||||||
<div class="next-post">
|
<div id="main">
|
||||||
@if(pagination.prev)
|
<div class="container">
|
||||||
<div class="arrow-next">
|
<div class="_204863">
|
||||||
<a id="next" href="{{ link.main }}{{ pagination.prev }}"></a>
|
<div class="imageDoor" style="--hover-image: url('/t/{{ item.id }}.webp');">
|
||||||
|
<img src="/t/{{ item.id }}.webp" alt="" />
|
||||||
</div>
|
</div>
|
||||||
@else
|
<div class="gapLeft">
|
||||||
<div class="arrow-next">
|
<span class="populateME"><b>f0ck</b> - {{ item.id }}</span>
|
||||||
<a id="next" href="#" style="color: #ccc !important;"></a>
|
|
||||||
</div>
|
</div>
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
<div class="media-object">
|
|
||||||
@if(item.mime.startsWith("video"))
|
|
||||||
<div class="embed-responsive embed-responsive-16by9">
|
|
||||||
<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;">
|
|
||||||
<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"))
|
|
||||||
<div class="embed-responsive embed-responsive-16by9">
|
|
||||||
<div class="embed-responsive-image" id="image-scroll">
|
|
||||||
<a href="{{ item.dest }}" id="elfe" target="_blank"><img id="f0ck-image" class="img-fluid" src="{{ item.dest }}" loading="lazy" decoding="async"/></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@else
|
|
||||||
<h1>404 - Not f0cked</h1>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
<div class="previous-post">
|
|
||||||
@if(pagination.next)
|
|
||||||
<div class="arrow-prev">
|
|
||||||
<a id="prev" href="{{ link.main }}{{ pagination.next }}"></a>
|
|
||||||
</div>
|
|
||||||
@else
|
|
||||||
<div class="arrow-prev">
|
|
||||||
<a id="prev" href="#" style="color: #ccc !important;"></a>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="metadata">
|
|
||||||
<span class="badge badge-dark">
|
|
||||||
<a href="/{{ item.id }}" style="--hover-image: url('/t/{{ item.id }}.webp');" class="id-link">{{ item.id }}</a>
|
|
||||||
@if(session)
|
@if(session)
|
||||||
(<a id="a_username" href="/user/{{ user.name.toLowerCase() }}/f0cks@if(tmp.mime)/{{ tmp.mime }}@endif">{{ user.name }}</a>)
|
<div class="gapRight">
|
||||||
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>
|
<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_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_tfull"><use href="/s/img/iconset.svg#window-{{ fullscreen == 1 ? 'minimize' : 'maximize' }}"></use></svg>
|
||||||
|
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</span>
|
</div>
|
||||||
<span class="badge badge-dark">{{ user.network }} / {{ user.channel }}</span>
|
<div class="content">
|
||||||
<span class="badge badge-dark image-source">
|
<div class="next-post">
|
||||||
@if(item.src.long.length)
|
@if(pagination.prev)
|
||||||
<a href="{{ item.src.long }}" target="_blank">{{ item.src.short }}</a>
|
<div class="arrow-next">
|
||||||
@else
|
<a id="next" href="{{ link.main }}{{ pagination.prev }}"></a>
|
||||||
hidden
|
</div>
|
||||||
@endif
|
@else
|
||||||
</span>
|
<div class="arrow-next">
|
||||||
<span class="badge badge-dark"><a class="dest-link" href="{{ item.dest }}" target="_blank">{{ item.mime }}</a> / {{ item.size }}</span>
|
<a id="next" href="#" style="color: #ccc !important;"></a>
|
||||||
<span class="badge badge-dark"><time class="timeago" tooltip="{{ item.timestamp.timefull }}">{{ item.timestamp.timeago }}</time></span>
|
</div>
|
||||||
<span class="badge badge-dark">{{ phrase }}</span>
|
@endif
|
||||||
<span class="badge badge-dark" id="tags">
|
</div>
|
||||||
@if(typeof item.tags !== "undefined")
|
<div class="media-object">
|
||||||
@each(item.tags as tag)
|
@if(item.mime.startsWith("video"))
|
||||||
<span @if(session)tooltip="{{ tag.user }}"@endif class="badge {{ tag.badge }} mr-2">
|
<div class="embed-responsive embed-responsive-16by9">
|
||||||
<a href="/tag/{{ tag.tag }}">{!! tag.tag !!}</a>@if(session) <a class="removetag" href="#">×</a>@endif
|
<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;">
|
||||||
|
<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"))
|
||||||
|
<div class="embed-responsive embed-responsive-16by9">
|
||||||
|
<div class="embed-responsive-image" id="image-scroll">
|
||||||
|
<a href="{{ item.dest }}" id="elfe" target="_blank"><img id="f0ck-image" class="img-fluid" src="{{ item.dest }}" loading="lazy" decoding="async"/></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<h1>404 - Not f0cked</h1>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="previous-post">
|
||||||
|
@if(pagination.next)
|
||||||
|
<div class="arrow-prev">
|
||||||
|
<a id="prev" href="{{ link.main }}{{ pagination.next }}"></a>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="arrow-prev">
|
||||||
|
<a id="prev" href="#" style="color: #ccc !important;"></a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="metadata">
|
||||||
|
<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>
|
||||||
|
<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.normalized }}">{!! tag.tag !!}</a>@if(session) <a class="removetag" href="#">×</a>@endif
|
||||||
|
</span>
|
||||||
|
@endeach
|
||||||
|
@endif
|
||||||
|
@if(session)
|
||||||
|
<a href="#" id="a_addtag">add tag</a> - <a href="#" id="a_toggle">toggle</a>
|
||||||
|
<datalist id="testlist"></datalist>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
<span class="badge" id="favs"@if(!item.favorites.length || !session) hidden@endif>
|
||||||
|
@if(item.favorites.length && session)
|
||||||
|
@each(item.favorites as fav)
|
||||||
|
<a href="/user/{{ fav.user.toLowerCase() }}/favs" tooltip="{{ fav.user }}" flow="up"><img src="@if(fav.avatar)/t/{{ fav.avatar }}.webp@else/s/img/default.png@endif" style="height: 32px; width: 32px" /></a>
|
||||||
@endeach
|
@endeach
|
||||||
@endif
|
@endif
|
||||||
@if(session)
|
</span>
|
||||||
<a href="#" id="a_addtag">add tag</a> - <a href="#" id="a_toggle">toggle</a>
|
</div>
|
||||||
<datalist id="testlist"></datalist>
|
|
||||||
@endif
|
|
||||||
</span>
|
|
||||||
<span class="badge" id="favs"@if(!item.favorites.length || !session) hidden@endif>
|
|
||||||
@if(item.favorites.length && session)
|
|
||||||
@each(item.favorites as fav)
|
|
||||||
<a href="/user/{{ fav.user.toLowerCase() }}/favs" tooltip="{{ fav.user }}" flow="up"><img src="@if(fav.avatar)/t/{{ fav.avatar }}.webp@else/s/img/default.png@endif" style="height: 32px; width: 32px" /></a>
|
|
||||||
@endeach
|
|
||||||
@endif
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@include(snippets/footer)
|
</div>
|
||||||
|
@include(snippets/footer)
|
||||||
|
@ -1,40 +1,51 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<div class="topf0ckers">
|
<div id="main">
|
||||||
<h3>Top f0ckers of all time <br>- Ranking -</h3>
|
<div class="topf0ckers">
|
||||||
</div>
|
<h3>Top f0ckers of all time <br>- Ranking -</h3>
|
||||||
<div class="ranking">
|
|
||||||
<div class="by-user">
|
|
||||||
<h3>Biggest taggers</h3>
|
|
||||||
<table class="table">
|
|
||||||
<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>{{ list[i].count }}</td>
|
|
||||||
</tr>
|
|
||||||
@endfor
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="by-hoster">
|
<div class="ranking">
|
||||||
<h3>Top {{ hoster.length }} hoster</h3>
|
<div class="by-user">
|
||||||
<table class="table">
|
<h3>Biggest taggers</h3>
|
||||||
@each(hoster as host)
|
<table class="table">
|
||||||
<tr><td>{{ host.part.length ? host.part : "Telegram" }}</td><td>{{ host.c }}</td></tr>
|
<tbody>
|
||||||
@endeach
|
@for(let i = 0; i < list.length; i++)
|
||||||
</table>
|
<tr>
|
||||||
</div>
|
<td>{{ i + 1 }}</td>
|
||||||
<div class="by-stats">
|
<td><a href="/{{ list[i].avatar }}"><img class="avatar" src="/t/{{ list[i].avatar }}.webp" /></a></td>
|
||||||
<h3>Tag stats</h3>
|
<td><a href="/user/{!! list[i].user !!}">{!! list[i].user !!}</a></td>
|
||||||
<table class="table">
|
<td>{{ list[i].count }}</td>
|
||||||
<tr><td>total</td><td>{{ stats.total }}</td></tr>
|
</tr>
|
||||||
<tr><td>tagged</td><td>{{ stats.tagged }}</td></tr>
|
@endfor
|
||||||
<tr><td>untagged</td><td>{{ stats.untagged }}</td></tr>
|
</tbody>
|
||||||
<tr><td>SFW</td><td>{{ stats.sfw }}</td></tr>
|
</table>
|
||||||
<tr><td>NSFW</td><td>{{ stats.nsfw }}</td></tr>
|
</div>
|
||||||
</table>
|
<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">
|
||||||
|
<tr><td>total</td><td>{{ stats.total }}</td></tr>
|
||||||
|
<tr><td>tagged</td><td>{{ stats.tagged }}</td></tr>
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
@include(snippets/footer)
|
@include(snippets/footer)
|
||||||
|
@ -1,35 +1,47 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<h1 style="text-align: center">f0ckgle</h1>
|
<div id="main">
|
||||||
<form action="/search" class="admin-search">
|
<div class="f0ckgle">
|
||||||
<input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit"><b>f0ck</b></button>
|
<div class="search-title">
|
||||||
</form>
|
<span style="color:#4285F4;">f</span>
|
||||||
<div class="results">
|
<span style="color:#EA4335;">0</span>
|
||||||
@if(result)
|
<span style="color:#FBBC05;">c</span>
|
||||||
<h1>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h1>
|
<span style="color:#4285F4;">k</span>
|
||||||
<table style="width: 100%" class="table">
|
<span style="color:#34A853;">g</span>
|
||||||
<thead>
|
<span style="color:#EA4335;">l</span>
|
||||||
<tr>
|
<span style="color:#4285F4;">e</span>
|
||||||
<th>Thumbnail</th>
|
</div>
|
||||||
<th>ID</th>
|
<form action="/search" class="admin-search">
|
||||||
<th>Tag</th>
|
<input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit">🔍</button>
|
||||||
<th>Mime</th>
|
</form>
|
||||||
<th>Username</th>
|
<div class="results">
|
||||||
<th>Score</th>
|
@if(result)
|
||||||
</tr>
|
<h2>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h2>
|
||||||
</thead>
|
<table style="width: 100%" class="table">
|
||||||
<tbody>
|
<thead>
|
||||||
@each(result as line)
|
<tr>
|
||||||
<tr>
|
<th>Thumbnail</th>
|
||||||
<td style="width: 128px;"><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank"><img src="/t/{{ line.id }}.webp" /></a></td>
|
<th>ID</th>
|
||||||
<td><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank">{{ line.id }}</a></td>
|
<th>Tag</th>
|
||||||
<td><a href="/tag/{!! line.tag !!}">{!! line.tag !!}</a></td>
|
<th>Mime</th>
|
||||||
<td>{{ line.mime }}</td>
|
<th>Username</th>
|
||||||
<td><a href="/user/{!! line.username !!}/f0cks/{{ line.id }}">{!! line.username !!}</a></td>
|
<th>Score</th>
|
||||||
<td>{{ line.score.toFixed(2) }}</td>
|
</tr>
|
||||||
</tr>
|
</thead>
|
||||||
@endeach
|
<tbody>
|
||||||
</tbody>
|
@each(result as line)
|
||||||
</table>
|
<tr>
|
||||||
@endif
|
<td style="width: 128px;"><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank"><img src="/t/{{ line.id }}.webp" /></a></td>
|
||||||
|
<td><span class="mview_desc">ID:</span><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank">{{ line.id }}</a></td>
|
||||||
|
<td><span class="mview_desc">Tag:</span><a href="/tag/{!! line.tag !!}">{!! line.tag !!}</a></td>
|
||||||
|
<td><span class="mview_desc">Mime:</span>{{ line.mime }}</td>
|
||||||
|
<td><span class="mview_desc">User:</span><a href="/user/{!! line.username !!}/f0cks/{{ line.id }}">{!! line.username !!}</a></td>
|
||||||
|
<td><span class="mview_desc">Score:</span>{{ line.score?.toFixed(2) }}</td>
|
||||||
|
</tr>
|
||||||
|
@endeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@include(snippets/footer)
|
@include(snippets/footer)
|
||||||
|
@ -1,52 +1,54 @@
|
|||||||
@include(snippets/header)
|
@include(snippets/header)
|
||||||
<h1>Settings</h1>
|
<div id="main">
|
||||||
@if(session.avatar)<a href="//f0ck.me/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif
|
<h1>Settings</h1>
|
||||||
<h2>Account</h2>
|
@if(session.avatar)<a href="/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif
|
||||||
<table class="table">
|
<h2>Account</h2>
|
||||||
<tbody>
|
<table class="table">
|
||||||
<tr>
|
<tbody>
|
||||||
<td>UserID</td>
|
<tr>
|
||||||
<td>{{ session.id }}</td>
|
<td>UserID</td>
|
||||||
</tr>
|
<td>{{ session.id }}</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>level</td>
|
<tr>
|
||||||
<td>{{ session.level }}/100</td>
|
<td>level</td>
|
||||||
</tr>
|
<td>{{ session.level }}/100</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>username</td>
|
<tr>
|
||||||
<td>{!! session.user !!}</td>
|
<td>username</td>
|
||||||
</tr>
|
<td>{!! session.user !!}</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>avatar</td>
|
<tr>
|
||||||
<td><input type="text" class="input" name="i_avatar" value="{{ session.avatar }}" /><input type="submit" id="s_avatar" value="save" /></td>
|
<td>avatar</td>
|
||||||
</tr>
|
<td><input type="text" class="input" name="i_avatar" value="{{ session.avatar }}" /><input type="submit" id="s_avatar" value="save" /></td>
|
||||||
</tbody>
|
</tr>
|
||||||
</table>
|
</tbody>
|
||||||
<h2>Sessions</h2>
|
</table>
|
||||||
<table class="table">
|
<h2>Sessions</h2>
|
||||||
<thead>
|
<table class="table">
|
||||||
<tr>
|
<thead>
|
||||||
<th> </th>
|
<tr>
|
||||||
<th>id</th>
|
<th> </th>
|
||||||
<th> </th>
|
<th>id</th>
|
||||||
<th>last action</th>
|
<th> </th>
|
||||||
</tr>
|
<th>last action</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
@each(sessions as sess)
|
<tbody>
|
||||||
<tr@if(sess.id === session.sess_id) style="background-color: rgb(0, 89, 0)"@endif>
|
@each(sessions as sess)
|
||||||
<td>{{ sess.kmsi ? '⚓' : '' }}</td>
|
<tr@if(sess.id === session.sess_id) style="background-color: rgb(0, 89, 0)"@endif>
|
||||||
<td tooltip="{{ sess.browser }}" flow="right">
|
<td>{{ sess.kmsi ? '⚓' : '' }}</td>
|
||||||
<p>{{ sess.id }}</p>
|
<td tooltip="{{ sess.browser }}" flow="right">
|
||||||
<p><a href="#" onclick="alert('not yet implemented!');">logout</a></p>
|
<p>{{ sess.id }}</p>
|
||||||
</td>
|
<p><a href="#" onclick="alert('not yet implemented!');">logout</a></p>
|
||||||
<td>
|
</td>
|
||||||
<p>last_used: {{ new Date(sess.last_used * 1e3).toLocaleString("de-DE") }}</p>
|
<td>
|
||||||
<p>created_at: {{ new Date(sess.created_at * 1e3).toLocaleString("de-DE") }}</p>
|
<p>last_used: {{ new Date(sess.last_used * 1e3).toLocaleString("de-DE") }}</p>
|
||||||
</td>
|
<p>created_at: {{ new Date(sess.created_at * 1e3).toLocaleString("de-DE") }}</p>
|
||||||
<td><a href="{{ sess.last_action }}" target="_blank">{{ sess.last_action }}</a></td>
|
</td>
|
||||||
</tr>
|
<td><a href="{{ sess.last_action }}" target="_blank">{{ sess.last_action }}</a></td>
|
||||||
@endeach
|
</tr>
|
||||||
</tbody>
|
@endeach
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@include(snippets/footer)
|
@include(snippets/footer)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!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>
|
<head>
|
||||||
<title>f0ck!</title>
|
<title>f0ck!</title>
|
||||||
<meta name="description" content="Welcome to the internet"/>
|
<meta name="description" content="Welcome to the internet"/>
|
||||||
|
@ -1,18 +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)
|
|
@ -1,83 +1,134 @@
|
|||||||
<nav class="navbar navbar-expand-lg">
|
@if(session)
|
||||||
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
|
<nav class="navbar navbar-expand-lg">
|
||||||
<div class="navigation-links">
|
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
|
||||||
<ul class="navbar-nav">
|
<div class="navigation-links">
|
||||||
<li class="nav-item dropdown">
|
<ul class="navbar-nav">
|
||||||
@if(session)
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="#" content="{{ session.user }}" data-toggle="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" /> {{ session.user }}
|
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<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() }}/f0cks">my f0cks</a></li>
|
||||||
<li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</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>
|
||||||
<li><a href="/search">search</a></li>
|
<li><a href="/admin">Admin</a></li>
|
||||||
<li><a href="/about">About</a></li>
|
<li><a href="/about">About</a></li>
|
||||||
<li><a href="/ranking">Ranking</a></li>
|
<li><a href="/ranking">ranking</a></li>
|
||||||
<li><a href="/logout">logout</a></li>
|
<li><a href="/settings">settings</a></li>
|
||||||
</ul>
|
<li><a href="/logout">logout</a></li>
|
||||||
@else
|
</ul>
|
||||||
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
|
</li>
|
||||||
<ul class="dropdown-menu">
|
<li class="nav-item dropdown" id="themes">
|
||||||
<li><a href="/login">login</a></li>
|
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
|
||||||
</ul>
|
<ul class="dropdown-menu">
|
||||||
@endif
|
@each(themes as t)
|
||||||
</li>
|
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
|
||||||
<li class="nav-item dropdown" id="themes">
|
@endeach
|
||||||
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Theme</a>
|
</ul>
|
||||||
<ul class="dropdown-menu">
|
</li>
|
||||||
@each(themes as t)
|
<li class="nav-item dropdown">
|
||||||
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
|
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime) ▼@endif</a>
|
||||||
@endeach
|
<ul class="dropdown-menu">
|
||||||
</ul>
|
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
|
||||||
</li>
|
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
|
||||||
<span class="placeholder"> </span>
|
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
|
||||||
<li class="nav-item dropdown">
|
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
|
||||||
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime) ▼@endif</a>
|
</ul>
|
||||||
<ul class="dropdown-menu">
|
</li>
|
||||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
|
<li class="nav-item @if(session)dropdown@endif">
|
||||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
|
<a class="nav-link ddcontent" href="#"@if(typeof session.mode !== "undefined") content="{{ modes[session.mode] ?? 'sfw' }}" data-toggle="dropdown"@endif>Mode</a>
|
||||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
|
@for(let i = 0; i < modes.length; i++)
|
||||||
</ul>
|
<li><a class="dropdown-item" href="/mode/{{ i }}">{{ modes[i] }}</a></li>
|
||||||
</li>
|
@endfor
|
||||||
@if(session)
|
</ul>
|
||||||
<li class="nav-item @if(session)dropdown@endif">
|
</li>
|
||||||
<a class="nav-link ddcontent" href="#"@if(typeof session.mode !== "undefined") content="{{ modes[session.mode] ?? 'sfw' }}" data-toggle="dropdown"@endif>Mode</a>
|
<li class="nav-item">
|
||||||
<ul class="dropdown-menu">
|
<a id="random" class="nav-link" href="/random">
|
||||||
@for(let i = 0; i < modes.length; i++)
|
<span class="nav-link-identifier">Random</span>
|
||||||
<li><a class="dropdown-item" href="/mode/{{ i }}">{{ modes[i] }}</a></li>
|
</a>
|
||||||
@endfor
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</div>
|
||||||
@endif
|
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
||||||
<li class="nav-item">
|
<div class="pagination-container-fluid">
|
||||||
<a id="random" class="nav-link" href="/random">
|
<div class="pagination-wrapper">
|
||||||
<span class="nav-link-identifier">Random</span>
|
@if(typeof pagination !== "undefined")
|
||||||
</a>
|
<nav class="pagination">
|
||||||
</li>
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">«</a>
|
||||||
</ul>
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">‹</a>
|
||||||
</div>
|
@each(pagination.cheat as i)
|
||||||
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
@if(i == pagination.page)
|
||||||
<div class="pagination-container-fluid">
|
<span class="btn disabled">{{ i }}</span>
|
||||||
<div class="pagination-wrapper">
|
@else
|
||||||
@if(typeof pagination !== "undefined")
|
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
|
||||||
<nav class="pagination">
|
@endif
|
||||||
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">«</a>
|
@endeach
|
||||||
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">‹</a>
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">›</a>
|
||||||
@each(pagination.cheat as i)
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">»</a>
|
||||||
@if(i == pagination.page)
|
</nav>
|
||||||
<span class="btn disabled">{{ i }}</span>
|
@endif
|
||||||
@else
|
</div>
|
||||||
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
|
</div>
|
||||||
@endif
|
</div>
|
||||||
@endeach
|
</nav>
|
||||||
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">›</a>
|
@else
|
||||||
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">»</a>
|
<nav class="navbar navbar-expand-lg">
|
||||||
</nav>
|
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
|
||||||
@endif
|
|
||||||
</div>
|
<div class="navigation-links-guest">
|
||||||
</div>
|
<ul class="navbar-nav-guests">
|
||||||
</div>
|
<li class="nav-item dropdown">
|
||||||
</nav>
|
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="/login">login</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">
|
||||||
|
@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">
|
||||||
|
<a id="random" class="nav-link" href="/random">
|
||||||
|
<span class="nav-link-identifier">Random</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</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">
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">«</a>
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">‹</a>
|
||||||
|
@each(pagination.cheat as i)
|
||||||
|
@if(i == pagination.page)
|
||||||
|
<span class="btn disabled">{{ i }}</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
|
||||||
|
@endif
|
||||||
|
@endeach
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">›</a>
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">»</a>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@endif
|
133
views/snippets/navbar2.html
Normal file
133
views/snippets/navbar2.html
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
@if(session)
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
|
||||||
|
<div class="navigation-links">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link" 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>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="/admin">adminpanel</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>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
<li><a href="/ranking">Ranking</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">
|
||||||
|
@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">
|
||||||
|
@for(let i = 0; i < modes.length; i++)
|
||||||
|
<li><a class="dropdown-item" href="/mode/{{ i }}">{{ modes[i] }}</a></li>
|
||||||
|
@endfor
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a id="random" class="nav-link" href="/random">
|
||||||
|
<span class="nav-link-identifier">Random</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</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">
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">«</a>
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">‹</a>
|
||||||
|
@each(pagination.cheat as i)
|
||||||
|
@if(i == pagination.page)
|
||||||
|
<span class="btn disabled">{{ i }}</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
|
||||||
|
@endif
|
||||||
|
@endeach
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">›</a>
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">»</a>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@else
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</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>
|
||||||
|
<li class="nav-item dropdown" id="themes">
|
||||||
|
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
@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">
|
||||||
|
<a id="random" class="nav-link" href="/random">
|
||||||
|
<span class="nav-link-identifier">Random</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</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">
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">«</a>
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">‹</a>
|
||||||
|
@each(pagination.cheat as i)
|
||||||
|
@if(i == pagination.page)
|
||||||
|
<span class="btn disabled">{{ i }}</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
|
||||||
|
@endif
|
||||||
|
@endeach
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">›</a>
|
||||||
|
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">»</a>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@endif
|
@ -1,37 +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>
|
|
||||||
@if(typeof totals !== "undefined")
|
|
||||||
<li class="nav-item" style="width: 100%; text-align: center">
|
|
||||||
total: {{ totals.total }} | tagged: {{ totals.tagged }} | untagged: {{ totals.untagged }} | sfw: {{ totals.sfw }} | nsfw: {{ totals.nsfw }}
|
|
||||||
</li>
|
|
||||||
@endif
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
|
||||||
<div class="pagination-container-fluid">
|
|
||||||
<div class="pagination-wrapper">
|
|
||||||
Henlo, {{ session.user }} <a href="/logout">Logout</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
14
views/top10.html
Normal file
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="//f0ck.me/{{ f0ck.id }}"><img src="//f0ck.me/t/{{ f0ck.id }}.webp" /></a></td>
|
||||||
|
<td><a href="//f0ck.me/{{ f0ck.id }}">{{ f0ck.id }}</a></td>
|
||||||
|
<td>{{ f0ck.username }}</td>
|
||||||
|
</tr>
|
||||||
|
@endeach
|
||||||
|
</table>
|
||||||
|
|
||||||
|
@include(snippets/footer)
|
49
views/user.html
Normal file
49
views/user.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
@include(snippets/header)
|
||||||
|
<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>{{ 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('items' in 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>
|
||||||
|
@else
|
||||||
|
no f0cks given
|
||||||
|
@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('items' in 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>
|
||||||
|
@else
|
||||||
|
no favorites
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@include(snippets/footer)
|
Reference in New Issue
Block a user