IDs + time

This commit is contained in:
sneedmaster
2026-02-11 13:31:05 +01:00
parent 2dface30be
commit 34f79b3c8e
10 changed files with 140 additions and 61 deletions

View File

@@ -1,5 +1,6 @@
{ {
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true, "typescript.enablePromptUseWorkspaceTsdk": true,
"editor.formatOnSave": true "editor.formatOnSave": true,
"editor.tabSize": 2
} }

View File

@@ -24,10 +24,10 @@
"limit_ip": 50, "limit_ip": 50,
"interval_ip": 600, "interval_ip": 600,
"limit_content": 10, "limit_content": 10,
"interval_content": 6000, "interval_content": 600,
"limit_both": 10, "limit_both": 10,
"interval_both": 36000, "interval_both": 3600,
"code_deletion_timeout": 6000, "code_deletion_timeout": 600,
"reply_limit": 10000, "reply_limit": 10000,
"bump_limit": 8000, "bump_limit": 8000,
"noko": true, "noko": true,

View File

@@ -1,5 +1,6 @@
import { TemplateCtx } from "../ctx"; import { TemplateCtx } from "../ctx";
import { Board } from "../db/schema"; import { Board } from "../db/schema";
import { secsToTimestring } from "../time";
import { Form, FormField } from "./form"; import { Form, FormField } from "./form";
import { PropsWithChildren } from "@kitajs/html"; import { PropsWithChildren } from "@kitajs/html";
@@ -127,11 +128,11 @@ const BoardConfigForm = ({
value={board.config.limit_ip.toString()} value={board.config.limit_ip.toString()}
/> />
</FormField> </FormField>
<FormField label="Interval: IP (s)"> <FormField label="Interval: IP">
<input <input
name="interval_ip" name="interval_ip"
type="number" type="text"
value={board.config.interval_ip.toString()} value={secsToTimestring(board.config.interval_ip)}
/> />
</FormField> </FormField>
<FormField label="Limit: Obsah"> <FormField label="Limit: Obsah">
@@ -141,11 +142,11 @@ const BoardConfigForm = ({
value={board.config.limit_content.toString()} value={board.config.limit_content.toString()}
/> />
</FormField> </FormField>
<FormField label="Interval: Obsah (s)"> <FormField label="Interval: Obsah">
<input <input
name="interval_content" name="interval_content"
type="number" type="text"
value={board.config.interval_ip.toString()} value={secsToTimestring(board.config.interval_content)}
/> />
</FormField> </FormField>
<FormField label="Limit: IP + Obsah"> <FormField label="Limit: IP + Obsah">
@@ -155,18 +156,18 @@ const BoardConfigForm = ({
value={board.config.limit_both.toString()} value={board.config.limit_both.toString()}
/> />
</FormField> </FormField>
<FormField label="Interval: IP + Obsah (s)"> <FormField label="Interval: IP + Obsah">
<input <input
name="interval_both" name="interval_both"
type="number" type="text"
value={board.config.interval_both.toString()} value={secsToTimestring(board.config.interval_both)}
/> />
</FormField> </FormField>
<FormField label="Vymazání kódem do (s)"> <FormField label="Vymazání kódem do">
<input <input
name="code_deletion_timeout" name="code_deletion_timeout"
type="number" type="text"
value={board.config.max_length.toString()} value={secsToTimestring(board.config.code_deletion_timeout)}
/> />
</FormField> </FormField>
<FormField label="Limit odpvědí"> <FormField label="Limit odpvědí">

View File

@@ -1,8 +1,9 @@
import { Board, Post } from "../db/schema"; import { Board, Post } from "../db/schema";
import { secsToDuration } from "../time";
import { import {
bytesToSize, bytesToSize,
canonicalPostURL, canonicalPostURL,
secsToDuration, contrastingColor,
threadURL, threadURL,
truncateFilename, truncateFilename,
} from "../util"; } from "../util";
@@ -75,13 +76,18 @@ const PostComponent = ({
</a> </a>
</span>{" "} </span>{" "}
{board.config.user_ids && ( {board.config.user_ids && (
<span <>
class="user-id" <span
style={{ backgroundColor: `#${post.user_id}` }} class="user-id"
safe style={{
> backgroundColor: `#${post.user_id.slice(0, 6)}`,
{post.user_id} color: `#${contrastingColor(post.user_id.slice(0, 6))}`,
</span> }}
safe
>
{post.user_id}
</span>{" "}
</>
)} )}
{post.thread === null && ( {post.thread === null && (
<> <>

View File

@@ -92,10 +92,8 @@ const PostForm = ({
(thread !== undefined && board.config.reply_captcha)) && (thread !== undefined && board.config.reply_captcha)) &&
!canUser(ctx.user, CzchanPerm.BYPASS_CAPTCHA)) && ( !canUser(ctx.user, CzchanPerm.BYPASS_CAPTCHA)) && (
<> <>
<FormField label="CAPTCHA" wrapper> <FormField label="CAPTCHA" wrapper required>
<img class="full-width" src="/captcha" /> <img class="full-width" src="/captcha" />
</FormField>
<FormField label="Řešení" required>
<input name="captcha" type="text" /> <input name="captcha" type="text" />
</FormField> </FormField>
</> </>

86
src/time.ts Normal file
View File

@@ -0,0 +1,86 @@
import timestring from "timestring";
const SECOND = 1,
MINUTE = 60,
HOUR = 3600,
DAY = 86400,
WEEK = 604800,
MONTH = 2592000,
YEAR = 31536000;
const secsToDuration = (secs: number) => {
let hours = 0;
let minutes = 0;
let seconds = 0;
hours = Math.floor(secs / HOUR);
secs -= hours * HOUR;
minutes = Math.floor(secs / MINUTE);
secs -= minutes * MINUTE;
seconds = Math.floor(secs / SECOND);
secs -= seconds * SECOND;
let duration = "";
if (hours > 0) {
duration += `${hours.toString().padStart(2, "0")}:`;
}
duration += `${minutes.toString().padStart(2, "0")}:`;
duration += `${seconds.toString().padStart(2, "0")}`;
return duration;
};
// Shits out a timestring, to be used for autofills
const secsToTimestring = (secs: number) => {
let years = 0;
let months = 0;
let weeks = 0;
let days = 0;
let hours = 0;
let minutes = 0;
let seconds = 0;
years = Math.floor(secs / YEAR);
secs -= years * YEAR;
months = Math.floor(secs / MONTH);
secs -= months * MONTH;
weeks = Math.floor(secs / WEEK);
secs -= weeks * WEEK;
days = Math.floor(secs / DAY);
secs -= days * DAY;
hours = Math.floor(secs / HOUR);
secs -= hours * HOUR;
minutes = Math.floor(secs / MINUTE);
secs -= minutes * MINUTE;
seconds = Math.floor(secs / SECOND);
secs -= seconds * SECOND;
const segments = [];
if (years > 0) segments.push(`${years}y`);
if (months > 0) segments.push(`${months}mon`);
if (weeks > 0) segments.push(`${weeks}w`);
if (days > 0) segments.push(`${days}d`);
if (hours > 0) segments.push(`${hours}h`);
if (minutes > 0) segments.push(`${minutes}m`);
if (seconds > 0) segments.push(`${seconds}s`);
return segments.join(" ");
};
const timestringToSecs = (time: string) => {
try {
// Horrible but consistent with the inverse and good enough for input
return timestring(time, undefined, {
weeksPerMonth: 30 / 7,
monthsPerYear: 73 / 6,
daysPerYear: 365,
});
} catch {
return null;
}
};
export { secsToDuration, secsToTimestring, timestringToSecs };

View File

@@ -2,7 +2,6 @@ import { Board, Post } from "./db/schema";
import { createHash } from "crypto"; import { createHash } from "crypto";
import ipaddr, { IPv6 } from "ipaddr.js"; import ipaddr, { IPv6 } from "ipaddr.js";
import { parse } from "path"; import { parse } from "path";
import timestring from "timestring";
const encodeCapcode = (capcode: { const encodeCapcode = (capcode: {
text: string; text: string;
@@ -118,35 +117,15 @@ const bytesToSize = (bytes: number) => {
return `${(bytes / Math.pow(1024, e)).toFixed(2)} ${" KMGTPEZY".charAt(e)}B`; return `${(bytes / Math.pow(1024, e)).toFixed(2)} ${" KMGTPEZY".charAt(e)}B`;
}; };
// TODO: MAKE THIS LESS RETARDED // For user IDs
const secsToDuration = (secs: number) => { const contrastingColor = (color: string) => {
let date = new Date(0); const r = parseInt(color.slice(0, 2), 16);
const g = parseInt(color.slice(2, 4), 16);
const b = parseInt(color.slice(4, 6), 16);
const luma = r * 0.2126 + g * 0.7152 + b * 0.0722;
date.setUTCSeconds(secs); return luma >= 165 ? "000000" : "ffffff";
const hours = date.getUTCHours();
const minutes = date.getUTCMinutes();
const seconds = date.getUTCSeconds();
let duration = "";
if (hours > 0) {
duration += `${hours.toString().padStart(2, "0")}:`;
}
duration += `${minutes.toString().padStart(2, "0")}:`;
duration += `${seconds.toString().padStart(2, "0")}`;
return duration;
};
const timestringToSecs = (time: string) => {
try {
return timestring(time);
} catch {
return null;
}
}; };
// Truncate // Truncate
@@ -180,8 +159,7 @@ export {
postDeadCrossQuote, postDeadCrossQuote,
password, password,
bytesToSize, bytesToSize,
secsToDuration, contrastingColor,
timestringToSecs,
truncate, truncate,
truncateFilename, truncateFilename,
}; };

View File

@@ -1,3 +1,4 @@
import { timestringToSecs } from "../../time";
import z from "zod"; import z from "zod";
const zBlockPolicy = z.enum( const zBlockPolicy = z.enum(
@@ -15,10 +16,10 @@ const zLimit = z.coerce
.int("Limit musí být celé číslo.") .int("Limit musí být celé číslo.")
.positive("Limit musí být kladný."); .positive("Limit musí být kladný.");
const zInterval = z.coerce const zInterval = z
.number("Interval musí být číslo.") .string()
.int("Interval musí být celé číslo.") .transform((time) => timestringToSecs(time))
.positive("Interval musí být klad."); .refine((time) => time !== null, "Interval musí být plat čas");
const zTheme = z const zTheme = z
.string() .string()

View File

@@ -175,6 +175,10 @@ footer {
font-weight: bold; font-weight: bold;
} }
.user-id {
padding: 2px;
}
.post-body { .post-body {
margin: 8px 16px; margin: 8px 16px;
} }

View File

@@ -70,6 +70,10 @@ header {
color: $trip; color: $trip;
} }
.user-id {
border: 1px solid $box-border;
}
.thumb-video { .thumb-video {
border: 2px solid $video-border; border: 2px solid $video-border;
} }