From 00e7820462ec53473c20fd3a1ed83706f185698f Mon Sep 17 00:00:00 2001 From: Max Richter Date: Sat, 25 Jan 2025 00:00:04 +0100 Subject: [PATCH] feat: admin log page --- deno.json | 3 +++ fresh.gen.ts | 2 ++ lib/cache.ts | 10 ++++++--- lib/documents.ts | 1 + lib/openai.ts | 2 +- lib/string.ts | 9 ++++++++ routes/admin/cache/index.tsx | 32 +++++++++++++++++++++++++++++ routes/api/articles/create/index.ts | 29 ++++++++++++++++---------- 8 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 routes/admin/cache/index.tsx diff --git a/deno.json b/deno.json index 7100bce..5142ec9 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,9 @@ { "lock": false, "nodeModulesDir": "auto", + "unstable": [ + "cron" + ], "tasks": { "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", "start": "deno run --env-file -A --watch=static/,routes/ dev.ts", diff --git a/fresh.gen.ts b/fresh.gen.ts index 3c388dd..e965132 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -6,6 +6,7 @@ import * as $_404 from "./routes/_404.tsx"; import * as $_app from "./routes/_app.tsx"; import * as $_layout from "./routes/_layout.tsx"; import * as $_middleware from "./routes/_middleware.ts"; +import * as $admin_cache_index from "./routes/admin/cache/index.tsx"; import * as $admin_log_index from "./routes/admin/log/index.tsx"; import * as $admin_performance_index from "./routes/admin/performance/index.tsx"; import * as $api_articles_name_ from "./routes/api/articles/[name].ts"; @@ -68,6 +69,7 @@ const manifest = { "./routes/_app.tsx": $_app, "./routes/_layout.tsx": $_layout, "./routes/_middleware.ts": $_middleware, + "./routes/admin/cache/index.tsx": $admin_cache_index, "./routes/admin/log/index.tsx": $admin_log_index, "./routes/admin/performance/index.tsx": $admin_performance_index, "./routes/api/articles/[name].ts": $api_articles_name_, diff --git a/lib/cache.ts b/lib/cache.ts index 6b7b3f6..979305c 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -48,7 +48,7 @@ export function createCache( } }, - info(): { count: number; sizeInKB: number } { + info() { // Cleanup expired entries before calculating info this.cleanup(); @@ -65,8 +65,8 @@ export function createCache( totalBytes += keySize + valueSize; } - const sizeInKB = totalBytes / 1024; // Convert bytes to kilobytes - return { count, sizeInKB }; + const sizeInKB = Math.floor(totalBytes / 1024); // Convert bytes to kilobytes + return { name: cacheName, count, sizeInKB }; }, has(key: string): boolean { @@ -98,3 +98,7 @@ export function createCache( }); return api; } + +export function getCacheInfo() { + return caches.values().map((c) => c.info()); +} diff --git a/lib/documents.ts b/lib/documents.ts index 8a3fab1..e59a86c 100644 --- a/lib/documents.ts +++ b/lib/documents.ts @@ -172,3 +172,4 @@ export function getTextOfChild(child: DocumentChild): string | undefined { } return; } + diff --git a/lib/openai.ts b/lib/openai.ts index c11c742..c822fce 100644 --- a/lib/openai.ts +++ b/lib/openai.ts @@ -3,7 +3,7 @@ import { zodResponseFormat } from "https://deno.land/x/openai@v4.69.0/helpers/zo import { OPENAI_API_KEY } from "@lib/env.ts"; import { hashString } from "@lib/helpers.ts"; import { createCache } from "@lib/cache.ts"; -import recipeSchema, { recipeResponseSchema } from "@lib/recipeSchema.ts"; +import { recipeResponseSchema } from "@lib/recipeSchema.ts"; const openAI = OPENAI_API_KEY && new OpenAI({ apiKey: OPENAI_API_KEY }); diff --git a/lib/string.ts b/lib/string.ts index e562a47..23a44bc 100644 --- a/lib/string.ts +++ b/lib/string.ts @@ -13,6 +13,15 @@ export function safeFileName(inputString: string): string { return fileName; } +export function toUrlSafeString(input: string): string { + return input + .trim() // Remove leading and trailing whitespace + .toLowerCase() // Convert to lowercase + .replace(/[^a-z0-9\s-]/g, "") // Remove non-alphanumeric characters except spaces and hyphens + .replace(/\s+/g, "-") // Replace spaces with hyphens + .replace(/-+/g, "-"); // Remove consecutive hyphens +} + export function extractHashTags(inputString: string) { const hashtags = []; diff --git a/routes/admin/cache/index.tsx b/routes/admin/cache/index.tsx new file mode 100644 index 0000000..7682d4f --- /dev/null +++ b/routes/admin/cache/index.tsx @@ -0,0 +1,32 @@ +import { MainLayout } from "@components/layouts/main.tsx"; +import { Handlers, PageProps } from "$fresh/server.ts"; +import { getCacheInfo } from "@lib/cache.ts"; + +export const handler: Handlers< + { cacheInfo: ReturnType } +> = { + GET(_, ctx) { + return ctx.render({ cacheInfo: getCacheInfo() }); + }, +}; + +export default function Greet( + props: PageProps< + { cacheInfo: ReturnType } + >, +) { + const { cacheInfo } = props.data; + return ( + + +
+          {JSON.stringify(cacheInfo, null, 2)}
+        
+
+
+ ); +} diff --git a/routes/api/articles/create/index.ts b/routes/api/articles/create/index.ts index a3b6a0b..ee9e745 100644 --- a/routes/api/articles/create/index.ts +++ b/routes/api/articles/create/index.ts @@ -8,7 +8,11 @@ import * as openai from "@lib/openai.ts"; import tds from "https://cdn.skypack.dev/turndown@7.2.0"; import { Article, createArticle } from "@lib/resource/articles.ts"; import { getYoutubeVideoDetails } from "@lib/youtube.ts"; -import { extractYoutubeId, isYoutubeLink } from "@lib/string.ts"; +import { + extractYoutubeId, + isYoutubeLink, + toUrlSafeString, +} from "@lib/string.ts"; import { createLogger } from "@lib/log/index.ts"; const parser = new DOMParser(); @@ -69,6 +73,10 @@ async function processCreateArticle( const url = new URL(fetchUrl); function makeUrlAbsolute(src: string) { + if (src.startsWith("//")) { + return "https:" + src; + } + if (src.startsWith("/")) { return `${url.origin}${src.replace(/$\//, "")}`; } @@ -98,20 +106,16 @@ async function processCreateArticle( if (href.startsWith("/")) { return `[${content}](${url.origin}${href.replace(/$\//, "")})`; - } - - if (href.startsWith("#")) { + } else if (href.startsWith("//")) { + return `[${content}](https:${href})`; + } else if (href.startsWith("#")) { if (content.length < 2) return ""; return `[${content}](${url.href}#${href})`.replace("##", "#"); - } - - if (!href.startsWith("https://") && !href.startsWith("http://")) { + } else { return `[${content}](${url.origin.replace(/\/$/, "")}/${ href.replace(/^\//, "") })`; } - - return `[${content}](${href})`; }, }); @@ -125,7 +129,10 @@ async function processCreateArticle( metaAuthor || openai.extractAuthorName(markdown), ]); - const id = shortTitle || title || ""; + console.log({ tags, shortTitle, author }); + streamResponse.enqueue("postprocessing article"); + + const id = toUrlSafeString(shortTitle || title || ""); const meta: Article["meta"] = { author: (author || "").replace("@", "twitter:"), @@ -161,7 +168,7 @@ async function processCreateArticle( } } - streamResponse.enqueue("finished processing"); + streamResponse.enqueue("writing to disk"); await createArticle(newArticle.id, newArticle);