211 Commits

Author SHA1 Message Date
d951a072ee dev progress 2022-10-28 04:08:35 +02:00
a64a4da8cd comment mockup 2022-10-27 22:19:27 +02:00
ddaaaee502 css changes 2022-10-27 17:58:30 +02:00
cc5ddf27d3 :^) 2022-10-27 17:32:34 +02:00
e7f6ecd239 pagination responsiveness 2022-10-27 15:26:50 +02:00
2c9058dec7 navbar rework 2022-10-27 15:04:00 +02:00
d13eeea588 adding some more stuff for mobile navbar exception 2022-10-23 01:45:14 +02:00
79441cf69a Merge pull request 'master' (#50) from master into dev
Reviewed-on: #50
2022-10-23 01:40:03 +02:00
904a7abd86 Fixing the navbar on mobile devices 2022-10-23 01:22:19 +02:00
aa0d88424b Merge pull request 'bye bye cuckflare' (#49) from dev into master
Reviewed-on: #49
2022-10-21 19:41:05 +02:00
201f7969b4 bye bye cuckflare 2022-10-21 19:38:20 +02:00
1da992d6a9 Fixing weird display bug in qutebrowser 2022-09-17 01:43:04 +02:00
77a703a5f0 remove autotagger 2022-08-09 16:29:04 +02:00
dc14d2f78f Merge remote-tracking branch 'origin/dev' 2022-07-28 12:47:24 +02:00
5fcc337038 Merge remote-tracking branch 'origin/dev' 2022-07-28 12:43:53 +02:00
076e98a963 70 lol 2022-07-28 12:43:23 +02:00
daab816ad2 testpush lol 2022-06-07 17:12:50 +00:00
0b6b8c549a meer padding und soos 2022-05-28 08:44:27 +00:00
39ba04bcc9 meer padding und soos 2022-05-28 10:43:34 +02:00
6b98458710 Merge remote-tracking branch 'origin/dev' 2022-05-25 15:26:53 +02:00
729522369c Merge branch 'dev' of git.lat:f0ck/f0ckv2 into dev 2022-05-25 15:26:20 +02:00
70f9e6553d don't include the username when showing favs 2022-05-25 15:25:36 +02:00
2f7f8950ae Merge remote-tracking branch 'origin/dev' 2022-05-25 05:05:10 +02:00
f2ba18d186 fix spacing after tagging 2022-05-23 05:43:14 +00:00
2309c3020a Merge remote-tracking branch 'origin/dev' 2022-05-22 16:44:46 +02:00
f8cc9f1dc0 xD 2022-05-22 14:42:01 +00:00
0754cb97a1 f0ck95 fix 2022-05-22 16:31:32 +02:00
2c7a55415a very ugly recoversystem lul 2022-05-22 12:13:44 +00:00
1425361a6f Themes 2022-05-22 13:22:05 +02:00
f8c59111aa Merge remote-tracking branch 'origin/dev' 2022-05-22 11:18:57 +00:00
a9f65bb798 fix search 2022-05-22 11:18:34 +00:00
d864679775 Merge remote-tracking branch 'origin/dev' 2022-05-22 11:07:35 +00:00
e68af4dc96 f0ck95/95d fixes 2022-05-22 13:06:56 +02:00
17c029e6da - added folder deleted to gitignore
- readme extended
2022-05-22 04:15:26 +00:00
49fb4bfc56 Merge remote-tracking branch 'origin/dev' 2022-05-22 06:11:57 +02:00
c6fbe956a0 soft delete v1 2022-05-22 04:11:15 +00:00
75c4e35fdc Merge remote-tracking branch 'origin/dev' 2022-05-21 17:04:36 +02:00
d2091494a1 fix tags + slugify 2022-05-21 15:03:01 +00:00
53a0880c55 Merge remote-tracking branch 'origin/dev' 2022-05-21 16:43:27 +02:00
74431a2a29 lel 2022-05-21 16:40:59 +02:00
af4c48f351 hide tags from the public 2022-05-21 16:39:24 +02:00
d0f8cb5acb fix pagination 2022-05-21 15:16:04 +02:00
29d5b20f5b f0cklib2.0 2022-05-21 13:08:35 +00:00
86b13f5173 👀 ???? 2022-05-21 11:42:05 +02:00
5bb3fefe52 overhauling error page 2022-05-21 11:42:05 +02:00
41194ca08a 👀 ???? 2022-05-21 11:38:37 +02:00
314c476f8d overhauling error page 2022-05-21 11:34:44 +02:00
a575f52005 overhauling error page 2022-05-21 11:30:23 +02:00
2ab187c780 Merge remote-tracking branch 'origin/dev' 2022-05-21 00:29:40 +00:00
fa0496949c blub 2022-05-21 02:29:27 +02:00
4d93ef1152 Merge remote-tracking branch 'origin/dev' 2022-05-20 23:12:55 +00:00
f8dffcdfc9 blah 2022-05-21 01:12:44 +02:00
3813af2c46 Merge remote-tracking branch 'origin/dev' 2022-05-20 23:10:03 +00:00
3ab534ee8e fix weird spacig for username 2022-05-21 01:09:49 +02:00
3e7ebba6fd avatar borders f0ck95 2022-05-21 01:01:17 +02:00
757c5aa4b4 Merge remote-tracking branch 'origin/dev' 2022-05-20 22:57:54 +00:00
7cc6776773 adding wrapper, fixing avatar width in nav 2022-05-21 00:57:31 +02:00
53ea6389c7 . 2022-05-19 16:17:44 +00:00
edfdd7ebbe . 2022-05-19 18:17:06 +02:00
08698ee886 Merge pull request 'fixed navbar :)' (#47) from dev into master
Reviewed-on: #47
2022-05-19 15:31:54 +02:00
e5b9bdb721 fixed navbar :) 2022-05-19 15:31:30 +02:00
961ec2cf55 Merge pull request 'blah' (#46) from dev into master
Reviewed-on: #46
2022-05-19 14:53:00 +02:00
8bee86c002 blah 2022-05-19 14:52:36 +02:00
9aec26f4d3 Merge pull request 'f0ck95 navlink unify with pagination style' (#45) from dev into master
Reviewed-on: #45
2022-05-19 14:46:38 +02:00
e16138583e f0ck95 navlink unify with pagination style 2022-05-19 14:46:22 +02:00
10ef57b0bf Merge remote-tracking branch 'origin/dev' 2022-05-19 01:41:08 +00:00
9f4432a942 f0ck95 fixes 2022-05-19 03:40:20 +02:00
b775d55fba Merge remote-tracking branch 'origin/dev' 2022-05-19 00:20:29 +00:00
c33e57c9c0 badge word wrap for source field 2022-05-19 02:20:17 +02:00
d315ae3efa Merge remote-tracking branch 'origin/dev' 2022-05-19 00:10:50 +00:00
dc1d658c9a blah 2022-05-19 02:10:35 +02:00
d4811c3d5c Merge remote-tracking branch 'origin/dev' 2022-05-19 00:04:57 +00:00
9be8b3f982 f0ck95 changes 2022-05-19 02:04:45 +02:00
1dacafdb3a Merge remote-tracking branch 'origin/dev' 2022-05-19 00:02:39 +00:00
3bc7cdcd1c f0ck95d black navbar brand bg 2022-05-19 02:02:24 +02:00
dfea411e1b Merge remote-tracking branch 'origin/dev' 2022-05-19 00:00:52 +00:00
3a899e4911 cleaning up the css, bumping version an 2022-05-19 01:58:13 +02:00
7a96f26f84 possible sticky navbar fix for chrome 2022-05-19 01:52:15 +02:00
412a4a348a Merge remote-tracking branch 'origin/dev' 2022-05-18 20:26:52 +00:00
587bebbd6a mobile css fix 2022-05-18 20:26:25 +00:00
3aa14e4bfb hotfix xd 2022-05-18 20:26:25 +00:00
522d4903ba fix for image overlapping outside borders 2022-05-18 20:26:25 +00:00
6d795652a7 fix 0px -> 0.. again 2022-05-18 20:26:25 +00:00
9c86d34b87 fixing image zoom 2022-05-18 20:26:25 +00:00
34e93b09c5 css fix 2022-05-18 20:26:25 +00:00
48fd0e5ab0 fix indents.. as always
Signed-off-by: Flummi <git@srv.fail>
2022-05-18 20:26:25 +00:00
7df97485e6 borders morders 2022-05-18 20:26:25 +00:00
8adee68835 hover schmover 2022-05-18 20:26:25 +00:00
0bda378993 smol border important 2022-05-18 20:26:25 +00:00
b03d265a94 inset border for mystery numbers 2022-05-18 20:26:25 +00:00
d78e90ddc0 f0ck95d changing sfw badge color to teal 2022-05-18 20:26:25 +00:00
a0962d8d5c good executables have icons 2022-05-18 20:26:25 +00:00
58d8268207 removing italics 2022-05-18 20:26:25 +00:00
002b796cbc adding new variable for load indicator and footbar 2022-05-18 20:26:25 +00:00
d6561a66b8 bugfix 2022-05-18 20:26:25 +00:00
b82044555d #posts -> .posts 2022-05-18 20:26:25 +00:00
45792787ba only show admin tools when logged in 2022-05-18 20:26:25 +00:00
81f4817f03 adding new f0ck style, various css changes 2022-05-18 20:26:25 +00:00
e0618443c0 misc bugfixes 2022-05-18 20:26:25 +00:00
6eddad7e0e userprofiles test 2022-05-18 20:26:25 +00:00
6f71101b9e removed hardcoded link 2022-05-18 20:26:25 +00:00
a33d1606ae ability to color iconset from css 2022-05-18 20:26:25 +00:00
d7abc4e797 misc bugfixes 2022-05-18 20:26:25 +00:00
5bcd797e6c mobile css fix 2022-05-18 22:19:05 +02:00
530d0938c6 hotfix xd 2022-05-18 21:54:19 +02:00
7f5ef839cc fix for image overlapping outside borders 2022-05-18 21:50:01 +02:00
a106a31c47 fix 0px -> 0.. again 2022-05-18 19:08:00 +00:00
14c6816c47 fixing image zoom 2022-05-18 21:05:13 +02:00
643cf0de76 css fix 2022-05-18 20:37:48 +02:00
c18c6b9283 fix indents.. as always
Signed-off-by: Flummi <git@srv.fail>
2022-05-18 16:13:12 +00:00
4a7c7f8730 borders morders 2022-05-18 17:58:16 +02:00
173321f5c8 hover schmover 2022-05-18 15:55:34 +00:00
6f12cbca32 smol border important 2022-05-18 17:27:30 +02:00
a460b974a8 inset border for mystery numbers 2022-05-18 17:20:07 +02:00
09d647cc48 f0ck95d changing sfw badge color to teal 2022-05-18 17:14:43 +02:00
d56c143e80 good executables have icons 2022-05-18 17:07:24 +02:00
b9fbcb4187 removing italics 2022-05-18 16:51:53 +02:00
1f761ec62b adding new variable for load indicator and footbar 2022-05-18 16:45:14 +02:00
c0f8f6c536 bugfix 2022-05-18 12:12:25 +00:00
881d44158e #posts -> .posts 2022-05-18 12:07:07 +00:00
e159776e9b only show admin tools when logged in 2022-05-18 13:57:48 +02:00
44ca53a050 adding new f0ck style, various css changes 2022-05-18 13:37:26 +02:00
97ef147160 misc bugfixes 2022-05-18 03:43:17 +00:00
f57a8b4320 userprofiles test 2022-05-17 14:57:32 +00:00
ba821b81fa removed hardcoded link 2022-05-17 10:59:17 +00:00
29acafe918 ability to color iconset from css 2022-05-17 10:03:11 +00:00
dc96a2578a misc bugfixes 2022-05-17 09:40:43 +00:00
9fc920c1d2 Merge pull request 'making it more 95' (#44) from dev into master
Reviewed-on: #44
2022-05-17 11:19:01 +02:00
eb16487d73 making it more 95 2022-05-17 11:18:49 +02:00
501f68c481 Merge pull request 'webkitscrollbar and darker badge color for f0ck95d' (#43) from dev into master
Reviewed-on: #43
2022-05-17 11:08:50 +02:00
4c94cac3ef webkitscrollbar and darker badge color for f0ck95d 2022-05-17 11:08:33 +02:00
5be4670ba3 Merge pull request 'hopefully last one for tonigh 👀' (#42) from dev into master
Reviewed-on: #42
2022-05-17 02:55:38 +02:00
50b983536c hopefully last one for tonigh 👀 2022-05-17 02:55:22 +02:00
f0ee736e40 Merge pull request 'small color fix for f0ck95d' (#41) from dev into master
Reviewed-on: #41
2022-05-17 02:32:55 +02:00
bb6f4dc2b8 small color fix for f0ck95d 2022-05-17 02:32:42 +02:00
b960acb0ed Merge pull request '👀' (#40) from dev into master
Reviewed-on: #40
2022-05-17 02:28:34 +02:00
7621f6ca48 👀 2022-05-17 02:28:19 +02:00
b8e69af878 Merge pull request '👀 f0ck95/f0ck95d fixes 👀' (#39) from dev into master
Reviewed-on: #39
2022-05-17 02:23:27 +02:00
be64a42d68 👀 f0ck95/f0ck95d fixes 👀 2022-05-17 02:22:45 +02:00
3d35367302 Merge pull request 'adding f0ck95 dark' (#38) from dev into master
Reviewed-on: #38
2022-05-17 00:06:30 +02:00
80c7d2f3d7 adding f0ck95 dark 2022-05-17 00:05:37 +02:00
8dfee166b4 Merge pull request 'autism awareness for the placeholder' (#37) from dev into master
Reviewed-on: #37
2022-05-16 21:32:33 +02:00
7a007c95e7 autism awareness for the placeholder 2022-05-16 21:32:11 +02:00
fd685a6c16 Merge pull request '👀 hopefully last hotfix for f0ck95' (#36) from dev into master
Reviewed-on: #36
2022-05-16 21:24:12 +02:00
75fa6e23f5 👀 hopefully last hotfix for f0ck95 2022-05-16 21:23:55 +02:00
bafb0916e2 Merge pull request 'fixing all themes and removing opensans reference' (#35) from dev into master
Reviewed-on: #35
2022-05-16 20:35:38 +02:00
fda30ebdee fixing all themes and removing opensans reference 2022-05-16 20:35:13 +02:00
eb5d4ccfbe Merge pull request 'hotfix f0ck95' (#34) from dev into master
Reviewed-on: #34
2022-05-16 20:15:55 +02:00
ce72c6f265 hotfix f0ck95 2022-05-16 20:15:40 +02:00
cba529deb4 Merge pull request 'adding f0ck95 theme' (#33) from dev into master
Reviewed-on: #33
2022-05-16 20:08:15 +02:00
8dd58553e2 adding f0ck95 theme 2022-05-16 20:07:40 +02:00
8f4ea66cd9 Merge pull request 'hotfix for mobile dropdown alignment and display' (#32) from dev into master
Reviewed-on: #32
2022-05-16 16:19:21 +02:00
00837600bb hotfix for mobile dropdown alignment and display 2022-05-16 16:19:04 +02:00
b76f6439fa Merge pull request 'hotfix for navbar dropdown fullwidth' (#31) from dev into master
Reviewed-on: #31
2022-05-16 16:10:56 +02:00
17946742ec hotfix for navbar dropdown fullwidth 2022-05-16 16:10:33 +02:00
ae0d26aaa7 Merge pull request 'fix for fav tooltips and navigation spacing' (#30) from dev into master
Reviewed-on: #30
2022-05-16 16:03:07 +02:00
9d7b701e96 fix for fav tooltips and navigation spacing 2022-05-16 16:01:23 +02:00
77c00de69a Merge remote-tracking branch 'origin/dev'
new file:   debug/adduser.mjs
modified:   f0ck.sql
	modified:   package.json
2022-05-16 12:58:52 +00:00
a7e2cb0dd9 psql-db: extension unaccent 2022-05-16 12:57:13 +00:00
9100f64d81 adduser 2022-05-16 11:41:09 +00:00
1222837d4d Merge remote-tracking branch 'origin/dev' 2022-05-16 12:42:03 +02:00
2447d62dac adding more in-depth python instructions 2022-05-16 12:39:42 +02:00
ac3647aa7f Merge remote-tracking branch 'origin/dev' 2022-05-14 19:23:02 +02:00
429b5003d5 adminpanel layout fix 2022-05-14 19:22:35 +02:00
da5d7285a4 Merge remote-tracking branch 'origin/dev' 2022-05-14 14:41:11 +02:00
1bc7085e68 autotagger is now userid: 1 2022-05-14 14:40:58 +02:00
f22b8b4e0d Merge remote-tracking branch 'origin/dev' 2022-05-14 13:47:43 +02:00
617385b4b6 test -> f0ck xD 2022-05-14 13:47:33 +02:00
1484c646e8 Merge remote-tracking branch 'origin/dev' 2022-05-14 13:33:20 +02:00
dd95c52de6 ... 2022-05-14 13:32:28 +02:00
f63556e713 readme.md 2022-05-14 13:30:12 +02:00
16d85ae5ad Merge remote-tracking branch 'origin/dev' 2022-05-14 13:00:04 +02:00
2ae986ccf1 enter message here 2022-05-14 12:59:35 +02:00
4f5d54ee19 Merge remote-tracking branch 'origin/dev' 2022-05-12 16:18:35 +02:00
a97ed32c9f ability to delete own f0cks 2022-05-12 16:18:18 +02:00
94c237561c Merge remote-tracking branch 'origin/dev' 2022-05-12 12:23:51 +02:00
7e9599adce fixed navbar 2022-05-12 12:23:30 +02:00
9dfffbb8bf Merge remote-tracking branch 'origin/dev' 2022-05-09 16:43:49 +02:00
b0acc1731d oopsie 2022-05-09 16:43:39 +02:00
647991b5e9 Merge remote-tracking branch 'origin/dev' 2022-05-09 16:40:52 +02:00
78b847ddae more nsfw etzala 2022-05-09 16:40:35 +02:00
1a3af07309 Merge remote-tracking branch 'origin/dev' 2022-05-09 13:00:29 +02:00
8f5680e50f keybinds - prevent triggering when holding shift, alt, ctrl or meta key 2022-05-09 12:59:33 +02:00
add3c7a648 Merge remote-tracking branch 'origin/dev' 2022-05-08 05:51:43 +02:00
e7ee063021 psql search_path 2022-05-08 05:51:09 +02:00
d57908f82e Merge pull request 'adding search description for mobile view and small admin page restructuring' (#29) from dev into master
Reviewed-on: #29
2022-05-08 01:16:48 +02:00
659adca258 making the admin page a bit better 2022-05-08 01:14:43 +02:00
46d8bd45a8 adding search description for mobile view 2022-05-08 01:14:13 +02:00
3731d1d785 adding search description for mobile view 2022-05-08 01:13:23 +02:00
872abd7f73 cfg: change psql schema 2022-05-07 19:08:55 +02:00
2a94a84c44 Merge remote-tracking branch 'origin/dev' 2022-05-07 17:49:51 +02:00
1787a69143 hopefully foolproof 2022-05-07 17:40:39 +02:00
ac96827ad1 b/.empty 2022-05-07 17:39:13 +02:00
2e0881b109 undo 2022-05-07 17:35:43 +02:00
315770bf30 lul revert 2022-05-07 16:17:38 +02:00
6de804561f lul 2022-05-07 16:15:32 +02:00
f4815d95e5 Merge remote-tracking branch 'origin/dev' 2022-05-07 14:09:41 +02:00
7e1129a567 xd 2022-05-07 14:09:21 +02:00
1e01a521e4 Merge remote-tracking branch 'origin/dev' 2022-05-07 12:38:23 +02:00
9541d9e0b8 update cuffeo 2022-05-07 12:36:41 +02:00
fb744fe341 Merge pull request 'fuuuuuuuuuck' (#28) from dev into master
Reviewed-on: #28
2022-05-07 10:45:51 +02:00
a6b881a196 fuuuuuuuuuck 2022-05-07 10:45:32 +02:00
1be456edb4 Merge pull request 'e.photo -> e.media' (#27) from dev into master
Reviewed-on: #27
2022-05-07 10:40:50 +02:00
1066e8772b e.photo -> e.media 2022-05-07 10:40:37 +02:00
4ebcbdd886 Merge pull request ':_D' (#26) from dev into master
Reviewed-on: #26
2022-05-07 10:37:13 +02:00
73577441d1 :_D 2022-05-07 10:36:58 +02:00
1a8aa81a0b Merge pull request 'dev' (#25) from dev into master
Reviewed-on: #25
2022-05-07 10:30:38 +02:00
86409c9d98 telegram magic 2022-05-07 10:29:53 +02:00
22b171c858 emphasis on active id in pagination 2022-05-07 10:21:32 +02:00
155d496592 fix random & search 2022-05-06 17:29:55 +02:00
82702d786a Merge pull request 'fix random & search' (#24) from dev into master
Reviewed-on: #24
2022-05-06 17:29:53 +02:00
48 changed files with 3656 additions and 1775 deletions

9
.gitignore vendored
View File

@ -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
View 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
View 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
View 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();

View File

@ -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
View 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
View File

@ -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",

View File

@ -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
View 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

View File

@ -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

View File

@ -101,6 +101,7 @@ const flash = ({ type, msg }) => {
delbutton.href = "#";
delbutton.addEventListener("click", deleteEvent);
span.insertAdjacentElement("beforeend", a);
span.innerHTML += '&nbsp;';
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}`;

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -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 = "";

View File

@ -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;
}
};

View File

@ -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;
const total = (await db`
select distinct on (items.id)
count(items.id) as total
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`` }
${ !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;
if(tag) {
if(tag.match(/sfw/) || tag.length <= 2)
return {
success: false,
message: "nope."
};
total = await db`
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)
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
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 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}
`;
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
`;
}
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 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
`;
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``
}
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;
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
`;
if(item.length === 0) {
return {

View File

@ -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;
};

View File

@ -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}
`;
@ -152,13 +175,13 @@ export default router => {
}
const q = (await db`
update "tags" set ${
db({
tag: newtag
}, 'tag')
}
where tag = ${tagname}
returning *
update "tags" set ${
db({
tag: newtag
}, 'tag')
}
where tag = ${tagname}
returning *
`)?.[0];
return res.json(q, tagname === newtag ? 200 : 201); // created (modified)
@ -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
});

View File

@ -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) {

View File

@ -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;

View File

@ -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({
@ -71,15 +121,15 @@ export default (router, tpl) => {
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
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();

View File

@ -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;
};

View File

@ -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}

View File

@ -1,4 +1,3 @@
//import knex from "knex";
import postgres from "postgres";
import cfg from "./config.mjs";

View File

@ -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(",")})`);
}
}]
};

View File

@ -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,

View File

@ -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)})`);
}
}];
};

View File

@ -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}`
]);
});

View File

@ -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(', ')}`);
}
}]
};

View File

@ -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" ${

View File

@ -1,49 +1,49 @@
@include(snippets/header)
<div class="about">
<div>
<a href="/48908"><img src="/s/img/loool.webp" /></a>
<div class="about">
<div>
<a href="//f0ck.me/48908"><img src="//f0ck.me/s/img/loool.webp" /></a>
<p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif &lt;3</p>
</div>
<h5>f0ck Contact</h5>
<p>Whatever it is, we might have a answer, even though it might not be the one you were looking for: <a href="mailto:admin@f0ck.me">admin@f0ck.me</a></p>
<h5>About f0ck</h5>
<p>f0ck is your friendly IRC shitposting bot, it's built for catching urls that are passed to it and displays the content of passed urls on a simple and accessible web gallery reachable at <a href="/">f0ck.me</a></p>
<h5>WTF is a f0ck?</h5>
<p>A f0ck is basically giving a fuck about some internet bullshit, like stupid images, videos and so on, but also for great things like good music taste and shit, it's basically "a f0ck was given" and f0ck and it's users gave a lot of f0cks over the past years, it's not hard to finally start giving a damn f0ck about something, just f0ck it dood!</p>
<h5>Where to f0ck?</h5>
<h4>#f0ck on n0xy.net</h4>
<p>You can invite f0ck to your channel on the following supported networks by simply typing<br><code>/invite f0ck</code></p>
<ul>
</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>
</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>
</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>
</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>
@include(snippets/footer)

View File

@ -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
View 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)

View File

@ -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 ? '&#9875;' : '' }}</td>
@ -22,5 +25,6 @@
<td>{{ session.last_action }}</td>
</tr>
@endeach
</tbody>
</table>
@include(snippets/footer)

165
views/comments.html Normal file
View 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>

View File

@ -1,8 +1,18 @@
@include(snippets/header)
<div class="container">
<div class="err">
<span>{{ message }}</span>
<img src="https://f0ck.me/s/img/favicon.gif" alt="f0ck?!">
<div class="_error_wrapper">
<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>
@include(snippets/footer)

View File

@ -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

View File

@ -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>

View File

@ -1,35 +1,37 @@
@include(snippets/header)
<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>
<table style="width: 100%" class="table">
<thead>
<tr>
<th>Thumbnail</th>
<th>ID</th>
<th>Tag</th>
<th>Mime</th>
<th>Username</th>
<th>Score</th>
</tr>
</thead>
<tbody>
@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>
</tr>
@endeach
</tbody>
</table>
@endif
<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)
<h2>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h2>
<table style="width: 100%" class="table">
<thead>
<tr>
<th>Thumbnail</th>
<th>ID</th>
<th>Tag</th>
<th>Mime</th>
<th>Username</th>
<th>Score</th>
</tr>
</thead>
<tbody>
@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><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)

View File

@ -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>

View File

@ -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>

View File

@ -12,3 +12,4 @@
</head>
<body>
@include(snippets/navbar)
<div id="main">

View File

@ -16,3 +16,4 @@
</head>
<body>
@include(snippets/navbar_admin)
<div id="main">

View File

@ -1,38 +1,97 @@
@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 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>
<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)&nbsp;&#9660;@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">&laquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">&lsaquo;</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">&rsaquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">&raquo;</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">
<ul class="navbar-nav">
<div class="navigation-links-guest">
<ul class="navbar-nav-guests">
<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" />&nbsp;{{ session.user }}
</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>
@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">&nbsp;</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)&nbsp;&#9660;@endif</a>
<ul class="dropdown-menu">
@ -42,16 +101,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">
@for(let i = 0; i < modes.length; i++)
<li><a class="dropdown-item" href="/mode/{{ i }}">{{ modes[i] }}</a></li>
@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 +130,4 @@
</div>
</div>
</nav>
@endif

View File

@ -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:&nbsp;{{ totals.total }}&nbsp;|&nbsp;tagged:&nbsp;{{ totals.tagged }}&nbsp;|&nbsp;untagged:&nbsp;{{ totals.untagged }}&nbsp;|&nbsp;sfw:&nbsp;{{ totals.sfw }}&nbsp;|&nbsp;nsfw:&nbsp;{{ 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 }}&nbsp;&nbsp;<a href="/logout">Logout</a>
</div>
</div>
</div>
</nav>

28
views/user.html Normal file
View 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)