Merge remote-tracking branch 'origin/master' into dev
All checks were successful
fetch npm modules / f0ck the f0cker (push) Successful in 28s

This commit is contained in:
Kibi Kelburton 2024-06-28 21:27:45 +02:00
commit 7098c335bc
23 changed files with 164 additions and 215 deletions

View File

@ -131,7 +131,7 @@ CREATE TABLE public."user" (
login character varying(255) NOT NULL, login character varying(255) NOT NULL,
"user" character varying(255) NOT NULL, "user" character varying(255) NOT NULL,
password character varying(167) NOT NULL, password character varying(167) NOT NULL,
level integer NOT NULL admin boolean NOT NULL
); );
ALTER TABLE public."user" OWNER TO f0ck; ALTER TABLE public."user" OWNER TO f0ck;

View File

@ -333,6 +333,7 @@ const flash = ({ type, msg }) => {
document.querySelector("a#a_toggle").addEventListener("click", toggleEvent); document.querySelector("a#a_toggle").addEventListener("click", toggleEvent);
[...document.querySelectorAll("#tags > .badge > a:first-child")].map(t => t.addEventListener("click", editTagEvent)); [...document.querySelectorAll("#tags > .badge > a:first-child")].map(t => t.addEventListener("click", editTagEvent));
[...document.querySelectorAll("#tags > .badge > a:last-child")].map(t => t.addEventListener("click", deleteEvent)); [...document.querySelectorAll("#tags > .badge > a:last-child")].map(t => t.addEventListener("click", deleteEvent));
if(document.querySelector("svg#a_delete"))
document.querySelector("svg#a_delete").addEventListener("click", deleteButtonEvent); document.querySelector("svg#a_delete").addEventListener("click", deleteButtonEvent);
document.querySelector("svg#a_favo").addEventListener("click", toggleFavEvent); document.querySelector("svg#a_favo").addEventListener("click", toggleFavEvent);

View File

@ -1,4 +1,4 @@
import _config from "../../config.json" assert { type: "json" }; import _config from "../../config.json" with { type: "json" };
let config = JSON.parse(JSON.stringify(_config)); let config = JSON.parse(JSON.stringify(_config));

View File

@ -134,15 +134,6 @@ export default new class {
const derivedKey = await scrypt(str, salt, 64); const derivedKey = await scrypt(str, salt, 64);
return crypto.timingSafeEqual(keyBuffer, derivedKey); return crypto.timingSafeEqual(keyBuffer, derivedKey);
}; };
async auth(req, res, next) {
if(!req.session) {
return res.reply({
code: 401,
body: "401 - Unauthorized"
});
}
return next();
};
async getTags(itemid) { async getTags(itemid) {
const tags = await db` const tags = await db`
select "tags".id, "tags".tag, "tags".normalized, "user".user select "tags".id, "tags".tag, "tags".normalized, "user".user
@ -217,6 +208,27 @@ export default new class {
TABLE_NAME='user_options' and TABLE_NAME='user_options' and
COLUMN_NAME = 'avatar' COLUMN_NAME = 'avatar'
`)[0].avatar; `)[0].avatar;
};
// meddlware
async auth(req, res, next) {
if(!req.session || !req.session.admin) {
return res.reply({
code: 401,
body: "401 - Unauthorized"
});
} }
return next();
};
async loggedin(req, res, next) {
if(!req.session) {
return res.reply({
code: 401,
body: "401 - Unauthorized"
});
}
return next();
};
}; };

View File

@ -7,12 +7,13 @@ import url from "url";
const globalfilter = cfg.nsfp.map(n => `tag_id = ${n}`).join(' or '); const globalfilter = cfg.nsfp.map(n => `tag_id = ${n}`).join(' or ');
export default { export default {
getf0cks: async (o = { user, tag, mime, page, mode, fav, session }) => { getf0cks: async (o = { user, tag, mime, page, mode, fav, session, limit }) => {
const user = o.user ? decodeURI(o.user) : null; const user = o.user ? decodeURI(o.user) : null;
const tag = lib.parseTag(o.tag ?? null); const tag = lib.parseTag(o.tag ?? null);
const mime = o.mime ?? null; const mime = o.mime ?? null;
const page = +(o.page ?? 1); const page = +(o.page ?? 1);
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%"; const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
const eps = o.limit ?? cfg.websrv.eps;
const tmp = { user, tag, mime, smime, page, mode: o.mode }; const tmp = { user, tag, mime, smime, page, mode: o.mode };
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0); const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
@ -70,7 +71,7 @@ export default {
group by items.id, tags.tag, ta.tag_id group by items.id, tags.tag, ta.tag_id
order by items.id desc order by items.id desc
offset ${offset} offset ${offset}
limit ${cfg.websrv.eps} limit ${eps}
`; `;
const cheat = []; const cheat = [];

View File

@ -3,18 +3,7 @@ import lib from "../lib.mjs";
import { exec } from "child_process"; import { exec } from "child_process";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
const auth = async (req, res, next) => {
if(!req.session) {
return res.reply({
code: 401,
body: "401 - Unauthorized"
});
}
return next();
};
export default (router, tpl) => { export default (router, tpl) => {
router.get(/^\/login(\/)?$/, async (req, res) => { router.get(/^\/login(\/)?$/, async (req, res) => {
if(req.cookies.session) { if(req.cookies.session) {
return res.reply({ return res.reply({
@ -72,7 +61,7 @@ export default (router, tpl) => {
}).end(); }).end();
}); });
router.get(/^\/logout$/, auth, async (req, res) => { router.get(/^\/logout$/, lib.loggedin, async (req, res) => {
const usersession = await db` const usersession = await db`
select * select *
from "user_sessions" from "user_sessions"
@ -103,7 +92,7 @@ export default (router, tpl) => {
}); });
}); });
router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage router.get(/^\/admin(\/)?$/, lib.auth, async (req, res) => { // frontpage
res.reply({ res.reply({
body: tpl.render("admin", { body: tpl.render("admin", {
@ -114,7 +103,7 @@ export default (router, tpl) => {
}); });
}); });
router.get(/^\/admin\/sessions(\/)?$/, auth, async (req, res) => { router.get(/^\/admin\/sessions(\/)?$/, lib.auth, async (req, res) => {
const rows = await db` const rows = await db`
select "user_sessions".*, "user".user select "user_sessions".*, "user".user
from "user_sessions" from "user_sessions"
@ -132,7 +121,7 @@ export default (router, tpl) => {
}); });
}); });
router.get(/^\/admin\/log(\/)?$/, auth, async (req, res) => { router.get(/^\/admin\/log(\/)?$/, lib.auth, async (req, res) => {
exec("journalctl -qeu f0ck --no-pager", (err, stdout) => { exec("journalctl -qeu f0ck --no-pager", (err, stdout) => {
res.reply({ res.reply({
body: tpl.render("admin/log", { body: tpl.render("admin/log", {
@ -143,7 +132,7 @@ export default (router, tpl) => {
}); });
}); });
router.get(/^\/admin\/recover\/?/, auth, async (req, res) => { router.get(/^\/admin\/recover\/?/, lib.auth, async (req, res) => {
if(req.url.qs?.id) { if(req.url.qs?.id) {
const id = +req.url.qs.id; const id = +req.url.qs.id;
const f0ck = await db` const f0ck = await db`

View File

@ -139,7 +139,7 @@ export default router => {
// tags lol // tags lol
group.put(/\/admin\/tags\/(?<tagname>.*)/, lib.auth, async (req, res) => { group.put(/\/admin\/tags\/(?<tagname>.*)/, lib.loggedin, async (req, res) => {
if(!req.params.tagname || !req.post.newtag) { if(!req.params.tagname || !req.post.newtag) {
return res.json({ return res.json({
success: false, success: false,
@ -187,7 +187,7 @@ export default router => {
return res.json(q, tagname === newtag ? 200 : 201); // created (modified) return res.json(q, tagname === newtag ? 200 : 201); // created (modified)
}); });
group.get(/\/admin\/tags\/suggest$/, lib.auth, async (req, res) => { group.get(/\/admin\/tags\/suggest$/, lib.loggedin, async (req, res) => {
const reply = { const reply = {
success: false, success: false,
suggestions: {} suggestions: {}
@ -267,7 +267,7 @@ export default router => {
}); });
}); });
group.post(/\/admin\/togglefav$/, lib.auth, async (req, res) => { group.post(/\/admin\/togglefav$/, lib.loggedin, async (req, res) => {
const postid = +req.post.postid; const postid = +req.post.postid;
let favs = await db` let favs = await db`

View File

@ -3,7 +3,7 @@ import lib from '../../lib.mjs';
export default router => { export default router => {
router.group(/^\/api\/v2\/settings/, group => { router.group(/^\/api\/v2\/settings/, group => {
group.put(/\/setAvatar/, lib.auth, async (req, res) => { group.put(/\/setAvatar/, lib.loggedin, async (req, res) => {
if(!req.post.avatar) { if(!req.post.avatar) {
return res.json({ return res.json({
msg: 'no avatar provided', msg: 'no avatar provided',

View File

@ -3,7 +3,7 @@ import lib from '../../lib.mjs';
export default router => { export default router => {
router.group(/^\/api\/v2\/admin\/(?<postid>\d+)\/tags/, group => { router.group(/^\/api\/v2\/admin\/(?<postid>\d+)\/tags/, group => {
group.get(/$/, lib.auth, async (req, res) => { group.get(/$/, lib.loggedin, async (req, res) => {
// get tags // get tags
if(!req.params.postid) { if(!req.params.postid) {
return res.json({ return res.json({
@ -18,7 +18,7 @@ export default router => {
}); });
}); });
group.post(/$/, lib.auth, async (req, res) => { group.post(/$/, lib.loggedin, async (req, res) => {
// assign and/or create tag // assign and/or create tag
if(!req.params.postid || !req.post.tagname) { if(!req.params.postid || !req.post.tagname) {
return res.json({ return res.json({
@ -80,7 +80,7 @@ export default router => {
}); });
}); });
group.put(/\/toggle$/, lib.auth, async (req, res) => { group.put(/\/toggle$/, lib.loggedin, async (req, res) => {
// xD // xD
if(!req.params.postid) { if(!req.params.postid) {
return res.json({ return res.json({

View File

@ -14,7 +14,7 @@ export default (router, tpl) => {
const user = decodeURIComponent(req.params.user); const user = decodeURIComponent(req.params.user);
const query = await db` const query = await db`
select "user".user, "user".created_at, user_options.* select "user".user, "user".admin, "user".created_at, user_options.*
from user_options from user_options
left join "user" on "user".id = user_options.user_id left join "user" on "user".id = user_options.user_id
where "user".user ilike ${user} where "user".user ilike ${user}
@ -31,32 +31,43 @@ export default (router, tpl) => {
}); });
} }
const f0cks = await f0cklib.getf0cks({ let f0cks, favs;
user: user,
mode: req.session.mode,
fav: false,
session: !!req.session
});
const favs = await f0cklib.getf0cks({
user: user,
mode: req.session.mode,
fav: true,
session: !!req.session
});
const count = { const count = {
f0cks: 0, f0cks: 0,
favs: 0 favs: 0
}; };
try {
f0cks = await f0cklib.getf0cks({
user: user,
mode: req.session.mode,
fav: false,
session: !!req.session,
limit: 99999999
});
if('items' in f0cks) { if('items' in f0cks) {
count.f0cks = f0cks.items.length; count.f0cks = f0cks.items.length;
f0cks.items = f0cks.items.slice(0, 50); f0cks.items = f0cks.items.slice(0, 50);
} }
} catch(err) {
f0cks = false;
count.f0cks = 0;
}
try {
favs = await f0cklib.getf0cks({
user: user,
mode: req.session.mode,
fav: true,
session: !!req.session,
limit: 99999999
});
if('items' in favs) { if('items' in favs) {
count.favs = favs.items.length; count.favs = favs.items.length;
favs.items = favs.items.slice(0, 50); favs.items = favs.items.slice(0, 50);
} }
} catch(err) {
favs = false;
count.favs = 0;
}
const data = { const data = {
user: query[0], user: query[0],
@ -100,7 +111,7 @@ export default (router, tpl) => {
}); });
}); });
router.get(/^\/mode\/(\d)/, auth, async (req, res) => { router.get(/^\/mode\/(\d)/, lib.loggedin, async (req, res) => {
const mode = +req.url.split[1]; const mode = +req.url.split[1];
let referertmp = req.headers.referer; let referertmp = req.headers.referer;
let referer = ""; let referer = "";

View File

@ -0,0 +1,26 @@
import db from "../../inc/sql.mjs";
import cfg from "../../inc/config.mjs";
import f0cklib from "../routeinc/f0cklib.mjs";
export default (router, tpl) => {
router.get(/^\/picdump$/, async (req, res) => {
const dump = await db`
SELECT *
FROM items
WHERE (
to_timestamp(stamp) >= date_trunc('week', CURRENT_TIMESTAMP - interval '1 week') AND
to_timestamp(stamp) < date_trunc('week', CURRENT_TIMESTAMP)
) AND
mime LIKE 'image/%'
ORDER BY stamp DESC
`;
res.reply({
body: tpl.render('picdump', {
dump,
tmp: null
}, req)
});
});
return router;
};

View File

@ -8,13 +8,13 @@ export default (router, tpl) => {
try { try {
const list = await db` const list = await db`
select select
"user".user, "user".user, "user".admin,
coalesce("user_options".avatar, ${await lib.getDefaultAvatar()}) as avatar, coalesce("user_options".avatar, ${await lib.getDefaultAvatar()}) as avatar,
count(distinct(tag_id, item_id)) as count count(distinct(tag_id, item_id)) as count
from "tags_assign" from "tags_assign"
left join "user" on "user".id = "tags_assign".user_id left join "user" on "user".id = "tags_assign".user_id
left join "user_options" on "user_options".user_id = "user".id left join "user_options" on "user_options".user_id = "user".id
group by "user".user, "user_options".avatar group by "user".user, "user_options".avatar, "user".admin
order by count desc order by count desc
`; `;
const stats = await lib.countf0cks(); const stats = await lib.countf0cks();

View File

@ -5,7 +5,7 @@ import search from "../routeinc/search.mjs";
const _eps = 20; const _eps = 20;
export default (router, tpl) => { export default (router, tpl) => {
router.get(/^\/search(\/)?$/, lib.auth, async (req, res) => { router.get(/^\/search(\/)?$/, lib.loggedin, async (req, res) => {
let ret; let ret;
let tag = req.url.qs.tag ?? []; let tag = req.url.qs.tag ?? [];
let page = req.url.qs.page ?? 1; let page = req.url.qs.page ?? 1;

View File

@ -211,6 +211,19 @@ export default async bot => {
await fs.promises.copyFile(source, `./public/b/${filename}`); await fs.promises.copyFile(source, `./public/b/${filename}`);
await fs.promises.unlink(source).catch(_=>{}); await fs.promises.unlink(source).catch(_=>{});
// user alias
let username = e.user.nick || e.user.username;
const alias = (await db`
select "user"."user"
from "user_alias"
join "user" on "user".id = user_alias.userid
where lower(user_alias.alias) ilike ${username}
limit 1
`)?.[0]?.user;
if(alias) {
username = alias;
}
await db` await db`
insert into items ${ insert into items ${
db({ db({
@ -219,7 +232,7 @@ export default async bot => {
mime: mime, mime: mime,
size: size, size: size,
checksum: checksum, checksum: checksum,
username: e.user.nick || e.user.username, username: username,
userchannel: e.channel, userchannel: e.channel,
usernetwork: e.network, usernetwork: e.network,
stamp: ~~(new Date() / 1000), stamp: ~~(new Date() / 1000),

View File

@ -71,7 +71,7 @@ process.on('unhandledRejection', err => {
if(req.cookies.session) { if(req.cookies.session) {
const user = await db` const user = await db`
select "user".id, "user".login, "user".user, "user".level, "user_sessions".id as sess_id, "user_options".* select "user".id, "user".login, "user".user, "user".admin, "user_sessions".id as sess_id, "user_options".*
from "user_sessions" from "user_sessions"
left join "user" on "user".id = "user_sessions".user_id left join "user" on "user".id = "user_sessions".user_id
left join "user_options" on "user_options".user_id = "user_sessions".user_id left join "user_options" on "user_options".user_id = "user_sessions".user_id

View File

@ -2,7 +2,7 @@
<div class="pagewrapper"> <div class="pagewrapper">
<div id="main"> <div id="main">
<div class="index-container"> <div class="index-container">
@if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif @if(tmp.user)<h2>user: <a href="/user/{{ tmp.user.toLowerCase() }}">{!! tmp.user.toLowerCase() !!}</a>@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
@if(tmp.tag)<h2>tag: @if(session)<a href="/search?tag={!! tmp.tag.toLowerCase() !!}" target="_blank">{!! tmp.tag.toLowerCase() !!}</a>@else{!! tmp.tag.toLowerCase() !!}@endif@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif @if(tmp.tag)<h2>tag: @if(session)<a href="/search?tag={!! tmp.tag.toLowerCase() !!}" target="_blank">{!! tmp.tag.toLowerCase() !!}</a>@else{!! tmp.tag.toLowerCase() !!}@endif@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
<div class="posts"> <div class="posts">
@each(items as item) @each(items as item)

View File

@ -14,7 +14,7 @@
<div class="gapRight"> <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_favo"><use href="/s/img/iconset.svg#heart_{{ Object.values(item.favorites).filter(u => u.user == session.user)[0] ? 'solid' : 'regular' }}"></use></svg>
<svg class="iconset" id="a_tfull"><use href="/s/img/iconset.svg#window-{{ fullscreen == 1 ? 'minimize' : 'maximize' }}"></use></svg> <svg class="iconset" id="a_tfull"><use href="/s/img/iconset.svg#window-{{ fullscreen == 1 ? 'minimize' : 'maximize' }}"></use></svg>
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg> @if(session.admin)<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>@endif
</div> </div>
@endif @endif
</div> </div>
@ -83,7 +83,7 @@
@if(typeof item.tags !== "undefined") @if(typeof item.tags !== "undefined")
@each(item.tags as tag) @each(item.tags as tag)
<span @if(session)tooltip="{{ tag.user }}"@endif class="badge {{ tag.badge }} mr-2"> <span @if(session)tooltip="{{ tag.user }}"@endif class="badge {{ tag.badge }} mr-2">
<a href="/tag/{{ tag.normalized }}">{!! tag.tag !!}</a>@if(session)&nbsp;<a class="removetag" href="#">&#215;</a>@endif <a href="/tag/{{ tag.normalized }}">{!! tag.tag !!}</a>@if(session.admin)&nbsp;<a class="removetag" href="#">&#215;</a>@endif
</span> </span>
@endeach @endeach
@endif @endif

22
views/picdump.html Normal file
View File

@ -0,0 +1,22 @@
@include(snippets/header)
<div id="main">
<h2>Picdump (last week)</h2>
<div class="picdump">
@each(dump as line)
<a href="/{{ line.id }}"><img src="/t/{{ line.id }}.webp"></a>
@endeach
</div>
</div>
<style>
.picdump {
width: 100%;
padding: 10px;
}
.picdump > a {
margin-top: 12px;
}
.picdump > a > img {
width: 100%;
}
</style>
@include(snippets/footer)

View File

@ -11,8 +11,8 @@
@for(let i = 0; i < list.length; i++) @for(let i = 0; i < list.length; i++)
<tr> <tr>
<td>{{ i + 1 }}</td> <td>{{ i + 1 }}</td>
<td><a href="/{{ list[i].avatar }}"><img class="avatar" src="/t/{{ list[i].avatar }}.webp" /></a></td> <td><a href="/{{ list[i].avatar }}"><img class="avatar" src="/t/{{ list[i].avatar }}.webp"></a></td>
<td><a href="/user/{!! list[i].user !!}">{!! list[i].user !!}</a></td> <td>@if(list[i].admin)&#11088;&nbsp;@endif<a href="/user/{!! list[i].user !!}">{!! list[i].user !!}</a></td>
<td>{{ list[i].count }}</td> <td>{{ list[i].count }}</td>
</tr> </tr>
@endfor @endfor

View File

@ -1,7 +1,7 @@
@include(snippets/header) @include(snippets/header)
<div id="main"> <div id="main">
<h1>Settings</h1> <h1>Settings</h1>
@if(session.avatar)<a href="/{{ 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> <h2>Account</h2>
<table class="table"> <table class="table">
<tbody> <tbody>
@ -10,8 +10,8 @@
<td>{{ session.id }}</td> <td>{{ session.id }}</td>
</tr> </tr>
<tr> <tr>
<td>level</td> <td>admin</td>
<td>{{ session.level }}/100</td> <td>{{ !!session.admin }}</td>
</tr> </tr>
<tr> <tr>
<td>username</td> <td>username</td>
@ -19,7 +19,14 @@
</tr> </tr>
<tr> <tr>
<td>avatar</td> <td>avatar</td>
<td><input type="text" class="input" name="i_avatar" value="{{ session.avatar }}" /><input type="submit" id="s_avatar" value="save" /></td> <td><input type="text" class="input" name="i_avatar" value="{{ session.avatar }}"></td>
</tr>
<tr>
<td>mail</td>
<td><input type="text" class="input" name="i_mail" placeholder="hashed" disabled></td>
</tr>
<tr>
<td colspan="2"><input type="submit" id="s_avatar" value="save"></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -5,14 +5,14 @@
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link user" href="#" content="{{ session.user }}" data-toggle="dropdown"> <a class="nav-link user" href="#" content="{{ session.user }}" data-toggle="dropdown">
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span> <img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar"><span>@if(session.admin)&#11088;&nbsp;@endif{{ session.user }}</span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="/user/{{ session.user.toLowerCase() }}">my profile</a></li> <li><a href="/user/{{ session.user.toLowerCase() }}">my profile</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li> <li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li> <li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li>
<li><a href="/search">search</a></li> <li><a href="/search">search</a></li>
<li><a href="/admin">Admin</a></li> @if(session.admin)<li><a href="/admin">Admin</a></li>@endif
<li><a href="/about">About</a></li> <li><a href="/about">About</a></li>
<li><a href="/ranking">ranking</a></li> <li><a href="/ranking">ranking</a></li>
<li><a href="/settings">settings</a></li> <li><a href="/settings">settings</a></li>

View File

@ -1,133 +0,0 @@
@if(session)
<nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
<div class="navigation-links">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link" href="#" content="{{ session.user }}" data-toggle="dropdown">
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span>
</a>
<ul class="dropdown-menu">
<li><a href="/admin">adminpanel</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li>
<li><a href="/settings">settings</a></li>
<li><a href="/search">search</a></li>
<li><a href="/about">About</a></li>
<li><a href="/ranking">Ranking</a></li>
<li><a href="/logout">logout</a></li>
</ul>
</li>
<li class="nav-item dropdown" id="themes">
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
<ul class="dropdown-menu">
@each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&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-guest">
<ul class="navbar-nav-guests">
<li class="nav-item dropdown">
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
<ul class="dropdown-menu">
<li><a href="/login">login</a></li>
</ul>
</li>
<li class="nav-item dropdown" id="themes">
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
<ul class="dropdown-menu">
@each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&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">
<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>
@endif

View File

@ -8,7 +8,7 @@
@endif @endif
<div class="layersoffear"> <div class="layersoffear">
<div class="profile_head_username"> <div class="profile_head_username">
<span>{{ user.user }}</span> <span>@if(user.admin)&#11088;&nbsp;@endif{{ user.user }}</span>
</div> </div>
<div class="profile_head_user_stats"> <div class="profile_head_user_stats">
ID: {{ user.user_id }} Joined: {{ user.created_at }} ID: {{ user.user_id }} Joined: {{ user.created_at }}
@ -18,9 +18,9 @@
<div class="user_content_wrapper"> <div class="user_content_wrapper">
<div class="f0cks"> <div class="f0cks">
<div class="f0cks-header"> <div class="f0cks-header">
f0ck{{ count.f0cks == 1 ? '' : 's' }}: {{ count.f0cks }} <a href="{{ f0cks.link.main }}">view all</a> f0ck{{ count.f0cks == 1 ? '' : 's' }}: {{ count.f0cks }} <a href="{{ f0cks.link?.main }}">view all</a>
</div> </div>
@if('items' in f0cks) @if(count.f0cks)
<div class="posts"> <div class="posts">
@each(f0cks.items as item) @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> <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>
@ -32,9 +32,9 @@
</div> </div>
<div class="favs"> <div class="favs">
<div class="favs-header"> <div class="favs-header">
fav{{ count.favs == 1 ? '' : 's' }}: {{ count.favs }} <a href="{{ favs.link.main }}">view all</a> fav{{ count.favs == 1 ? '' : 's' }}: {{ count.favs }} <a href="{{ favs.link?.main }}">view all</a>
</div> </div>
@if('items' in favs) @if(count.favs)
<div class="posts"> <div class="posts">
@each(favs.items as item) @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> <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>