IDs + time
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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í">
|
||||||
|
|||||||
@@ -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
|
<span
|
||||||
class="user-id"
|
class="user-id"
|
||||||
style={{ backgroundColor: `#${post.user_id}` }}
|
style={{
|
||||||
|
backgroundColor: `#${post.user_id.slice(0, 6)}`,
|
||||||
|
color: `#${contrastingColor(post.user_id.slice(0, 6))}`,
|
||||||
|
}}
|
||||||
safe
|
safe
|
||||||
>
|
>
|
||||||
{post.user_id}
|
{post.user_id}
|
||||||
</span>
|
</span>{" "}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{post.thread === null && (
|
{post.thread === null && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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
86
src/time.ts
Normal 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 };
|
||||||
38
src/util.ts
38
src/util.ts
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 kladný.");
|
.refine((time) => time !== null, "Interval musí být platný čas");
|
||||||
|
|
||||||
const zTheme = z
|
const zTheme = z
|
||||||
.string()
|
.string()
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user