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.enablePromptUseWorkspaceTsdk": true,
"editor.formatOnSave": true
"editor.formatOnSave": true,
"editor.tabSize": 2
}

View File

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

View File

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

View File

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

View File

@@ -92,10 +92,8 @@ const PostForm = ({
(thread !== undefined && board.config.reply_captcha)) &&
!canUser(ctx.user, CzchanPerm.BYPASS_CAPTCHA)) && (
<>
<FormField label="CAPTCHA" wrapper>
<FormField label="CAPTCHA" wrapper required>
<img class="full-width" src="/captcha" />
</FormField>
<FormField label="Řešení" required>
<input name="captcha" type="text" />
</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 ipaddr, { IPv6 } from "ipaddr.js";
import { parse } from "path";
import timestring from "timestring";
const encodeCapcode = (capcode: {
text: string;
@@ -118,35 +117,15 @@ const bytesToSize = (bytes: number) => {
return `${(bytes / Math.pow(1024, e)).toFixed(2)} ${" KMGTPEZY".charAt(e)}B`;
};
// TODO: MAKE THIS LESS RETARDED
// For user IDs
const secsToDuration = (secs: number) => {
let date = new Date(0);
const contrastingColor = (color: string) => {
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);
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;
}
return luma >= 165 ? "000000" : "ffffff";
};
// Truncate
@@ -180,8 +159,7 @@ export {
postDeadCrossQuote,
password,
bytesToSize,
secsToDuration,
timestringToSecs,
contrastingColor,
truncate,
truncateFilename,
};

View File

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

View File

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

View File

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