Compare commits
211 Commits
06813a87d4
...
comments
Author | SHA1 | Date | |
---|---|---|---|
d951a072ee | |||
a64a4da8cd | |||
ddaaaee502 | |||
cc5ddf27d3 | |||
e7f6ecd239 | |||
2c9058dec7 | |||
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 |
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,7 +1,10 @@
|
||||
node_modules/
|
||||
logs/*.log
|
||||
config.json
|
||||
public/b/*
|
||||
public/ca/*
|
||||
public/t/*
|
||||
public/b
|
||||
public/ca
|
||||
public/t
|
||||
deleted/b
|
||||
deleted/ca
|
||||
deleted/t
|
||||
tmp/*
|
||||
|
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# how to install:
|
||||
## dependencies
|
||||
```bash
|
||||
sudo pacman -S nodejs npm ffmpeg yt-dlp ffmpegthumbnailer postgresql python python-pip 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
|
||||
pip install nsfw_detector
|
||||
#if this fails make sure to have enough dedicated ram or swap space, alternatively try with pip install nsfw_detector --no-cache-dir
|
||||
npm start
|
||||
```
|
61
config_example.json
Normal file
61
config_example.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"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" ],
|
||||
"eps": 294,
|
||||
"cache": false,
|
||||
"phrases": [
|
||||
"<img src=\"/s/img/crap/nyancat.gif\" style=\"margin-top: 5px\" />"
|
||||
]
|
||||
},
|
||||
"clients": [
|
||||
|
||||
],
|
||||
"sql": {
|
||||
"host": "localhost",
|
||||
"user": "f0ck",
|
||||
"password": "",
|
||||
"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/flac": "flac",
|
||||
"audio/x-flac": "flac",
|
||||
"video/x-m4v": "mp4"
|
||||
}
|
||||
}
|
32
debug/adduser.mjs
Normal file
32
debug/adduser.mjs
Normal file
@ -0,0 +1,32 @@
|
||||
import db from '../src/inc/sql.mjs';
|
||||
import lib from '../src/inc/lib.mjs';
|
||||
|
||||
import readline from 'node:readline/promises';
|
||||
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
|
||||
const newuser = process.argv[2]?.length ? process.argv[2] : await rl.question('username: ');
|
||||
const password = await rl.question('password: ');
|
||||
const level = +(await rl.question('level (0-100): '));
|
||||
|
||||
rl.close();
|
||||
|
||||
if(!newuser.length || !password.length) {
|
||||
console.log('nope lol');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const id = (await db`
|
||||
insert into "user" ${
|
||||
db({
|
||||
login: newuser.toLowerCase(),
|
||||
user: newuser,
|
||||
password: await lib.hash(password),
|
||||
level: level >= 0 && level <= 100 ? level : 0
|
||||
})
|
||||
}
|
||||
returning id
|
||||
`)[0]?.id;
|
||||
|
||||
console.log(`created new user ${newuser} with ID ${id}`);
|
||||
process.exit();
|
@ -36,7 +36,7 @@ import lib from "../src/inc/lib.mjs";
|
||||
db({
|
||||
item_id: f.id,
|
||||
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" ${
|
||||
db({
|
||||
item_id: f.id,
|
||||
tag_id: 8, // hentai
|
||||
user_id: 7 // autotagger
|
||||
tag_id: 4, // hentai
|
||||
user_id: 1 // autotagger
|
||||
})
|
||||
}
|
||||
`;
|
||||
|
214
f0ck.sql
Normal file
214
f0ck.sql
Normal file
@ -0,0 +1,214 @@
|
||||
\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
|
||||
);
|
||||
|
||||
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;
|
37
package-lock.json
generated
37
package-lock.json
generated
@ -9,18 +9,18 @@
|
||||
"version": "2.2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cuffeo": "^1.0.7-3",
|
||||
"cuffeo": "^1.1.0",
|
||||
"flumm-fetch": "^1.0.1",
|
||||
"flummpress": "^2.0.5",
|
||||
"postgres": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cuffeo": {
|
||||
"version": "1.0.7-3",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.0.7-3.tgz",
|
||||
"integrity": "sha512-Lz8AlLdFWeLLGsf6KBXTHnpseeMbuQH69BamhZr8O9Se9pzuHQgv/ed13n2XWreN0RTyRa49YPtsNVwoQnNrLw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.1.0.tgz",
|
||||
"integrity": "sha512-7lmx2dvREqCYwy8oUzk3Q0EkyLZkKQTYTBLEjNqKVbinzdEL45Oimi54lmBDFPlrrvTLzQkFvzKmiJ7Zcegi2w==",
|
||||
"dependencies": {
|
||||
"flumm-fetch-cookies": "^1.4.0"
|
||||
"flumm-fetch": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/flumm-fetch": {
|
||||
@ -28,17 +28,6 @@
|
||||
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
|
||||
@ -56,11 +45,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"cuffeo": {
|
||||
"version": "1.0.7-3",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.0.7-3.tgz",
|
||||
"integrity": "sha512-Lz8AlLdFWeLLGsf6KBXTHnpseeMbuQH69BamhZr8O9Se9pzuHQgv/ed13n2XWreN0RTyRa49YPtsNVwoQnNrLw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.1.0.tgz",
|
||||
"integrity": "sha512-7lmx2dvREqCYwy8oUzk3Q0EkyLZkKQTYTBLEjNqKVbinzdEL45Oimi54lmBDFPlrrvTLzQkFvzKmiJ7Zcegi2w==",
|
||||
"requires": {
|
||||
"flumm-fetch-cookies": "^1.4.0"
|
||||
"flumm-fetch": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"flumm-fetch": {
|
||||
@ -68,14 +57,6 @@
|
||||
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "f0ckv2",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"description": "f0ck, kennste?",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
@ -9,12 +9,13 @@
|
||||
"autotagger": "node --experimental-json-modules debug/autotagger.mjs",
|
||||
"thumbnailer": "node --experimental-json-modules debug/thumbnailer.mjs",
|
||||
"test": "node --experimental-json-modules debug/test.mjs",
|
||||
"clean": "node --experimental-json-modules debug/clean.mjs"
|
||||
"clean": "node --experimental-json-modules debug/clean.mjs",
|
||||
"adduser": "node --experimental-json-modules debug/adduser.mjs"
|
||||
},
|
||||
"author": "Flummi",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cuffeo": "^1.0.7-3",
|
||||
"cuffeo": "^1.1.0",
|
||||
"flumm-fetch": "^1.0.1",
|
||||
"flummpress": "^2.0.5",
|
||||
"postgres": "^3.0.1"
|
||||
|
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
@ -3,8 +3,8 @@
|
||||
<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) -->
|
||||
<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 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="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="heart_regular" viewBox="0 0 512 512"><path d="M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"/></symbol>
|
||||
<symbol id="heart_solid" viewBox="0 0 512 512"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"/></symbol>
|
||||
<symbol id="cross" viewBox="0 0 512 512"><path d="M53.6,62.3 L458.4,458.4 M458.4,62.3 L53.6,458.4" style="stroke-linecap: round;stroke-width: 60;"/></symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
@ -101,6 +101,7 @@ const flash = ({ type, msg }) => {
|
||||
delbutton.href = "#";
|
||||
delbutton.addEventListener("click", deleteEvent);
|
||||
span.insertAdjacentElement("beforeend", a);
|
||||
span.innerHTML += ' ';
|
||||
span.insertAdjacentElement("beforeend", delbutton);
|
||||
|
||||
document.querySelector("#tags").insertAdjacentElement("afterbegin", span);
|
||||
@ -287,6 +288,7 @@ const flash = ({ type, msg }) => {
|
||||
|
||||
textfield.addEventListener("keyup", async e => {
|
||||
if(e.key === 'Enter') {
|
||||
parent.removeChild(textfield);
|
||||
// send
|
||||
let res = await fetch('/api/v2/admin/tags/' + encodeURIComponent(oldtag), {
|
||||
method: 'PUT',
|
||||
@ -303,7 +305,7 @@ const flash = ({ type, msg }) => {
|
||||
switch(status) {
|
||||
case 200: // success, change
|
||||
case 201:
|
||||
parent.removeChild(textfield);
|
||||
//parent.removeChild(textfield);
|
||||
parent.insertAdjacentElement('afterbegin', old);
|
||||
parent.querySelector('a:last-child').style.display = '';
|
||||
old.href = `/tag/${res.tag}`;
|
||||
|
@ -29,6 +29,8 @@
|
||||
};
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.key in keybindings && e.target.tagName !== "INPUT") {
|
||||
if(e.shiftKey || e.ctrlKey || e.metaKey || e.altKey)
|
||||
return;
|
||||
e.preventDefault();
|
||||
keybindings[e.key]();
|
||||
}
|
||||
@ -56,7 +58,7 @@
|
||||
: f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
|
||||
f0ckimage.hasAttribute("style")
|
||||
? f0ckimage.removeAttribute("style")
|
||||
: f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0;");
|
||||
: 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>
|
||||
@ -64,13 +66,13 @@
|
||||
// <scroller>
|
||||
let tts = 0;
|
||||
const scroll_treshold = 1;
|
||||
if(document.querySelector("div#posts")) {
|
||||
if([...document.querySelectorAll("div.posts")].length === 1) {
|
||||
document.addEventListener("wheel", e => {
|
||||
if(Math.ceil(window.innerHeight + window.scrollY) >= document.body.offsetHeight && e.deltaY > 0) { // down
|
||||
if(elem = document.querySelector(".pagination > .next:not(.disabled)")) {
|
||||
if(tts < scroll_treshold) {
|
||||
document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--accent)";
|
||||
document.querySelector("div#footbar").style.color = "var(--accent)";
|
||||
document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--footbar-color)";
|
||||
document.querySelector("div#footbar").style.color = "var(--footbar-color)";
|
||||
tts++;
|
||||
}
|
||||
else
|
||||
@ -80,7 +82,7 @@
|
||||
else if(window.scrollY <= 0 && e.deltaY < 0) { // up
|
||||
if(elem = document.querySelector(".pagination > .prev:not(.disabled)")) {
|
||||
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";
|
||||
tts++;
|
||||
}
|
||||
@ -150,10 +152,10 @@
|
||||
}
|
||||
else {
|
||||
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
|
||||
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)");
|
||||
}
|
||||
}
|
||||
|
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 |
@ -20,7 +20,7 @@ export default async bot => {
|
||||
|
||||
let trigger;
|
||||
|
||||
if(e.photo) {
|
||||
if(e.media) {
|
||||
trigger = [...bot._trigger.entries()].filter(t => t[1].name === "parser");
|
||||
if(!e.message)
|
||||
e.message = "";
|
||||
|
@ -63,7 +63,7 @@ export default new class {
|
||||
const link = [];
|
||||
if(env.tag) link.push("tag", env.tag);
|
||||
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('/');
|
||||
if(!tmp.endsWith('/'))
|
||||
@ -162,14 +162,13 @@ export default new class {
|
||||
return tags;
|
||||
};
|
||||
async detectNSFW(dest) {
|
||||
return false;
|
||||
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}'))"`
|
||||
);
|
||||
const res = JSON.parse(stdout.replace(/\'/g, '"').split('\n').slice(1, -1));
|
||||
const tmp = Object.values(res)[0];
|
||||
|
||||
tmp.sexy = tmp.sexy / 2;
|
||||
|
||||
let nsfw = false;
|
||||
if(tmp.neutral >= .7)
|
||||
nsfw = false;
|
||||
@ -187,5 +186,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;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -4,158 +4,74 @@ import cfg from "../config.mjs";
|
||||
import fs from "fs";
|
||||
import url from "url";
|
||||
|
||||
const globalfilter = cfg.nsfp.map(n => `tag_id = ${n}`).join(' or ');
|
||||
|
||||
export default {
|
||||
getf0cks: async (o = { user, tag, mime, page, mode, fav }) => {
|
||||
getf0cks: async (o = { user, tag, mime, page, mode, fav, session }) => {
|
||||
const user = o.user ? decodeURI(o.user) : null;
|
||||
const tag = lib.parseTag(o.tag ?? null);
|
||||
const mime = (o.mime ?? "");
|
||||
const mime = o.mime ?? null;
|
||||
const page = +(o.page ?? 1);
|
||||
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);
|
||||
|
||||
let data;
|
||||
let total;
|
||||
|
||||
if(tag) {
|
||||
if(tag.match(/sfw/) || tag.length <= 2)
|
||||
return {
|
||||
success: false,
|
||||
message: "nope."
|
||||
};
|
||||
|
||||
total = await db`
|
||||
select count(*) as total
|
||||
const total = (await db`
|
||||
select distinct on (items.id)
|
||||
count(items.id) 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`;
|
||||
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 ? 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 = 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)
|
||||
if(!total || total === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message: "404 - no f0cks given"
|
||||
};
|
||||
}
|
||||
|
||||
const pages = +Math.ceil(total / cfg.websrv.eps);
|
||||
const act_page = Math.min(pages, page || 1);
|
||||
const offset = Math.max(0, (act_page - 1) * cfg.websrv.eps);
|
||||
|
||||
let rows;
|
||||
|
||||
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
|
||||
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 ? 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}
|
||||
`;
|
||||
}
|
||||
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 = [];
|
||||
for(let i = Math.max(1, act_page - 3); i <= Math.min(act_page + 3, pages); i++)
|
||||
@ -163,7 +79,7 @@ export default {
|
||||
|
||||
const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks', path: 'p/' });
|
||||
|
||||
data = {
|
||||
return {
|
||||
success: true,
|
||||
items: rows,
|
||||
pagination: {
|
||||
@ -177,9 +93,8 @@ export default {
|
||||
link,
|
||||
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 tag = lib.parseTag(o.tag ?? null);
|
||||
const mime = (o.mime ?? "");
|
||||
@ -197,74 +112,26 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
let items;
|
||||
|
||||
if(o.fav) {
|
||||
items = db`
|
||||
select "items".*
|
||||
from "favorites"
|
||||
left join "items" on "items".id = "favorites".item_id
|
||||
left join "user" on "user".id = "favorites".user_id
|
||||
${ 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 "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
|
||||
const items = await db`
|
||||
select distinct on (items.id)
|
||||
items.*
|
||||
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 ? 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
|
||||
`;
|
||||
}
|
||||
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 actitem = items[item];
|
||||
@ -334,71 +201,34 @@ export default {
|
||||
};
|
||||
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 tag = lib.parseTag(o.tag ?? null);
|
||||
const mime = (o.mime ?? "");
|
||||
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
|
||||
|
||||
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
|
||||
|
||||
let item;
|
||||
if(o.fav) { // dood lol
|
||||
item = db`
|
||||
select "items".*
|
||||
from "favorites"
|
||||
left join "items" on "items".id = "favorites".item_id
|
||||
left join "user" on "user".id = "favorites".user_id
|
||||
${ 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 "user".user ilike ${user}
|
||||
${ mime
|
||||
? db`and "items".mime ilike ${mime + '/%'}`
|
||||
: db``
|
||||
}
|
||||
const item = await db`
|
||||
select
|
||||
items.id
|
||||
from items
|
||||
left join tags_assign on tags_assign.item_id = items.id
|
||||
left join tags on tags.id = tags_assign.tag_id
|
||||
left join favorites on favorites.item_id = items.id
|
||||
left join "user" on "user".id = favorites.user_id
|
||||
where
|
||||
${ db.unsafe(modequery) }
|
||||
and items.active = 'true'
|
||||
${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
|
||||
${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
|
||||
${ user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
|
||||
${ mime ? db`and items.mime ilike ${smime}` : db`` }
|
||||
${ !o.session ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
|
||||
group by items.id, tags.tag
|
||||
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) {
|
||||
return {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import db from "../sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
import { exec } from "child_process";
|
||||
import { promises as fs } from "fs";
|
||||
|
||||
const auth = async (req, res, next) => {
|
||||
if(!req.session) {
|
||||
@ -130,5 +131,61 @@ export default (router, tpl) => {
|
||||
});
|
||||
});
|
||||
|
||||
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'
|
||||
`;
|
||||
|
||||
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
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { promises as fs } from "fs";
|
||||
import db from '../../sql.mjs';
|
||||
import lib from '../../lib.mjs';
|
||||
import search from '../../routeinc/search.mjs';
|
||||
@ -16,8 +17,10 @@ export default router => {
|
||||
const rows = await db`
|
||||
select *
|
||||
from "items"
|
||||
where mime ilike ${mime}
|
||||
and username ilike ${user}
|
||||
where
|
||||
mime ilike ${mime} and
|
||||
username ilike ${user} and
|
||||
active = 'true'
|
||||
order by random()
|
||||
limit 1
|
||||
`;
|
||||
@ -28,27 +31,47 @@ export default router => {
|
||||
});
|
||||
});
|
||||
|
||||
group.get(/\/p\/([0-9]+)/, async (req, res) => { // legacy
|
||||
let eps = 100;
|
||||
let id = +req.url.split[3];
|
||||
group.get(/\/items\/get/, async (req, res) => {
|
||||
let eps = 150;
|
||||
|
||||
const rows = await db`
|
||||
select *
|
||||
from "items"
|
||||
where id < ${+id}
|
||||
order by id desc
|
||||
limit ${+eps}
|
||||
`;
|
||||
|
||||
const items = {
|
||||
items: rows,
|
||||
last: rows[rows.length - 1].id
|
||||
const opt = {
|
||||
older: req.url.qs.older ?? null,
|
||||
newer: req.url.qs.newer ?? null,
|
||||
mode: +req.url.qs.mode ?? 0 // 0 sfw, 1 nsfw, 2 untagged, 3 all
|
||||
};
|
||||
|
||||
const newest = (await db`select max(id) as id from "items"`)[0].id;
|
||||
const oldest = (await db`select min(id) as id from "items"`)[0].id;
|
||||
const modequery = lib.getMode(opt.mode);
|
||||
|
||||
const rows = (await db`
|
||||
select "items".id, "items".mime, coalesce("tags_assign".tag_id, 0) as tag_id
|
||||
from "items"
|
||||
left join "tags_assign" on "tags_assign".item_id = "items".id and ("tags_assign".tag_id = 1 or "tags_assign".tag_id = 2)
|
||||
where
|
||||
${db.unsafe(modequery)} and
|
||||
active = 'true'
|
||||
${
|
||||
opt.older
|
||||
? db`and id <= ${opt.older}`
|
||||
: opt.newer
|
||||
? db`and id >= ${opt.newer}`
|
||||
: db``
|
||||
}
|
||||
order by id ${
|
||||
opt.newer
|
||||
? db`asc`
|
||||
: db`desc`
|
||||
}
|
||||
limit ${eps}
|
||||
`).sort((a, b) => b.id - a.id);
|
||||
|
||||
return res.json({
|
||||
atEnd: rows[0].id === newest,
|
||||
atStart: rows[rows.length - 1].id === oldest,
|
||||
success: true,
|
||||
items
|
||||
});
|
||||
items: rows
|
||||
}, 200);
|
||||
});
|
||||
|
||||
group.get(/\/item\/[0-9]+$/, async (req, res) => {
|
||||
@ -57,20 +80,20 @@ export default router => {
|
||||
const item = await db`
|
||||
select *
|
||||
from "items"
|
||||
where id = ${+id}
|
||||
where id = ${+id} and active = 'true'
|
||||
limit 1
|
||||
`;
|
||||
const next = await db`
|
||||
select id
|
||||
from "items"
|
||||
where id > ${+id}
|
||||
where id > ${+id} and active = 'true'
|
||||
order by id
|
||||
limit 1
|
||||
`;
|
||||
const prev = await db`
|
||||
select id
|
||||
from "items"
|
||||
where id < ${+id}
|
||||
where id < ${+id} and active = 'true'
|
||||
order by id desc
|
||||
limit 1
|
||||
`;
|
||||
@ -103,7 +126,7 @@ export default router => {
|
||||
const rows = db`
|
||||
select id, mime, size, src, stamp, userchannel, username, usernetwork
|
||||
from "items"
|
||||
where username = ${user}
|
||||
where username = ${user} and active = 'true'
|
||||
order by stamp desc
|
||||
limit ${+eps}
|
||||
`;
|
||||
@ -203,13 +226,42 @@ export default router => {
|
||||
msg: 'no postid'
|
||||
});
|
||||
}
|
||||
const postid = +req.post.postid;
|
||||
const id = +req.post.postid;
|
||||
|
||||
await db`
|
||||
delete from "items"
|
||||
where id = ${+postid}
|
||||
if(id <= 1) {
|
||||
return res.json({
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
const f0ck = await db`
|
||||
select dest, mime
|
||||
from "items"
|
||||
where
|
||||
id = ${id} and
|
||||
active = 'true'
|
||||
limit 1
|
||||
`;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
return res.json({
|
||||
success: false,
|
||||
msg: `f0ck ${id}: f0ck not found`
|
||||
});
|
||||
}
|
||||
|
||||
await db`update "items" set active = 'false' where id = ${id}`;
|
||||
|
||||
await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{});
|
||||
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
|
||||
await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
|
||||
|
||||
if(f0ck[0].mime.startsWith('audio')) {
|
||||
await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{});
|
||||
await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
});
|
||||
|
@ -16,7 +16,7 @@ export default router => {
|
||||
const itemid = (await db`
|
||||
select id
|
||||
from "items"
|
||||
where id = ${+avatar}
|
||||
where id = ${+avatar} and active = 'true'
|
||||
`)?.[0]?.id;
|
||||
|
||||
if(!itemid) {
|
||||
|
@ -30,7 +30,7 @@ export default router => {
|
||||
const postid = +req.params.postid;
|
||||
const tagname = req.post.tagname?.trim();
|
||||
|
||||
if(tagname.length >= 45) {
|
||||
if(tagname.length > 70) {
|
||||
return res.json({
|
||||
success: false,
|
||||
msg: 'tag is too long!'
|
||||
@ -131,6 +131,14 @@ export default router => {
|
||||
const postid = +req.params.postid;
|
||||
const tagname = decodeURIComponent(req.params.tagname);
|
||||
|
||||
if(tagname == 'sfw' || tagname == 'nsfw' || tagname == 'hentai' || tagname == 'audio') {
|
||||
return res.json({
|
||||
success: false,
|
||||
msg: 'blacklisted',
|
||||
tags: await lib.getTags(postid)
|
||||
});
|
||||
}
|
||||
|
||||
const tags = await lib.getTags(postid);
|
||||
|
||||
const tagid = tags.filter(t => t.tag === tagname)[0]?.id ?? null;
|
||||
|
@ -10,6 +10,55 @@ const auth = async (req, res, next) => {
|
||||
};
|
||||
|
||||
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_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
|
||||
});
|
||||
|
||||
if('items' in f0cks)
|
||||
f0cks.items = f0cks.items.slice(0, 50);
|
||||
if('items' in favs)
|
||||
favs.items = favs.items.slice(0, 50);
|
||||
|
||||
const data = {
|
||||
user: query[0],
|
||||
f0cks,
|
||||
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) => {
|
||||
const mode = req.params.itemid ? 'item' : 'index';
|
||||
const data = await (req.params.itemid ? f0cklib.getf0ck : f0cklib.getf0cks)({
|
||||
@ -19,7 +68,8 @@ export default (router, tpl) => {
|
||||
page: req.params.page,
|
||||
itemid: req.params.itemid,
|
||||
fav: req.params.mode == 'favs',
|
||||
mode: req.session.mode
|
||||
mode: req.session.mode,
|
||||
session: !!req.session
|
||||
});
|
||||
if(!data.success) {
|
||||
return res.reply({
|
||||
@ -73,7 +123,7 @@ export default (router, tpl) => {
|
||||
const list = await db`
|
||||
select
|
||||
"user".user,
|
||||
coalesce("user_options".avatar, 47319) as avatar,
|
||||
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
|
||||
|
@ -1,3 +1,4 @@
|
||||
import cfg from "../../inc/config.mjs";
|
||||
import f0cklib from "../routeinc/f0cklib.mjs";
|
||||
|
||||
export default (router, tpl) => {
|
||||
@ -5,8 +6,8 @@ export default (router, tpl) => {
|
||||
let referer = req.headers.referer ?? '';
|
||||
let opts = {};
|
||||
|
||||
if(referer.match(/f0ck\.me/)) { // parse referer
|
||||
referer = referer.split("f0ck.me")[1];
|
||||
if(referer.match(new RegExp(cfg.main.url.regex))) { // parse referer
|
||||
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+))?$/);
|
||||
if(tmp)
|
||||
opts = tmp.groups;
|
||||
@ -18,7 +19,8 @@ export default (router, tpl) => {
|
||||
mime: opts.mime,
|
||||
page: opts.page,
|
||||
fav: opts.mode == 'favs',
|
||||
mode: req.session.mode
|
||||
mode: req.session.mode,
|
||||
session: !!req.session
|
||||
});
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -17,8 +17,8 @@ export default (router, tpl) => {
|
||||
total = (await db`
|
||||
select count(*) as total
|
||||
from "items"
|
||||
where src ilike ${'%' + tag.substring(4) + '%'}
|
||||
group by "items".id, "tags".tag
|
||||
where src ilike ${'%' + tag.substring(4) + '%'} and active = 'true'
|
||||
group by "items".id
|
||||
`).length;
|
||||
}
|
||||
else {
|
||||
@ -40,8 +40,11 @@ export default (router, tpl) => {
|
||||
ret = await db`
|
||||
select *
|
||||
from "items"
|
||||
where src ilike ${'%' + tag.substring(4) + '%'}
|
||||
group by "items".id, "tags".tag
|
||||
where
|
||||
src ilike ${'%' + tag.substring(4) + '%'} and
|
||||
active = 'true'
|
||||
group by "items".id
|
||||
order by "items".id desc
|
||||
offset ${offset}
|
||||
limit ${_eps}
|
||||
`;
|
||||
@ -52,7 +55,7 @@ export default (router, tpl) => {
|
||||
from "tags"
|
||||
left join "tags_assign" on "tags_assign".tag_id = "tags".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
|
||||
offset ${offset}
|
||||
limit ${_eps}
|
||||
|
@ -1,4 +1,3 @@
|
||||
//import knex from "knex";
|
||||
import postgres from "postgres";
|
||||
import cfg from "./config.mjs";
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { promises as fs } from "fs";
|
||||
import db from "../sql.mjs";
|
||||
import { getLevel } from "../../inc/admin.mjs";
|
||||
|
||||
export default async bot => {
|
||||
|
||||
@ -7,37 +8,104 @@ export default async bot => {
|
||||
name: "delete",
|
||||
call: /^\!(del|rm) .*/i,
|
||||
active: true,
|
||||
level: 100,
|
||||
f: async e => {
|
||||
const ret = (await Promise.all(e.args.map(async id => {
|
||||
let deleted = [];
|
||||
|
||||
for(let id of e.args) {
|
||||
id = +id;
|
||||
if(id <= 0)
|
||||
return false;
|
||||
if(id <= 1)
|
||||
continue;
|
||||
|
||||
const f0ck = await db`
|
||||
select dest
|
||||
select dest, mime, username, userchannel, usernetwork
|
||||
from "items"
|
||||
where id = ${+id}
|
||||
where
|
||||
id = ${id} and
|
||||
active = 'true'
|
||||
limit 1
|
||||
`;
|
||||
if(f0ck.length === 0)
|
||||
return false;
|
||||
const level = getLevel(e.user).level;
|
||||
|
||||
if(f0ck.length === 0) {
|
||||
e.reply(`f0ck ${id}: f0ck not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(
|
||||
(f0ck[0].username !== (e.user.nick || e.user.username) ||
|
||||
f0ck[0].userchannel !== e.channel ||
|
||||
f0ck[0].usernetwork !== e.network) &&
|
||||
level < 100
|
||||
) {
|
||||
e.reply(`f0ck ${id}: insufficient permissions`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(~~(new Date() / 1e3) >= (f0ck[0].stamp + 600) && level < 100) {
|
||||
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}`).catch(_=>{});
|
||||
await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
|
||||
|
||||
await db`
|
||||
delete from "items"
|
||||
where id = ${+id}
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}))).filter(d => d);
|
||||
if(f0ck.length === 0) {
|
||||
e.reply(`f0ck ${id}: f0ck not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ret.length > 0)
|
||||
e.reply(`deleted ${ret.length}/${e.args.length} (${ret.join(",")}) f0cks`);
|
||||
else
|
||||
e.reply(`oof`);
|
||||
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}`;
|
||||
|
||||
recovered.push(id);
|
||||
}
|
||||
|
||||
e.reply(`recovered ${recovered.length}/${e.args.length} f0cks (${recovered.join(",")})`);
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ export default async bot => {
|
||||
return e.reply("no f0cks given! lol D:");
|
||||
|
||||
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}`,
|
||||
`~${lib.formatSize(rows[0].size)}`,
|
||||
rows[0].mime,
|
||||
|
@ -24,7 +24,7 @@ export default async bot => {
|
||||
if(rows.length === 0)
|
||||
return e.reply("nothing found, f0cker");
|
||||
|
||||
return e.reply(`f0ckrnd: ${cfg.main.url}/${rows[0].id} by: ${rows[0].username} (${rows[0].mime}, ~${lib.formatSize(rows[0].size)})`);
|
||||
return e.reply(`f0ckrnd: ${cfg.main.url.full}/${rows[0].id} by: ${rows[0].username} (${rows[0].mime}, ~${lib.formatSize(rows[0].size)})`);
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
@ -29,8 +29,8 @@ export default async bot => {
|
||||
f: e => {
|
||||
const links = e.message.match(regex)?.filter(link => !link.includes("f0ck.me")) || [];
|
||||
|
||||
if(e.photo)
|
||||
links.push(e.photo);
|
||||
if(e.media)
|
||||
links.push(e.media);
|
||||
|
||||
if(links.length === 0)
|
||||
return false;
|
||||
@ -51,7 +51,7 @@ export default async bot => {
|
||||
where src = ${link}
|
||||
`;
|
||||
if(q_repost.length > 0)
|
||||
return e.reply(`repost motherf0cker (link): ${cfg.main.url}/${q_repost[0].id}`);
|
||||
return e.reply(`repost motherf0cker (link): ${cfg.main.url.full}/${q_repost[0].id}`);
|
||||
|
||||
// generate uuid
|
||||
const uuid = (await db`
|
||||
@ -120,7 +120,7 @@ export default async bot => {
|
||||
where checksum = ${checksum}
|
||||
`;
|
||||
if(q_repostc.length > 0)
|
||||
return e.reply(`repost motherf0cker (checksum): ${cfg.main.url}/${q_repostc[0].id}`);
|
||||
return e.reply(`repost motherf0cker (checksum): ${cfg.main.url.full}/${q_repostc[0].id}`);
|
||||
|
||||
await fs.promises.copyFile(`./tmp/${filename}`, `./public/b/${filename}`);
|
||||
await fs.promises.unlink(`./tmp/${filename}`).catch(_=>{});
|
||||
@ -128,7 +128,7 @@ export default async bot => {
|
||||
await db`
|
||||
insert into items ${
|
||||
db({
|
||||
src: e.photo ? "" : link,
|
||||
src: e.media ? "" : link,
|
||||
dest: filename,
|
||||
mime: mime,
|
||||
size: size,
|
||||
@ -137,7 +137,7 @@ export default async bot => {
|
||||
userchannel: e.channel,
|
||||
usernetwork: e.network,
|
||||
stamp: ~~(new Date() / 1000),
|
||||
active: 1
|
||||
active: true
|
||||
}, 'src', 'dest', 'mime', 'size', 'checksum', 'username', 'userchannel', 'usernetwork', 'stamp', 'active')
|
||||
}
|
||||
`;
|
||||
@ -191,29 +191,31 @@ export default async bot => {
|
||||
speed = !Number.isFinite(speed) ? "yes" : `${speed.toFixed(2)} Mbit/s`;
|
||||
|
||||
// autotagger
|
||||
let tags = [];
|
||||
/*let tags = [];
|
||||
let score = 0;
|
||||
try {
|
||||
if(mime.startsWith('image')) {
|
||||
const res = await lib.detectNSFW(filename);
|
||||
score = res.score;
|
||||
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: itemid,
|
||||
tag_id: res.nsfw ? 2 : 1,
|
||||
user_id: 7
|
||||
tag_id: res.isNSFW ? 2 : 1,
|
||||
user_id: 1
|
||||
})
|
||||
}
|
||||
`;
|
||||
tags.push(res.nsfw ? 'nsfw' : 'sfw');
|
||||
tags.push(res.isNSFW ? 'nsfw' : 'sfw');
|
||||
|
||||
if(res.hentai >= .7) {
|
||||
await db`
|
||||
insert into "tags_assign" ${
|
||||
db({
|
||||
item_id: f.id,
|
||||
tag_id: 8, // hentai
|
||||
user_id: 7 // autotagger
|
||||
tag_id: 4, // hentai
|
||||
user_id: 1 // autotagger
|
||||
})
|
||||
}
|
||||
`;
|
||||
@ -226,11 +228,11 @@ export default async bot => {
|
||||
db([{
|
||||
item_id: itemid,
|
||||
tag_id: 1,
|
||||
user_id: 7
|
||||
user_id: 1
|
||||
}, {
|
||||
item_id: itemid,
|
||||
tag_id: 7178,
|
||||
user_id: 7
|
||||
tag_id: 3, // audio
|
||||
user_id: 1
|
||||
}])
|
||||
}
|
||||
`;
|
||||
@ -238,10 +240,13 @@ export default async bot => {
|
||||
}
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}*/
|
||||
|
||||
/*e.reply([
|
||||
`[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}` + (tags.length > 0 ? ` | tags: ${tags.join(', ')} (score: ${score.toFixed(2)})` : '')
|
||||
]);*/
|
||||
e.reply([
|
||||
`[f0cked] link: ${cfg.main.url}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}` + (tags.length > 0 ? ` | tags: ${tags.join(', ')}` : '')
|
||||
`[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}`
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -15,8 +15,8 @@ export default async bot => {
|
||||
return e.reply("lol no");
|
||||
const tags = (await lib.getTags(id)).map(t => t.tag);
|
||||
if(tags.length === 0)
|
||||
return e.reply(`item ${cfg.main.url}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url}/${id} is tagged as: ${tags.join(', ')}`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} is tagged as: ${tags.join(', ')}`);
|
||||
}
|
||||
}, {
|
||||
name: "tags add",
|
||||
@ -73,8 +73,8 @@ export default async bot => {
|
||||
|
||||
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
||||
if(ntags.length === 0)
|
||||
return e.reply(`item ${cfg.main.url}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
}
|
||||
}, {
|
||||
name: "tags remove",
|
||||
@ -126,8 +126,8 @@ export default async bot => {
|
||||
|
||||
const ntags = (await lib.getTags(id)).map(t => t.tag);
|
||||
if(ntags.length === 0)
|
||||
return e.reply(`item ${cfg.main.url}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
|
||||
return e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
@ -61,7 +61,7 @@ import flummpress from "flummpress";
|
||||
req.session = false;
|
||||
if(req.url.pathname.match(/^\/(s|b|t|ca)\//))
|
||||
return;
|
||||
req.theme = req.cookies.theme ?? 'f0ck';
|
||||
req.theme = req.cookies.theme || 'f0ck';
|
||||
|
||||
if(req.cookies.session) {
|
||||
const user = await db`
|
||||
@ -95,6 +95,8 @@ import flummpress from "flummpress";
|
||||
where id = ${+user[0].sess_id}
|
||||
`;
|
||||
|
||||
req.session.theme = req.cookies.theme;
|
||||
|
||||
// update userprofile
|
||||
await db`
|
||||
insert into "user_options" ${
|
||||
|
@ -1,7 +1,7 @@
|
||||
@include(snippets/header)
|
||||
<div class="about">
|
||||
<div>
|
||||
<a href="/48908"><img src="/s/img/loool.webp" /></a>
|
||||
<a href="//f0ck.me/48908"><img src="//f0ck.me/s/img/loool.webp" /></a>
|
||||
<p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif <3</p>
|
||||
</div>
|
||||
<h5>f0ck Contact</h5>
|
||||
@ -34,7 +34,7 @@
|
||||
<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>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>
|
||||
@ -44,6 +44,6 @@
|
||||
<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>
|
||||
<p>Logs: We do not log users accessing our website.</p>
|
||||
</div>
|
||||
@include(snippets/footer)
|
@ -1,3 +1,10 @@
|
||||
@include(snippets/header_admin)
|
||||
|
||||
<div class="container">
|
||||
<h1>Henlo, {{ session.user }}</h1>
|
||||
<p>Hier entsteht eine Internetpräsenz!</p>
|
||||
<img src="/s/img/favicon.gif" alt="f0ck bash">
|
||||
<p>@if(typeof totals !== "undefined")
|
||||
total: {{ totals.total }} | tagged: {{ totals.tagged }} | untagged: {{ totals.untagged }} | sfw: {{ totals.sfw }} | nsfw: {{ totals.nsfw }}
|
||||
@endif</p>
|
||||
</div>
|
||||
@include(snippets/footer)
|
24
views/admin/recover.html
Normal file
24
views/admin/recover.html
Normal file
@ -0,0 +1,24 @@
|
||||
@include(snippets/header_admin)
|
||||
<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>
|
||||
@include(snippets/footer)
|
@ -1,5 +1,6 @@
|
||||
@include(snippets/header_admin)
|
||||
<table style="width: 100%;">
|
||||
<table class="table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>ID</td>
|
||||
@ -10,6 +11,8 @@
|
||||
<td>last_used</td>
|
||||
<td>last_action</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@each(sessions as session)
|
||||
<tr>
|
||||
<td>{{ session.kmsi ? '⚓' : '' }}</td>
|
||||
@ -22,5 +25,6 @@
|
||||
<td>{{ session.last_action }}</td>
|
||||
</tr>
|
||||
@endeach
|
||||
</tbody>
|
||||
</table>
|
||||
@include(snippets/footer)
|
165
views/comments.html
Normal file
165
views/comments.html
Normal file
@ -0,0 +1,165 @@
|
||||
<div class="sidebar">
|
||||
<div class="header_sidebar">
|
||||
<span>Comments</span>
|
||||
</div>
|
||||
<div class="commentbox">
|
||||
<textarea name="comments" id="comments1" cols="30" rows="5" placeholder="Write a shitpost?"></textarea>
|
||||
<button class="commentbutton">Shit</button>
|
||||
</div>
|
||||
<div class="commentholder">
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<div class="comment_userimg">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar_comments" />
|
||||
</div>
|
||||
<div class="comment_username">
|
||||
{{ session.user }}
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
<p>Lorem Ipsum Lorem Ipsum <br><img src="https://f0ck.me/b/63144b2b.webp" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,8 +1,18 @@
|
||||
@include(snippets/header)
|
||||
<div class="container">
|
||||
<div class="_error_wrapper">
|
||||
<div class="err">
|
||||
<span>{{ message }}</span>
|
||||
<img src="https://f0ck.me/s/img/favicon.gif" alt="f0ck?!">
|
||||
<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>
|
||||
@include(snippets/footer)
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="index-container">
|
||||
@if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
|
||||
@if(tmp.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 id="posts">
|
||||
<div class="posts">
|
||||
@each(items as item)
|
||||
<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>
|
||||
@endeach
|
||||
|
@ -1,5 +1,20 @@
|
||||
<div class="wrapper">
|
||||
@include(snippets/header)
|
||||
<div class="container">
|
||||
<div class="_204863">
|
||||
<div class="imageDoor" style="--hover-image: url('/t/{{ item.id }}.webp');">
|
||||
<img src="/t/{{ item.id }}.webp" alt="" />
|
||||
</div>
|
||||
<div class="gapLeft">
|
||||
<span class="populateME"><b>f0ck</b> - {{ item.id }}</span>
|
||||
</div>
|
||||
@if(session)
|
||||
<div class="gapRight">
|
||||
<svg class="iconset" id="a_favo"><use href="/s/img/iconset.svg#heart_{{ Object.values(item.favorites).filter(u => u.user == session.user)[0] ? 'solid' : 'regular' }}"></use></svg>
|
||||
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="next-post">
|
||||
@if(pagination.prev)
|
||||
@ -45,11 +60,9 @@
|
||||
</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>
|
||||
<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>)
|
||||
<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>
|
||||
@endif
|
||||
</span>
|
||||
<span class="badge badge-dark">{{ user.network }} / {{ user.channel }}</span>
|
||||
@ -84,5 +97,10 @@
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
<div class="random">
|
||||
<a href="/random"></a>
|
||||
</div>
|
||||
</div>
|
||||
@include(comments)
|
||||
@include(snippets/footer)
|
||||
</div>
|
@ -1,11 +1,12 @@
|
||||
@include(snippets/header)
|
||||
<div class="f0ckgle">
|
||||
<h1 style="text-align: center">f0ckgle</h1>
|
||||
<form action="/search" class="admin-search">
|
||||
<input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit"><b>f0ck</b></button>
|
||||
</form>
|
||||
<div class="results">
|
||||
@if(result)
|
||||
<h1>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h1>
|
||||
<h2>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h2>
|
||||
<table style="width: 100%" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -21,15 +22,16 @@
|
||||
@each(result as line)
|
||||
<tr>
|
||||
<td style="width: 128px;"><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank"><img src="/t/{{ line.id }}.webp" /></a></td>
|
||||
<td><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank">{{ line.id }}</a></td>
|
||||
<td><a href="/tag/{!! line.tag !!}">{!! line.tag !!}</a></td>
|
||||
<td>{{ line.mime }}</td>
|
||||
<td><a href="/user/{!! line.username !!}/f0cks/{{ line.id }}">{!! line.username !!}</a></td>
|
||||
<td>{{ line.score.toFixed(2) }}</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>
|
||||
@include(snippets/footer)
|
@ -1,6 +1,6 @@
|
||||
@include(snippets/header)
|
||||
<h1>Settings</h1>
|
||||
@if(session.avatar)<a href="//f0ck.me/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif
|
||||
@if(session.avatar)<a href="/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif
|
||||
<h2>Account</h2>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
|
@ -1,4 +1,5 @@
|
||||
@if(session)<!--<div style="position: fixed; bottom: 0; z-index: 998; background-color: var(--bg); width: 100%; height: 29px; border-top: 1px solid var(--accent)">{{ JSON.stringify(session) }}</div>-->@endif
|
||||
</div>
|
||||
<script async src="/s/js/theme.js?v=@mtime(/public/s/js/theme.js)"></script>
|
||||
<script src="/s/js/v0ck.js?v=@mtime(/public/s/js/v0ck.js)"></script>
|
||||
<script src="/s/js/f0ck.js?v=@mtime(/public/s/js/f0ck.js)"></script>
|
||||
|
@ -12,3 +12,4 @@
|
||||
</head>
|
||||
<body>
|
||||
@include(snippets/navbar)
|
||||
<div id="main">
|
@ -16,3 +16,4 @@
|
||||
</head>
|
||||
<body>
|
||||
@include(snippets/navbar_admin)
|
||||
<div id="main">
|
@ -1,11 +1,11 @@
|
||||
@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">
|
||||
@if(session)
|
||||
<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" /> {{ session.user }}
|
||||
<a class="nav-link user" href="#" content="{{ session.user }}" data-toggle="dropdown">
|
||||
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/admin">adminpanel</a></li>
|
||||
@ -17,22 +17,15 @@
|
||||
<li><a href="/ranking">Ranking</a></li>
|
||||
<li><a href="/logout">logout</a></li>
|
||||
</ul>
|
||||
@else
|
||||
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/login">login</a></li>
|
||||
</ul>
|
||||
@endif
|
||||
</li>
|
||||
<li class="nav-item dropdown" id="themes">
|
||||
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Theme</a>
|
||||
<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>
|
||||
<span class="placeholder"> </span>
|
||||
<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">
|
||||
@ -42,7 +35,6 @@
|
||||
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@if(session)
|
||||
<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">
|
||||
@ -51,7 +43,6 @@
|
||||
@endfor
|
||||
</ul>
|
||||
</li>
|
||||
@endif
|
||||
<li class="nav-item">
|
||||
<a id="random" class="nav-link" href="/random">
|
||||
<span class="nav-link-identifier">Random</span>
|
||||
@ -81,3 +72,62 @@
|
||||
</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
|
@ -20,18 +20,14 @@
|
||||
<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 class="nav-item">
|
||||
<a class="nav-link" href="/admin/recover">
|
||||
<span class="nav-link-identifier">Recover</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/logout">Logout</a>
|
||||
</li>
|
||||
@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>
|
28
views/user.html
Normal file
28
views/user.html
Normal file
@ -0,0 +1,28 @@
|
||||
@include(snippets/header)
|
||||
<h1>{{ user.user }}</h1>
|
||||
|
||||
<h2>f0cks:</h2>
|
||||
@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>
|
||||
<a href="{{ f0cks.link.main }}">show all f0cks</a>
|
||||
@else
|
||||
no f0cks given
|
||||
@endif
|
||||
|
||||
<h2>favs:</h2>
|
||||
@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>
|
||||
<a href="{{ favs.link.main }}">show all favs</a>
|
||||
@else
|
||||
no favorites
|
||||
@endif
|
||||
|
||||
@include(snippets/footer)
|
Reference in New Issue
Block a user