From c75fff3d45d3352b37513040b428ae98aa88bbaa Mon Sep 17 00:00:00 2001 From: ynwd <10122431+ynwd@users.noreply.github.com> Date: Sun, 15 Sep 2024 20:19:58 +0700 Subject: [PATCH] feat: add store --- components/button.tsx | 2 +- {http => core}/build/build.test.ts | 0 {http => core}/build/build.ts | 0 {http => core}/build/deps.ts | 0 {http => core}/build/esbuildMod.ts | 0 core/server/deps.ts | 21 ++ {http => core}/server/mod.test.tsx | 0 {http => core}/server/mod.ts | 26 ++- {http => core}/server/render.test.tsx | 2 +- {http => core}/server/render.ts | 0 core/server/store.test.ts | 82 +++++++ core/server/store.ts | 304 ++++++++++++++++++++++++++ {http => core}/server/types.ts | 4 +- deno.json | 4 +- http/server/deps.ts | 21 -- middleware/markdown/mod.tsx | 2 +- mod.ts | 4 +- modules/auth/mod.tsx | 8 +- modules/blog/mod.ts | 22 +- modules/docs/mod.ts | 22 +- modules/group/group.service.test.ts | 2 +- modules/index/index.layout.tsx | 2 +- modules/index/index.page.tsx | 2 +- modules/index/mod.ts | 22 +- modules/markdown/mod.tsx | 28 +-- modules/permission/permission.test.ts | 2 +- modules/toc/toc.layout.tsx | 2 +- modules/toc/toc.page.tsx | 2 +- modules/user/user.service.test.ts | 2 +- modules/web/app.layout.tsx | 2 +- modules/web/dear.page.tsx | 2 +- utils/session.ts | 15 ++ 32 files changed, 481 insertions(+), 126 deletions(-) rename {http => core}/build/build.test.ts (100%) rename {http => core}/build/build.ts (100%) rename {http => core}/build/deps.ts (100%) rename {http => core}/build/esbuildMod.ts (100%) create mode 100644 core/server/deps.ts rename {http => core}/server/mod.test.tsx (100%) rename {http => core}/server/mod.ts (96%) rename {http => core}/server/render.test.tsx (98%) rename {http => core}/server/render.ts (100%) create mode 100644 core/server/store.test.ts create mode 100644 core/server/store.ts rename {http => core}/server/types.ts (98%) delete mode 100644 http/server/deps.ts create mode 100644 utils/session.ts diff --git a/components/button.tsx b/components/button.tsx index ddabf4284..487d35223 100644 --- a/components/button.tsx +++ b/components/button.tsx @@ -1,4 +1,4 @@ -import type { ComponentChildren } from "https://esm.sh/preact@10.23.2"; +import type { ComponentChildren } from "https://esm.sh/preact@10.24.0"; export default function Button(props: { children: ComponentChildren }) { return ( diff --git a/http/build/build.test.ts b/core/build/build.test.ts similarity index 100% rename from http/build/build.test.ts rename to core/build/build.test.ts diff --git a/http/build/build.ts b/core/build/build.ts similarity index 100% rename from http/build/build.ts rename to core/build/build.ts diff --git a/http/build/deps.ts b/core/build/deps.ts similarity index 100% rename from http/build/deps.ts rename to core/build/deps.ts diff --git a/http/build/esbuildMod.ts b/core/build/esbuildMod.ts similarity index 100% rename from http/build/esbuildMod.ts rename to core/build/esbuildMod.ts diff --git a/core/server/deps.ts b/core/server/deps.ts new file mode 100644 index 000000000..a1fbf1825 --- /dev/null +++ b/core/server/deps.ts @@ -0,0 +1,21 @@ +export { STATUS_CODE, STATUS_TEXT } from "jsr:@std/http@^1.0.6"; + +export * from "jsr:@std/media-types@^1.0.2"; +export * from "jsr:@std/path@^1.0.1"; + +export { encodeHex } from "jsr:@std/encoding@^1.0.1/hex"; +export { assertEquals } from "jsr:@std/assert@^1.0.5"; +export { assertExists } from "jsr:@std/assert@^1.0.5"; +export { assert } from "jsr:@std/assert@^1.0.5"; + +export { h } from "https://esm.sh/preact@10.24.0"; +export type { + ComponentChild, + ComponentChildren, + JSX, + VNode, +} from "https://esm.sh/preact@10.24.0"; +export { + renderToString, + renderToStringAsync, +} from "https://esm.sh/preact-render-to-string@6.5.9?deps=preact@10.24.0"; diff --git a/http/server/mod.test.tsx b/core/server/mod.test.tsx similarity index 100% rename from http/server/mod.test.tsx rename to core/server/mod.test.tsx diff --git a/http/server/mod.ts b/core/server/mod.ts similarity index 96% rename from http/server/mod.ts rename to core/server/mod.ts index 7dfcb49a9..e6a63833b 100644 --- a/http/server/mod.ts +++ b/core/server/mod.ts @@ -21,6 +21,7 @@ import { Static, } from "./types.ts"; import { EsbuildMod } from "../build/esbuildMod.ts"; +import { Store } from "./store.ts"; export function checkReferer(req: Request) { const referer = req.headers.get("referer"); @@ -77,7 +78,7 @@ const createResponse = ( }; export default class Server implements Fastro { - constructor(options?: Record) { + constructor(options?: Map) { this.serverOptions = options ?? {}; this.#handler = this.#createHandler(); this.#addPropsEndpoint(); @@ -303,10 +304,10 @@ if (root) fetchProps(root); } } - #getParamsHandler>( + #getParamsHandler>( req: Request, data: T, - ): [Handler, Record | undefined] | undefined { + ): [Handler, Map | undefined] | undefined { for (const [key, handler] of Object.entries(data)) { const [method, path] = key.split("-"); const pattern = new URLPattern({ pathname: path }); @@ -318,10 +319,10 @@ if (root) fetchProps(root); } } - #getParamsPage>( + #getParamsPage>( req: Request, data: T, - ): [Page, Record | undefined] | undefined { + ): [Page, Map | undefined] | undefined { for (const [path, page] of Object.entries(data)) { const pattern = new URLPattern({ pathname: path }); if (path.includes(":") && pattern.test(req.url)) { @@ -381,7 +382,7 @@ if (root) fetchProps(root); const url = new URL(req.url); let key = url.pathname; let page: Page = this.#routePage[key]; - let params: Record | undefined = undefined; + let params: Map | undefined = undefined; if (!page) { const res = this.#getParamsPage(req, this.#routePage); if (res) { @@ -472,14 +473,14 @@ if (root) fetchProps(root); #iterableToRecord( params: URLSearchParams, ) { - const record: Record = {}; + const record: Map = {}; params.forEach((v, k) => (record[k] = v)); return record; } #transformRequest = ( req: Request, - params?: Record, + params?: Map, url?: URL, ) => { const u = url ?? new URL(req.url); @@ -493,7 +494,7 @@ if (root) fetchProps(root); #transformCtx = ( info: Deno.ServeHandlerInfo, url: URL, - options: Record, + options: Map, ) => { const ctx = options as Context; const r = new Render(this); @@ -517,7 +518,7 @@ if (root) fetchProps(root); const url = new URL(req.url); const key = req.method + "-" + url.pathname; let handler = this.#routeHandler[key]; - let params: Record | undefined; + let params: Map | undefined; if (!handler) { const res = this.#getParamsHandler(req, this.#routeHandler); if (res) { @@ -645,11 +646,11 @@ if (root) fetchProps(root); } }; - getPages(): Record { + getPages(): Map { return this.#routePage; } - getRoutes(): Record { + getRoutes(): Map { return this.#routeHandler; } @@ -670,4 +671,5 @@ if (root) fetchProps(root); #maxAge = 0; #nonce = ""; serverOptions: Record = {}; + store = new Store(); } diff --git a/http/server/render.test.tsx b/core/server/render.test.tsx similarity index 98% rename from http/server/render.test.tsx rename to core/server/render.test.tsx index 624ca6299..66a840bc4 100644 --- a/http/server/render.test.tsx +++ b/core/server/render.test.tsx @@ -1,4 +1,4 @@ -import { assert, assertEquals, assertExists } from "@app/http/server/deps.ts"; +import { assert, assertEquals, assertExists } from "@app/core/server/deps.ts"; import fastro from "@app/mod.ts"; import hello from "@app/modules/web/hello.page.tsx"; import dear from "@app/modules/web/dear.page.tsx"; diff --git a/http/server/render.ts b/core/server/render.ts similarity index 100% rename from http/server/render.ts rename to core/server/render.ts diff --git a/core/server/store.test.ts b/core/server/store.test.ts new file mode 100644 index 000000000..eeba54bef --- /dev/null +++ b/core/server/store.test.ts @@ -0,0 +1,82 @@ +import { assert, assertEquals } from "./deps.ts"; +import { Store } from "./store.ts"; + +Deno.test("ExpiringMap: should set and get a value", async () => { + const expiringMap = new Store(1000); // 1 second TTL + expiringMap.set("key1", "value1"); + assertEquals(await expiringMap.get("key1"), "value1"); +}); + +Deno.test("ExpiringMap: should return undefined for expired keys", async () => { + const expiringMap = new Store(100); + expiringMap.set("key1", "value1"); + await new Promise((resolve) => setTimeout(resolve, 200)); + assertEquals(await expiringMap.get("key1"), undefined); +}); + +Deno.test("ExpiringMap: should check if a key exists", async () => { + const expiringMap = new Store(100); + expiringMap.set("key1", "value1"); + assert(expiringMap.has("key1")); + await new Promise((resolve) => setTimeout(resolve, 200)); + assert(!expiringMap.has("key1")); +}); + +Deno.test("ExpiringMap: should delete a key", async () => { + const expiringMap = new Store(); + expiringMap.set("key1", "value1"); + assert(expiringMap.delete("key1")); + assertEquals(await expiringMap.get("key1"), undefined); +}); + +Deno.test("ExpiringMap: should clear all entries", () => { + const expiringMap = new Store(); + expiringMap.set("key1", "value1"); + expiringMap.set("key2", "value2"); + expiringMap.clear(); + assertEquals(expiringMap.size(), 0); +}); + +Deno.test("ExpiringMap: should get the size of the map", () => { + const expiringMap = new Store(); + expiringMap.set("key1", "value1"); + expiringMap.set("key2", "value2"); + assertEquals(expiringMap.size(), 2); +}); + +Deno.test("ExpiringMap: should iterate over valid entries using forEach", () => { + const expiringMap = new Store(1000); + expiringMap.set("key1", "value1"); + expiringMap.set("key2", "value2"); + + const entries: [string, string][] = []; + expiringMap.forEach((value, key) => { + entries.push([key, value]); + }); + + assertEquals(entries.length, 2); + assertEquals(entries, [["key1", "value1"], ["key2", "value2"]]); +}); + +Deno.test("ExpiringMap: should set a key with custom TTL", async () => { + const expiringMap = new Store(2000); + expiringMap.set("key1", "value1", 1000); + assertEquals(await expiringMap.get("key1"), "value1"); + await new Promise((resolve) => setTimeout(resolve, 1100)); + assertEquals(await expiringMap.get("key1"), undefined); +}); + +Deno.test("ExpiringMap: save it to github", async () => { + const token = Deno.env.get("GITHUB_TOKEN"); + if (!token) return; + const expiringMap = new Store(2000, { + owner: "fastrodev", + repo: "fastro", + path: "modules/store/records.json", + token, + }); + const d = new Date(); + expiringMap.set("key1", d.getTime(), 1000); + const c = await expiringMap.commit(); + assertEquals(c?.data.content?.name, "records.json"); +}); diff --git a/core/server/store.ts b/core/server/store.ts new file mode 100644 index 000000000..fcf5909bb --- /dev/null +++ b/core/server/store.ts @@ -0,0 +1,304 @@ +// deno-lint-ignore-file no-explicit-any +import { Octokit } from "npm:@octokit/rest"; + +type StoreOptions = { + owner: string; + repo: string; + path: string; + token: string; + branch?: string; +} | null; + +export class Store { + private map: Map; + private defaultTTL: number; + private options: StoreOptions; + private saveIntervalId: number | null = null; + + constructor(defaultTTL: number = 60000, options: StoreOptions = null) { + this.map = new Map(); + this.defaultTTL = defaultTTL; + this.options = options; + } + + set(key: K, value: V, ttl?: number): void { + const expiry = Date.now() + (ttl ?? this.defaultTTL); + this.map.set(key, { value, expiry }); + } + + async get(key: K): Promise { + if (this.map.size === 0 && this.options) { + const rr = await getFileFromGithub( + this.options.token, + this.options.owner, + this.options.repo, + this.options.path, + this.options.branch, + ) as any; + if (rr) { + const data = atob(rr.content); + this.map = recordToMap(JSON.parse(data)); + } + } + + const entry = this.map.get(key); + if (entry) { + if (Date.now() < entry.expiry) { + return entry.value; + } else { + this.map.delete(key); + } + } + return undefined; + } + + has(key: K): boolean { + const entry = this.map.get(key); + if (entry) { + if (Date.now() < entry.expiry) { + return true; + } else { + this.map.delete(key); + } + } + return false; + } + + delete(key: K): boolean { + return this.map.delete(key); + } + + clear(): void { + this.map.clear(); + } + + size(): number { + this.cleanUpExpiredEntries(); + return this.map.size; + } + + entries() { + this.cleanUpExpiredEntries(); + return this.map.entries(); + } + + values() { + this.cleanUpExpiredEntries(); + return this.map.values(); + } + + keys() { + this.cleanUpExpiredEntries(); + return this.map.keys(); + } + + forEach(callback: (value: V, key: K) => void): void { + this.cleanUpExpiredEntries(); + this.map.forEach((entry, key) => { + callback(entry.value, key); + }); + } + + async commit() { + this.cleanUpExpiredEntries(); + if (!this.options) throw new Error("Options is needed."); + return await this.saveToGitHub( + this.options.token, + this.options.owner, + this.options.repo, + this.options.path, + ); + } + + async destroy() { + this.map.clear(); + if (!this.options) throw new Error("Options is needed."); + return await deleteGithubFile( + this.options.token, + this.options.owner, + this.options.repo, + this.options.path, + this.options.branch, + ); + } + + startAutoSave(interval: number): void { + if (this.saveIntervalId) { + clearInterval(this.saveIntervalId); + } + + this.saveIntervalId = setInterval(() => { + if (!this.options) return; + return this.saveToGitHub( + this.options.token, + this.options.owner, + this.options.repo, + this.options.path, + ); + }, interval); + } + + stopAutoSave(): void { + if (this.saveIntervalId) { + clearInterval(this.saveIntervalId); + this.saveIntervalId = null; + } + } + + private async saveToGitHub( + token: string, + owner: string, + repo: string, + path: string, + branch?: string, + ) { + try { + if (this.options) { + const record = mapToRecord(this.map); + const data = JSON.stringify(record); + return await uploadFileToGitHub( + token, + owner, + repo, + path, + data, + branch, + ); + } + } catch (error) { + throw error; + } + } + + private cleanUpExpiredEntries(): void { + for (const [key, entry] of this.map.entries()) { + if (Date.now() >= entry.expiry) { + this.map.delete(key); + } + } + } +} + +function recordToMap( + record: Record, +): Map { + const map = new Map(); + + Object.entries(record).forEach(([key, value]) => { + map.set(key as K, value as { value: V; expiry: number }); + }); + + return map; +} + +function mapToRecord( + map: Map, +): Record { + return Object.fromEntries(map) as Record; +} + +async function getSHA( + token: string, + repoOwner: string, + repoName: string, + filePath: string, + branch?: string, +): Promise { + const octokit = new Octokit({ auth: token }); + + try { + const response = await octokit.repos.getContent({ + owner: repoOwner, + repo: repoName, + path: filePath, + branch, + }) as any; + + return response.data.sha; + } catch (error) { + throw error; + } +} + +async function uploadFileToGitHub( + token: string, + repoOwner: string, + repoName: string, + filePath: string, + fileContent: string, + branch?: string, +) { + const octokit = new Octokit({ auth: token }); + try { + const res = await getFileFromGithub( + token, + repoOwner, + repoName, + filePath, + branch, + ) as any; + + const sha = res + ? await getSHA(token, repoOwner, repoName, filePath) + : undefined; + + const message = res ? `Update ${filePath}.` : `Create ${filePath}.`; + return await octokit.repos.createOrUpdateFileContents({ + owner: repoOwner, + repo: repoName, + path: filePath, + message, + content: btoa(fileContent), + sha, + branch, + }); + } catch (error) { + throw error; + } +} + +async function getFileFromGithub( + token: string, + repoOwner: string, + repoName: string, + filePath: string, + branch?: string, +) { + const octokit = new Octokit({ auth: token }); + try { + const res = await octokit.repos.getContent({ + owner: repoOwner, + repo: repoName, + path: filePath, + branch, + }); + return res.data; + } catch { + return undefined; + } +} + +async function deleteGithubFile( + token: string, + repoOwner: string, + repoName: string, + filePath: string, + branch?: string, +) { + const octokit = new Octokit({ auth: token }); + try { + const sha = await getSHA(token, repoOwner, repoName, filePath); + if (!sha) throw new Error("SHA is needed"); + const res = await octokit.repos.deleteFile({ + owner: repoOwner, + repo: repoName, + path: filePath, + sha, + branch, + message: `Delete ${filePath}`, + }); + return res.data; + } catch (error) { + throw error; + } +} diff --git a/http/server/types.ts b/core/server/types.ts similarity index 98% rename from http/server/types.ts rename to core/server/types.ts index 340b8493b..b982566cf 100644 --- a/http/server/types.ts +++ b/core/server/types.ts @@ -1,5 +1,6 @@ // deno-lint-ignore-file no-explicit-any import { ComponentChildren, JSX } from "./deps.ts"; +import { Store } from "./store.ts"; /** * The callback which is called when the server starts listening. @@ -122,7 +123,7 @@ export class Context { /** * Server options defined in Fastro Constuctor */ - options!: Record; + options!: Map; [key: string]: any; } @@ -246,6 +247,7 @@ export interface Fastro { use(...handler: Array): Fastro; group(mf: ModuleFunction): Promise; serverOptions: Record; + store: Store; getNonce(): string; getPages(): Record; getRoutes(): Record; diff --git a/deno.json b/deno.json index 9382a791e..d5d1cc2f6 100644 --- a/deno.json +++ b/deno.json @@ -12,8 +12,8 @@ }, "imports": { "@app/": "./", - "preact": "https://esm.sh/preact@10.23.2", - "preact/": "https://esm.sh/preact@10.23.2/", + "preact": "https://esm.sh/preact@10.24.0", + "preact/": "https://esm.sh/preact@10.24.0/", "chalk": "npm:chalk@5" }, "compilerOptions": { diff --git a/http/server/deps.ts b/http/server/deps.ts deleted file mode 100644 index b7386cc61..000000000 --- a/http/server/deps.ts +++ /dev/null @@ -1,21 +0,0 @@ -export { STATUS_CODE, STATUS_TEXT } from "jsr:@std/http@^0.224.5/status"; - -export * from "jsr:@std/media-types@^1.0.2"; -export * from "jsr:@std/path@^1.0.1"; - -export { encodeHex } from "jsr:@std/encoding@^1.0.1/hex"; -export { assertEquals } from "jsr:@std/assert@^0.226.0/assert-equals"; -export { assertExists } from "jsr:@std/assert@^0.226.0/assert-exists"; -export { assert } from "jsr:@std/assert@^0.226.0/assert"; - -export { h } from "https://esm.sh/preact@10.23.2"; -export type { - ComponentChild, - ComponentChildren, - JSX, - VNode, -} from "https://esm.sh/preact@10.23.2"; -export { - renderToString, - renderToStringAsync, -} from "https://esm.sh/preact-render-to-string@6.5.9?deps=preact@10.23.2"; diff --git a/middleware/markdown/mod.tsx b/middleware/markdown/mod.tsx index 70a0ffbea..216fe4ab2 100644 --- a/middleware/markdown/mod.tsx +++ b/middleware/markdown/mod.tsx @@ -1,5 +1,5 @@ import { CSS, extract, remark, render } from "./deps.ts"; -import { renderToString } from "../../http/server/deps.ts"; +import { renderToString } from "../../core/server/deps.ts"; import { Context, FunctionComponent, HttpRequest } from "../../mod.ts"; import "https://esm.sh/v135/prismjs@1.29.0/components/prism-css"; diff --git a/mod.ts b/mod.ts index 8022c2962..a168e383b 100644 --- a/mod.ts +++ b/mod.ts @@ -1,6 +1,6 @@ export * from "./middleware/markdown/mod.tsx"; export * from "./middleware/tailwind/mod.ts"; export * from "./middleware/tailwind/types.ts"; -export * from "./http/server/types.ts"; -import Server from "./http/server/mod.ts"; +export * from "./core/server/types.ts"; +import Server from "./core/server/mod.ts"; export default Server; diff --git a/modules/auth/mod.tsx b/modules/auth/mod.tsx index e57a384b7..0ef28c717 100644 --- a/modules/auth/mod.tsx +++ b/modules/auth/mod.tsx @@ -1,5 +1,5 @@ -import { Context, Fastro, HttpRequest } from "@app/http/server/types.ts"; -import { STATUS_CODE } from "@app/http/server/deps.ts"; +import { Context, Fastro, HttpRequest } from "../../core/server/types.ts"; +import { STATUS_CODE } from "../../core/server/deps.ts"; import { kv } from "@app/utils/db.ts"; import { createGitHubOAuthConfig, @@ -93,7 +93,7 @@ export const callbackHandler = async (req: HttpRequest, ctx: Context) => { req, ); const user = await getUser(tokens.accessToken); - ctx.server.serverOptions[sessionId] = user; + ctx.server.store.set(sessionId, user, 60 * 1000); return response; } catch { return new Response(null, { status: STATUS_CODE.InternalServerError }); @@ -103,7 +103,7 @@ export const callbackHandler = async (req: HttpRequest, ctx: Context) => { export const signoutHandler = async (req: HttpRequest, ctx: Context) => { const sessionId = await getSessionId(req); if (!sessionId) throw new Error("session ID is undefined"); - ctx.server.serverOptions[sessionId] = undefined; + ctx.server.store.delete(sessionId); return await signOut(req); }; diff --git a/modules/blog/mod.ts b/modules/blog/mod.ts index bdf572cdd..b349dd5b0 100644 --- a/modules/blog/mod.ts +++ b/modules/blog/mod.ts @@ -1,7 +1,7 @@ import { Fastro } from "@app/mod.ts"; import tocLayout from "@app/modules/toc/toc.layout.tsx"; import tocApp from "@app/modules/toc/toc.page.tsx"; -import { getSessionId } from "@app/modules/auth/mod.tsx"; +import { getSession } from "@app/utils/session.ts"; export default function (s: Fastro) { s.page("/blog", { @@ -9,23 +9,11 @@ export default function (s: Fastro) { layout: tocLayout, folder: "modules/toc", handler: async (req, ctx) => { - const sessionId = await getSessionId(req); - const hasSessionIdCookie = sessionId !== undefined; - const isLogin = hasSessionIdCookie; - let avatar_url = ""; - let html_url = ""; - if (sessionId) { - const r = ctx.server.serverOptions[sessionId]; - if (r) { - avatar_url = r.avatar_url; - html_url = r.html_url; - } - } - + const ses = await getSession(req, ctx); return ctx.render({ - isLogin, - avatar_url, - html_url, + isLogin: ses?.isLogin, + avatar_url: ses?.avatar_url, + html_url: ses?.html_url, title: "Blog", description: "Blog", destination: "/blog", diff --git a/modules/docs/mod.ts b/modules/docs/mod.ts index 408a7a84f..b778d79c6 100644 --- a/modules/docs/mod.ts +++ b/modules/docs/mod.ts @@ -2,7 +2,7 @@ import { Fastro } from "@app/mod.ts"; import tocLayout from "@app/modules/toc/toc.layout.tsx"; import tocApp from "@app/modules/toc/toc.page.tsx"; import { docToc } from "@app/modules/docs/docs.layout.tsx"; -import { getSessionId } from "@app/modules/auth/mod.tsx"; +import { getSession } from "@app/utils/session.ts"; export default function (s: Fastro) { s.page("/docs", { @@ -10,23 +10,11 @@ export default function (s: Fastro) { layout: tocLayout, folder: "modules/toc", handler: async (req, ctx) => { - const sessionId = await getSessionId(req); - const hasSessionIdCookie = sessionId !== undefined; - const isLogin = hasSessionIdCookie; - let avatar_url = ""; - let html_url = ""; - if (sessionId) { - const r = ctx.server.serverOptions[sessionId]; - if (r) { - avatar_url = r.avatar_url; - html_url = r.html_url; - } - } - + const ses = await getSession(req, ctx); return ctx.render({ - isLogin, - avatar_url, - html_url, + isLogin: ses?.isLogin, + avatar_url: ses?.avatar_url, + html_url: ses?.html_url, title: "Docs", description: "Docs", destination: "/docs", diff --git a/modules/group/group.service.test.ts b/modules/group/group.service.test.ts index ba0477aad..d250c8c67 100644 --- a/modules/group/group.service.test.ts +++ b/modules/group/group.service.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "@app/http/server/deps.ts"; +import { assertEquals } from "@app/core/server/deps.ts"; import { createGroup, deleteGroup, diff --git a/modules/index/index.layout.tsx b/modules/index/index.layout.tsx index f74415c34..429a05a4b 100644 --- a/modules/index/index.layout.tsx +++ b/modules/index/index.layout.tsx @@ -1,4 +1,4 @@ -import { LayoutProps } from "@app/http/server/types.ts"; +import { LayoutProps } from "../../core/server/types.ts"; export default function index( { data, children }: LayoutProps< diff --git a/modules/index/index.page.tsx b/modules/index/index.page.tsx index 4a087b3de..ef1581737 100644 --- a/modules/index/index.page.tsx +++ b/modules/index/index.page.tsx @@ -1,4 +1,4 @@ -import { PageProps } from "@app/http/server/types.ts"; +import { PageProps } from "../../core/server/types.ts"; import { Footer } from "@app/components/footer.tsx"; import Header from "@app/components/header.tsx"; import Launchpad from "@app/modules/index/index.launchpad.tsx"; diff --git a/modules/index/mod.ts b/modules/index/mod.ts index f355f2c35..d7c107f3c 100644 --- a/modules/index/mod.ts +++ b/modules/index/mod.ts @@ -1,7 +1,7 @@ import { Fastro, HttpRequest } from "@app/mod.ts"; import indexApp from "@app/modules/index/index.page.tsx"; import index from "@app/modules/index/index.layout.tsx"; -import { getSessionId } from "@app/modules/auth/mod.tsx"; +import { getSession } from "@app/utils/session.ts"; function init() { const basePath = Deno.env.get("DENO_DEPLOYMENT_ID") @@ -35,19 +35,7 @@ export default function (s: Fastro) { const res = denoRunCheck(req); if (res) return init(); - const sessionId = await getSessionId(req); - const hasSessionIdCookie = sessionId !== undefined; - const isLogin = hasSessionIdCookie; - let avatar_url = ""; - let html_url = ""; - if (sessionId) { - const r = ctx.server.serverOptions[sessionId]; - if (r) { - avatar_url = r.avatar_url; - html_url = r.html_url; - } - } - + const ses = await getSession(req, ctx); return ctx.render({ title: "Fast & Modular Web Framework", description: @@ -61,9 +49,9 @@ export default function (s: Fastro) { : "https://fastro.dev", new: "Collaboration and Profit Sharing", destination: "blog/collaboration", - isLogin, - avatar_url, - html_url, + isLogin: ses?.isLogin, + avatar_url: ses?.avatar_url, + html_url: ses?.html_url, }); }, }); diff --git a/modules/markdown/mod.tsx b/modules/markdown/mod.tsx index 7d03487ff..f1355623f 100644 --- a/modules/markdown/mod.tsx +++ b/modules/markdown/mod.tsx @@ -3,8 +3,7 @@ import { defaultLayout, getMarkdownBody, } from "@app/middleware/markdown/mod.tsx"; -import { getSessionId } from "@app/modules/auth/mod.tsx"; -import { kv } from "@app/utils/db.ts"; +import { getSession } from "@app/utils/session.ts"; /** * @param layout @@ -18,31 +17,18 @@ export default function ( prefix: string, ) { return async function middleware(req: HttpRequest, ctx: Context) { - const sessionId = await getSessionId(req); - const hasSessionIdCookie = sessionId !== undefined; - const isLogin = hasSessionIdCookie; - let avatar_url = ""; - let html_url = ""; - if (sessionId) { - // deno-lint-ignore no-explicit-any - const r = await kv.get([sessionId]) as any; - if (r && r.value) { - avatar_url = r.value.avatar_url; - html_url = r.value.html_url; - } - } - const data = { - avatar_url, - html_url, - isLogin, - }; + const ses = await getSession(req, ctx); const body = await getMarkdownBody( req, layout, folder, ctx.url.pathname, prefix, - data, + { + avatar_url: ses?.avatar_url, + html_url: ses?.html_url, + isLogin: ses?.isLogin, + }, ); if (!body) return ctx.next(); return new Response(body, { diff --git a/modules/permission/permission.test.ts b/modules/permission/permission.test.ts index 821e768b9..9a7f850bd 100644 --- a/modules/permission/permission.test.ts +++ b/modules/permission/permission.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "@app/http/server/deps.ts"; +import { assertEquals } from "@app/core/server/deps.ts"; import { createPermission, deletePermission, diff --git a/modules/toc/toc.layout.tsx b/modules/toc/toc.layout.tsx index ab23e1d2f..19ecc14a8 100644 --- a/modules/toc/toc.layout.tsx +++ b/modules/toc/toc.layout.tsx @@ -1,6 +1,6 @@ // import { InlineNav } from "@app/components/inline-nav.tsx"; import { Footer } from "@app/components/footer.tsx"; -import { LayoutProps } from "@app/http/server/types.ts"; +import { LayoutProps } from "../../core/server/types.ts"; import Header from "@app/components/header.tsx"; export default function ({ children, data }: LayoutProps< diff --git a/modules/toc/toc.page.tsx b/modules/toc/toc.page.tsx index 639b9ef9b..3d75198f0 100644 --- a/modules/toc/toc.page.tsx +++ b/modules/toc/toc.page.tsx @@ -1,4 +1,4 @@ -import { PageProps } from "@app/http/server/types.ts"; +import { PageProps } from "../../core/server/types.ts"; type Post = { title: string; url: string; date: string }; diff --git a/modules/user/user.service.test.ts b/modules/user/user.service.test.ts index c929c1b5a..e2050d718 100644 --- a/modules/user/user.service.test.ts +++ b/modules/user/user.service.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "@app/http/server/deps.ts"; +import { assertEquals } from "@app/core/server/deps.ts"; import { createUser, deleteUser, diff --git a/modules/web/app.layout.tsx b/modules/web/app.layout.tsx index bae3272ce..bd9dc3682 100644 --- a/modules/web/app.layout.tsx +++ b/modules/web/app.layout.tsx @@ -1,4 +1,4 @@ -import { LayoutProps } from "@app/http/server/types.ts"; +import { LayoutProps } from "@app/core/server/types.ts"; export default function layout( { data, children }: LayoutProps< diff --git a/modules/web/dear.page.tsx b/modules/web/dear.page.tsx index 1dc3412d2..7d3d0f186 100644 --- a/modules/web/dear.page.tsx +++ b/modules/web/dear.page.tsx @@ -1,4 +1,4 @@ -import { PageProps } from "@app/http/server/types.ts"; +import { PageProps } from "../../core/server/types.ts"; export default function Dear( { data }: PageProps<{ data: string; user: string; title: string }>, diff --git a/utils/session.ts b/utils/session.ts new file mode 100644 index 000000000..ee44c299f --- /dev/null +++ b/utils/session.ts @@ -0,0 +1,15 @@ +// deno-lint-ignore-file no-explicit-any +import { getSessionId } from "@app/modules/auth/mod.tsx"; +import { Context, HttpRequest } from "@app/mod.ts"; + +export async function getSession(req: HttpRequest, ctx: Context) { + const sessionId = await getSessionId(req); + if (!sessionId) return undefined; + + const r = ctx.server.store.get(sessionId) as any; + const avatar_url = r ? r.avatar_url : null; + const html_url = r ? r.html_url : null; + const isLogin = r ? true : false; + + return { isLogin, avatar_url, html_url }; +}