Chyba v readme + délka banu

This commit is contained in:
2026-04-20 14:22:38 +02:00
parent bbe75c008c
commit 79b3069312
11 changed files with 40 additions and 48 deletions

15
.vscode/launch.json vendored
View File

@@ -1,15 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node-terminal",
"name": "Run Script: start (czchan)",
"request": "launch",
"command": "npm run start",
"cwd": "${workspaceFolder}"
}
]
}

View File

@@ -7,7 +7,7 @@ Primárně český imageboardový engine zaměřený na rychlost, UX a dobrou ko
- node 23+
- postgres
- valkey nebo redis
- imagemagick 9+
- imagemagick 7+
- ffmpeg
## Instalace

10
package-lock.json generated
View File

@@ -11,6 +11,7 @@
"dependencies": {
"@kitajs/html": "^4.2.13",
"@kitajs/ts-html-plugin": "^4.1.4",
"@logdna/tail-file": "^4.0.2",
"@minify-html/node": "^0.18.1",
"apache-crypt": "^1.2.6",
"async-mutex": "^0.5.0",
@@ -2960,6 +2961,15 @@
"typescript": "^5.9.3"
}
},
"node_modules/@logdna/tail-file": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@logdna/tail-file/-/tail-file-4.0.2.tgz",
"integrity": "sha512-aEZF3LanJoewsQ9J1CRk5wvOqvQvw4lhxitSMXz3sQPgfezamzwW+pGvKXxvht7EvJEYSlYPxfSjL6T2bDSEkg==",
"license": "MIT",
"engines": {
"node": ">= 18"
}
},
"node_modules/@minify-html/node": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/@minify-html/node/-/node-0.18.1.tgz",

View File

@@ -9,6 +9,7 @@
],
"scripts": {
"test": "echo \"Léky teď\"",
"lint": "npx eslint .",
"backend": "npx xss-scan && npx tsc",
"js": "esbuild web/ts/main.ts --bundle --minify --outfile=public/js/bundle.tmp.js && npx babel public/js/bundle.tmp.js --out-file public/js/bundle.js && rm public/js/bundle.tmp.js",
"css": "npx sass ./web/sass:./public/css --no-source-map --style compressed && npx postcss ./public/css/**/*.css --use autoprefixer --replace",
@@ -55,6 +56,7 @@
"dependencies": {
"@kitajs/html": "^4.2.13",
"@kitajs/ts-html-plugin": "^4.1.4",
"@logdna/tail-file": "^4.0.2",
"@minify-html/node": "^0.18.1",
"apache-crypt": "^1.2.6",
"async-mutex": "^0.5.0",

View File

@@ -16,9 +16,9 @@ const SANE_DEFAULTS: Config = {
},
},
tasks: {
prune_bans_interval: 60_000,
prune_captchas_interval: 60_000,
prune_files_interval: 300_000,
prune_bans_interval: 60,
prune_captchas_interval: 60,
prune_files_interval: 300,
},
ui: {
name: "czchan",
@@ -28,6 +28,7 @@ const SANE_DEFAULTS: Config = {
reltime: true,
page_size: 20,
preview_replies: 5,
default_ban_length: 86400,
},
secrets: {
jwt: "",

View File

@@ -30,6 +30,7 @@ type CzchanConfig = {
reltime: boolean;
page_size: number;
preview_replies: number;
default_ban_length: number;
};
secrets: {
jwt: string;
@@ -93,29 +94,9 @@ const jsonConf = async (file: string) => {
return config;
};
const normalizeSiteConfig = (site: Config): Config => {
return {
...site,
server: {
...site.server,
headers: {
real_ip: site.server.headers?.real_ip || "X-Real-IP",
country_code: site.server.headers?.country_code || "X-Country-Code",
region_code: site.server.headers?.region_code || "X-Region-Code",
asn: site.server.headers?.asn || "X-ASN",
},
},
tasks: {
prune_bans_interval: site.tasks?.prune_bans_interval || 60_000,
prune_captchas_interval: site.tasks?.prune_captchas_interval || 60_000,
prune_files_interval: site.tasks?.prune_files_interval || 300_000,
},
};
};
const loadConfig = async (): Promise<CzchanConfig> => {
return {
site: normalizeSiteConfig(await jsonConf("./config/site.json")),
site: await jsonConf("./config/site.json"),
boardDefaults: await jsonConf("./config/board_defaults.json"),
roles: await jsonConf("./config/roles.json"),
assets: {
@@ -144,7 +125,6 @@ const getConfig = async (): Promise<CzchanConfig> => {
return {
...config,
site: normalizeSiteConfig(config.site),
};
};

View File

@@ -219,6 +219,10 @@ const contrastingColor = (color: string) => {
// Truncate
const truncate = (string: string, length: number) => {
if (string.length === 0) {
return null;
}
string = string.replace(/\s+/g, " ").replace(/\|\|(.+?)\|\|/g, "[SPOILER]");
return string.length > length ? `${string.slice(0, length)}...` : string;
};

View File

@@ -38,7 +38,7 @@ const scheduleTask = (
voidRun();
setInterval(voidRun, interval);
setInterval(voidRun, interval * 1000);
};
const main = async () => {

View File

@@ -1,4 +1,5 @@
import { canUser, CzchanPerm } from "../../../lib/permissions.js";
import { secsToTimestring } from "../../../lib/time.js";
import type { TemplateCtx } from "../../ctx.js";
import { Form, FormButton, FormField, FormHeader } from "./form.js";
import type { PropsWithChildren } from "@kitajs/html";
@@ -82,7 +83,13 @@ const PostActionsForm = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => (
</>
}
>
<input name="ban_duration" type="text" />
<input
name="ban_duration"
type="text"
placeholder={secsToTimestring(
ctx.config.site.ui.default_ban_length,
)}
/>
</FormField>
<FormField label="↳ Trvalý" wrapper>
<input name="permanent_ban" type="checkbox" />

View File

@@ -233,8 +233,11 @@ const modActions = async (
const expires = form.permanent_ban
? null
: // TODO: make default ban duration a config field
// 1 day for now
new Date(Date.now() + (form.ban_duration ?? 86400) * 1000);
new Date(
Date.now() +
(form.ban_duration ?? req.config.site.ui.default_ban_length) *
1000,
);
const ip_range = `${ip}/${prefix}`;
@@ -256,10 +259,10 @@ const modActions = async (
post,
`${
post.content
}\n\n<span class="red"><b>(${form.ban_message})</b></span>`,
}${post.content.length > 0 ? "\n\n" : ""}<span class="red"><b>(${form.ban_message})</b></span>`,
`${
post.content_unformatted
}\n\n##**${escapeHtml(form.ban_message)}**##`,
}${post.content_unformatted.length > 0 ? "\n\n" : ""}##**${escapeHtml(form.ban_message)}**##`,
);
}
}

View File

@@ -61,7 +61,7 @@ const Template = (
ctx={ctx}
title={`/${board.id}/ - ${op.subject ?? truncate(op.content_unformatted, 64) ?? "[bez obsahu]"}`}
theme={board.config.theme}
description={truncate(op.content_unformatted, 128)}
description={truncate(op.content_unformatted, 128) ?? "[bez obsahu]"}
/>
<PageBody ctx={ctx}>
<IBHeader banner={banner} board={board} />