Compare commits
3 Commits
ef98263ba0
...
3d30f55e72
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d30f55e72 | |||
| bd2c535cee | |||
| ef688a21b7 |
@ -1,27 +1,27 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig, envField } from 'astro/config';
|
import { defineConfig, envField } from 'astro/config';
|
||||||
import { modifiedTime } from './src/utils/lastModified.mjs';
|
import { modifiedTime } from './src/utils/lastModified.mjs';
|
||||||
|
import db from "@astrojs/db";
|
||||||
import mdx from "@astrojs/mdx";
|
import mdx from "@astrojs/mdx";
|
||||||
import node from "@astrojs/node";
|
import node from "@astrojs/node";
|
||||||
import devOnlyRoutes from '@fujocoded/astro-dev-only';
|
import devOnlyRoutes from '@fujocoded/astro-dev-only';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://haetae.32-b.it",
|
site: "http://haetae.32-b.it",
|
||||||
markdown: {
|
markdown: {
|
||||||
remarkPlugins: [modifiedTime],
|
remarkPlugins: [modifiedTime],
|
||||||
smartypants: false,
|
// smartypants: false,
|
||||||
},
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
mdx(),
|
mdx(),
|
||||||
|
db(),
|
||||||
devOnlyRoutes({
|
devOnlyRoutes({
|
||||||
// dryRun: true,
|
// dryRun: true,
|
||||||
routePatterns: ["/guestbook/admin"]
|
routePatterns: ["/guestbook/admin"]
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
adapter: node({
|
adapter: node({ mode: "standalone" }),
|
||||||
mode: "standalone",
|
|
||||||
}),
|
|
||||||
env: {
|
env: {
|
||||||
schema: {
|
schema: {
|
||||||
ASTRO_DB_REMOTE_URL: envField.string({ context: "server", access: "secret" }),
|
ASTRO_DB_REMOTE_URL: envField.string({ context: "server", access: "secret" }),
|
||||||
|
|||||||
19
db/config.ts
Normal file
19
db/config.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { column, defineDb, defineTable, NOW } from 'astro:db';
|
||||||
|
|
||||||
|
const Guestbook = defineTable({
|
||||||
|
columns: {
|
||||||
|
id: column.number({ primaryKey: true }),
|
||||||
|
username: column.text(),
|
||||||
|
website: column.text({ optional: true }),
|
||||||
|
message: column.text(),
|
||||||
|
published: column.date({ default: NOW }),
|
||||||
|
updated: column.date({ optional: true }),
|
||||||
|
reply: column.text({ optional: true }),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default defineDb({
|
||||||
|
tables: {
|
||||||
|
Guestbook,
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { sql } from "drizzle-orm";
|
import { sql, type InferSelectModel } from "drizzle-orm";
|
||||||
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
export const guestbookTable = sqliteTable("guestbook_table", {
|
export const guestbookTable = sqliteTable("guestbook_table", {
|
||||||
@ -9,4 +9,6 @@ export const guestbookTable = sqliteTable("guestbook_table", {
|
|||||||
published: text().notNull().default(sql`(CURRENT_TIMESTAMP)`),
|
published: text().notNull().default(sql`(CURRENT_TIMESTAMP)`),
|
||||||
updated: text().$onUpdate(() => sql`(CURRENT_TIMESTAMP)`),
|
updated: text().$onUpdate(() => sql`(CURRENT_TIMESTAMP)`),
|
||||||
reply: text(),
|
reply: text(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type GuestbookEntry = typeof guestbookTable.$inferSelect;
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { db } from "db";
|
|
||||||
import { guestbookTable } from "./schema";
|
|
||||||
|
|
||||||
export default async function seed() {
|
|
||||||
await db.insert(guestbookTable).values([
|
|
||||||
{ id: 1, username: "test user", message: "this is a message!" },
|
|
||||||
{ id: 2, username: "heylo", website: "https://world.org", message: "hiii!!" },
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { loadEnv } from "vite";
|
|
||||||
import { defineConfig } from 'drizzle-kit';
|
|
||||||
|
|
||||||
const { ASTRO_DB_REMOTE_URL } = loadEnv(process.env.NODE_ENV!, process.cwd(), "");
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
out: "./db/migrations",
|
|
||||||
schema: "./db/schema.ts",
|
|
||||||
dialect: "turso",
|
|
||||||
dbCredentials: {
|
|
||||||
url: ASTRO_DB_REMOTE_URL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
BIN
guestbook.db
BIN
guestbook.db
Binary file not shown.
20
package.json
20
package.json
@ -9,20 +9,18 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/mdx": "^4.3.6",
|
"@astrojs/db": "^0.18.3",
|
||||||
"@astrojs/node": "9.4.4",
|
"@astrojs/mdx": "^4.3.12",
|
||||||
"@astrojs/rss": "4.0.12",
|
"@astrojs/node": "9.5.1",
|
||||||
|
"@astrojs/rss": "4.0.14",
|
||||||
"@fujocoded/astro-dev-only": "0.0.4",
|
"@fujocoded/astro-dev-only": "0.0.4",
|
||||||
"@libsql/client": "^0.15.15",
|
"astro": "5.16.0",
|
||||||
"astro": "5.13.10",
|
|
||||||
"astro-breadcrumbs": "^3.3.1",
|
"astro-breadcrumbs": "^3.3.1",
|
||||||
"dayjs": "^1.11.18",
|
"dayjs": "^1.11.19",
|
||||||
"drizzle-orm": "^0.44.5",
|
"isomorphic-dompurify": "^2.33.0",
|
||||||
"isomorphic-dompurify": "^2.28.0",
|
"marked": "^16.4.2"
|
||||||
"marked": "^16.3.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.18.6",
|
"@types/node": "^22.19.1"
|
||||||
"drizzle-kit": "^0.31.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1808
pnpm-lock.yaml
generated
1808
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,8 @@
|
|||||||
|
nodeLinker: hoisted
|
||||||
|
|
||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
- esbuild
|
- esbuild
|
||||||
- sharp
|
- sharp
|
||||||
|
|
||||||
nodeLinker: hoisted
|
overrides:
|
||||||
|
esbuild@<=0.24.2: '>=0.25.0'
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
export default {
|
export default {
|
||||||
host: {
|
host: {
|
||||||
host: "137.184.37.162",
|
host: "15.235.122.159",
|
||||||
username: "haetae",
|
username: "haetae",
|
||||||
remoteProjectPath: "/var/www/fujohost/haetae/",
|
remoteProjectPath: "/var/www/",
|
||||||
remoteDbPath: "/var/www/fujohost/haetae/guestbook.db",
|
remoteDbPath: "/var/www/fujohost/haetae/guestbook.db",
|
||||||
dbDriver: "astro:db",
|
dbDriver: "astro:db",
|
||||||
privateKeyPath: "/home/ayowaddup/.ssh/shippy.id_ed25519"
|
privateKeyPath: "/home/ayowaddup/.ssh/shippy.id_ed25519"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { defineAction } from "astro:actions";
|
import { defineAction } from "astro:actions";
|
||||||
import { z } from "astro:content";
|
import { z } from "astro:schema";
|
||||||
import DOMPurify from "isomorphic-dompurify";
|
import DOMPurify from "isomorphic-dompurify";
|
||||||
|
|
||||||
export const contact = {
|
export const contact = {
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { ActionError, defineAction } from "astro:actions";
|
import { ActionError, defineAction } from "astro:actions";
|
||||||
import { z } from "astro:content";
|
import { z } from "astro:schema";
|
||||||
import { db } from "db";
|
import { db, eq, Guestbook } from "astro:db";
|
||||||
import { guestbookTable } from "db/schema";
|
// import { guestbookTable } from "db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import DOMPurify from "isomorphic-dompurify";
|
import DOMPurify from "isomorphic-dompurify";
|
||||||
|
|
||||||
export const guestbook = {
|
export const guestbook = {
|
||||||
@ -26,7 +25,7 @@ export const guestbook = {
|
|||||||
const sanitized = DOMPurify.sanitize(addLine);
|
const sanitized = DOMPurify.sanitize(addLine);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const entry = await db.insert(guestbookTable).values({
|
const entry = await db.insert(Guestbook).values({
|
||||||
username,
|
username,
|
||||||
website,
|
website,
|
||||||
message: sanitized,
|
message: sanitized,
|
||||||
@ -50,7 +49,7 @@ export const guestbook = {
|
|||||||
throw new ActionError({ code: "UNAUTHORIZED" });
|
throw new ActionError({ code: "UNAUTHORIZED" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await db.select().from(guestbookTable).where(eq(guestbookTable.id, id));
|
const entry = await db.select().from(Guestbook).where(eq(Guestbook.id, id));
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw new ActionError({
|
throw new ActionError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
@ -62,10 +61,10 @@ export const guestbook = {
|
|||||||
const sanitized = DOMPurify.sanitize(addLine);
|
const sanitized = DOMPurify.sanitize(addLine);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const update = await db.update(guestbookTable).set({
|
const update = await db.update(Guestbook).set({
|
||||||
reply: sanitized,
|
reply: sanitized,
|
||||||
updated: new Date().toDateString(),
|
updated: new Date(),
|
||||||
}).where(eq(guestbookTable.id, id)).returning();
|
}).where(eq(Guestbook.id, id)).returning();
|
||||||
|
|
||||||
return update[0];
|
return update[0];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -83,7 +82,7 @@ export const guestbook = {
|
|||||||
throw new ActionError({ code: "UNAUTHORIZED" });
|
throw new ActionError({ code: "UNAUTHORIZED" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await db.select().from(guestbookTable).where(eq(guestbookTable.id, id));
|
const entry = await db.select().from(Guestbook).where(eq(Guestbook.id, id));
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw new ActionError({
|
throw new ActionError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
@ -92,7 +91,7 @@ export const guestbook = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const entry = await db.delete(guestbookTable).where(eq(guestbookTable.id, id)).returning();
|
const entry = await db.delete(Guestbook).where(eq(Guestbook.id, id)).returning();
|
||||||
|
|
||||||
return entry[0];
|
return entry[0];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
*, *::before, *::after { box-sizing: border-box; }
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
* { margin: 0; }
|
* { margin: 0; }
|
||||||
|
|
||||||
html, body {
|
body {
|
||||||
height: 100vh;
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
|
||||||
|
@supports (min-height: 100svh) {
|
||||||
|
min-height: 100svh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body { line-height: calc(1em + 0.5rem); }
|
body { line-height: calc(1em + 0.5rem); }
|
||||||
@ -15,5 +19,5 @@ img, picture, video, canvas, svg {
|
|||||||
|
|
||||||
input, button, textarea, select { font: inherit; }
|
input, button, textarea, select { font: inherit; }
|
||||||
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
|
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
|
||||||
p { text-wrap: pretty; }
|
h1, h2, h3, h4, h5, h6 { text-wrap: balance; }
|
||||||
h1, h2, h3, h4, h5, h6 { text-wrap: balance; }
|
p { text-wrap: pretty; }
|
||||||
@ -1,11 +1,14 @@
|
|||||||
---
|
---
|
||||||
import { desc } from "drizzle-orm";
|
import type { GuestbookEntry } from "db/schema";
|
||||||
import { db } from "db";
|
|
||||||
import { guestbookTable } from "db/schema";
|
|
||||||
import formatDate from "@/utils/formatDate";
|
import formatDate from "@/utils/formatDate";
|
||||||
|
import speech from "$/speech.png";
|
||||||
import pikachu from "$/images/portrait-0025.png";
|
import pikachu from "$/images/portrait-0025.png";
|
||||||
|
|
||||||
const entries = await db.select().from(guestbookTable).orderBy(desc(guestbookTable.published));
|
interface Props {
|
||||||
|
entries: GuestbookEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { entries } = Astro.props;
|
||||||
---
|
---
|
||||||
<section id="entries">
|
<section id="entries">
|
||||||
{entries.map(entry => (
|
{entries.map(entry => (
|
||||||
@ -29,21 +32,23 @@ const entries = await db.select().from(guestbookTable).orderBy(desc(guestbookTab
|
|||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{entry.reply && <article class="reply" id={`reply-${entry.username}-${entry.id}`}>
|
{entry.reply && (
|
||||||
<img src={pikachu.src} width="80" height="80" alt="a portrait of pikachu" />
|
<article class="reply" id={`reply-${entry.username}-${entry.id}`}>
|
||||||
<div class="entry">
|
<img src={pikachu.src} width="80" height="80" alt="a portrait of pikachu" />
|
||||||
<header>
|
<div class="entry">
|
||||||
<h1>Reply to {entry.username}</h1>
|
<header>
|
||||||
<time datetime={entry.updated}>
|
<h1>Reply to {entry.username}</h1>
|
||||||
{formatDate(entry.updated!, false, 'MMMM D, YYYY')}
|
<time datetime={entry.updated}>
|
||||||
</time>
|
{formatDate(entry.updated!, false, 'MMMM D, YYYY')}
|
||||||
</header>
|
</time>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Fragment set:html={entry.reply} />
|
<Fragment set:html={entry.reply} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</article>}
|
)}
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@ -53,9 +58,16 @@ const entries = await db.select().from(guestbookTable).orderBy(desc(guestbookTab
|
|||||||
<p>There's nothing here! Want to be the first to comment?</p>
|
<p>There's nothing here! Want to be the first to comment?</p>
|
||||||
</article>
|
</article>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<slot name="pagination" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style define:vars={{ border: `url(${speech.src})` }}>
|
||||||
|
:root {
|
||||||
|
--speech-bg-color: #f8f8f8;
|
||||||
|
--speech-fg-color: #404040;
|
||||||
|
}
|
||||||
|
|
||||||
#entries {
|
#entries {
|
||||||
margin: 2rem 0 3rem;
|
margin: 2rem 0 3rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { getCollection } from "astro:content";
|
|||||||
import UpdateCard from "./UpdateCard.astro";
|
import UpdateCard from "./UpdateCard.astro";
|
||||||
|
|
||||||
const updates = await getCollection("updates");
|
const updates = await getCollection("updates");
|
||||||
|
const data = updates.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||||
---
|
---
|
||||||
<section id="updates">
|
<section id="updates">
|
||||||
<header>
|
<header>
|
||||||
@ -18,9 +19,12 @@ const updates = await getCollection("updates");
|
|||||||
<span class="segment">Date</span>
|
<span class="segment">Date</span>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
|
<!--
|
||||||
|
<UpdateCard title={data[0].data.title} date={data[0].data.date}>
|
||||||
|
{data[0].body}
|
||||||
|
</UpdateCard> -->
|
||||||
|
|
||||||
<UpdateCard title="redo gallery and guestbook" date="2024-05-10">
|
<UpdateCard title="redo gallery and guestbook" date="2024-05-10">
|
||||||
{updates.length}
|
|
||||||
<p>redid <a href="/gallery">gallery page</a> to make adding images easier and added alpinejs to <a href="/guestbook">guestbook</a> for fetching entries live</p>
|
<p>redid <a href="/gallery">gallery page</a> to make adding images easier and added alpinejs to <a href="/guestbook">guestbook</a> for fetching entries live</p>
|
||||||
</UpdateCard>
|
</UpdateCard>
|
||||||
|
|
||||||
@ -29,14 +33,14 @@ const updates = await getCollection("updates");
|
|||||||
</UpdateCard>
|
</UpdateCard>
|
||||||
|
|
||||||
<footer id="updates-pagination">
|
<footer id="updates-pagination">
|
||||||
<a id="previous" aria-label="go to previous post" href="javascript:void(0)">
|
<button id="previous" aria-label="go to previous post">
|
||||||
<div class="arrow" />
|
<div class="arrow" />
|
||||||
<div class="title">prev</div>
|
<div class="title">prev</div>
|
||||||
</a>
|
</button>
|
||||||
<a id="next" aria-label="go to next post" href="javascript:void(0)">
|
<button id="next" aria-label="go to next post">
|
||||||
<div class="title">next</div>
|
<div class="title">next</div>
|
||||||
<div class="arrow" />
|
<div class="arrow" />
|
||||||
</a>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -146,12 +150,16 @@ const updates = await getCollection("updates");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* BASE STYLING FOR ARROW BUTTONS */
|
/* BASE STYLING FOR ARROW BUTTONS */
|
||||||
a {
|
button {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
color: var(--normal-color);
|
color: var(--normal-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
background: unset;
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
font-family: ;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -193,4 +201,9 @@ const updates = await getCollection("updates");
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- <script>
|
||||||
|
const prev = document.getElementById("previous");
|
||||||
|
const next = document.getElementById("next");
|
||||||
|
</script> -->
|
||||||
@ -40,7 +40,7 @@ const links = [
|
|||||||
title={chapter.data.title}
|
title={chapter.data.title}
|
||||||
ficTitle={fic[0].data.title}
|
ficTitle={fic[0].data.title}
|
||||||
date={chapter.data.publishedAt}
|
date={chapter.data.publishedAt}
|
||||||
{lastModified}
|
{lastModified}
|
||||||
notes={chapter.data.notes}
|
notes={chapter.data.notes}
|
||||||
>
|
>
|
||||||
<Fragment slot="breadcrumbs">
|
<Fragment slot="breadcrumbs">
|
||||||
@ -53,19 +53,20 @@ const links = [
|
|||||||
|
|
||||||
{chapters.length > 1 && (
|
{chapters.length > 1 && (
|
||||||
<nav id="chapter-pagination" slot="pagination">
|
<nav id="chapter-pagination" slot="pagination">
|
||||||
<div id="chapter-index">
|
<form id="chapter-index">
|
||||||
<label for="chapter-select">Chapters:</label>
|
<label for="chapter-select">Chapters:</label>
|
||||||
<select name="chapter-select" id="chapter-select">
|
<select name="chapter-select" id="chapter-select">
|
||||||
{chapters.map(chapter => (
|
{chapters.map(chapter => (
|
||||||
<option
|
<option
|
||||||
value={`/fics/${chapter.id}`}
|
value={`/fics/${chapter.id}`}
|
||||||
selected={chapter.id.split("/")[1] === chapterId ? "selected" : undefined}
|
selected={chapter.id.split("/")[1] === chapterId ? true : undefined}
|
||||||
>
|
>
|
||||||
{chapter.data.title}
|
{chapter.data.title}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
<button>Go to chapter</button>
|
||||||
|
</form>
|
||||||
{previous && <a id="previous" href={`/fics/${previous.id}`}>{previous.data.title}</a>}
|
{previous && <a id="previous" href={`/fics/${previous.id}`}>{previous.data.title}</a>}
|
||||||
{next && <a id="next" href={`/fics/${next.id}`}>{next.data.title}</a>}
|
{next && <a id="next" href={`/fics/${next.id}`}>{next.data.title}</a>}
|
||||||
</nav>
|
</nav>
|
||||||
@ -104,7 +105,7 @@ const links = [
|
|||||||
|
|
||||||
#chapter-index {
|
#chapter-index {
|
||||||
grid-area: 1 / 1 / 1 / -1;
|
grid-area: 1 / 1 / 1 / -1;
|
||||||
width: min-content;
|
/* width: min-content; */
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +131,10 @@ const links = [
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const form = document.forms.namedItem("chapter-index");
|
||||||
const select: HTMLSelectElement | null = document.querySelector("#chapter-select");
|
const select: HTMLSelectElement | null = document.querySelector("#chapter-select");
|
||||||
select?.addEventListener("change", (e) => {
|
form?.addEventListener("submit", (e) => {
|
||||||
if (e.target instanceof HTMLSelectElement) {
|
e.preventDefault();
|
||||||
window.location.href = e.target.value;
|
window.location.href = select?.value!;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -24,7 +24,7 @@ const { Content } = await render(fic);
|
|||||||
|
|
||||||
const parser = marked.use({ gfm: true, breaks: true });
|
const parser = marked.use({ gfm: true, breaks: true });
|
||||||
const summary = await parser.parse(fic.data.summary);
|
const summary = await parser.parse(fic.data.summary);
|
||||||
const lastMod = fic.rendered && (fic.rendered.metadata!.frontmatter as any)['lastModified'];
|
const lastModified = fic.rendered && (fic.rendered.metadata!.frontmatter as any)['lastModified'];
|
||||||
const notes = fic.rendered && await parser.parse((fic.rendered.metadata!.frontmatter as any)["notes"]);
|
const notes = fic.rendered && await parser.parse((fic.rendered.metadata!.frontmatter as any)["notes"]);
|
||||||
---
|
---
|
||||||
<Layout title={fic.data.title}>
|
<Layout title={fic.data.title}>
|
||||||
@ -78,7 +78,7 @@ const notes = fic.rendered && await parser.parse((fic.rendered.metadata!.frontma
|
|||||||
|
|
||||||
{fic.body && (
|
{fic.body && (
|
||||||
<section id="oneshot">
|
<section id="oneshot">
|
||||||
<ChapterContent lastModified={lastMod} notes={notes}>
|
<ChapterContent lastModified={lastModified} notes={notes}>
|
||||||
<Content />
|
<Content />
|
||||||
</ChapterContent>
|
</ChapterContent>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export const GET: APIRoute = async (context) => {
|
|||||||
for (const entry of chapters) {
|
for (const entry of chapters) {
|
||||||
const { Content } = await render(entry);
|
const { Content } = await render(entry);
|
||||||
const content = await container.renderToString(Content);
|
const content = await container.renderToString(Content);
|
||||||
|
|
||||||
feed.push({
|
feed.push({
|
||||||
link: `/fics/${entry.id}`,
|
link: `/fics/${entry.id}`,
|
||||||
title: entry.data.title,
|
title: entry.data.title,
|
||||||
|
|||||||
33
src/pages/guestbook/[page].astro
Normal file
33
src/pages/guestbook/[page].astro
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
import type { GetStaticPaths } from "astro";
|
||||||
|
import { Font } from "astro:assets";
|
||||||
|
import { desc } from "drizzle-orm";
|
||||||
|
import { db } from "db";
|
||||||
|
import { guestbookTable } from "db/schema";
|
||||||
|
|
||||||
|
import Layout from "@/layouts/Layout.astro";
|
||||||
|
import Entries from "~/Entries.astro";
|
||||||
|
|
||||||
|
export const getStaticPaths = (async ({ paginate }) => {
|
||||||
|
const data = await db.select().from(guestbookTable).orderBy(desc(guestbookTable.published));
|
||||||
|
return paginate(data, { pageSize: 1 });
|
||||||
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
|
const { page } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<Fragment slot="head">
|
||||||
|
<Font cssVariable="--mono" preload />
|
||||||
|
<Font cssVariable="--mlss" preload />
|
||||||
|
</Fragment>
|
||||||
|
<Entries entries={page.data} server:defer>
|
||||||
|
<nav slot="pagination">
|
||||||
|
{page.currentPage}
|
||||||
|
{page.url.first ? <a href={page.url.first}>First</a> : null}
|
||||||
|
{page.url.prev ? <a href={page.url.prev}>Previous</a> : null}
|
||||||
|
{page.url.next ? <a href={page.url.next}>Next</a> : null}
|
||||||
|
{page.url.last ? <a href={page.url.last}>Last</a> : null}
|
||||||
|
</nav>
|
||||||
|
</Entries>
|
||||||
|
</Layout>
|
||||||
@ -1,13 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { actions, isInputError } from "astro:actions";
|
import { actions, isInputError } from "astro:actions";
|
||||||
import { Font } from "astro:assets";
|
import { Font } from "astro:assets";
|
||||||
|
import { desc } from "drizzle-orm";
|
||||||
|
import { db } from "db";
|
||||||
|
import { guestbookTable } from "db/schema";
|
||||||
|
|
||||||
import Layout from "@/layouts/Layout.astro";
|
import Layout from "@/layouts/Layout.astro";
|
||||||
import speech from "$/speech.png";
|
|
||||||
import Entries from "~/Entries.astro";
|
import Entries from "~/Entries.astro";
|
||||||
import Dialog from "~/Dialog.astro";
|
import Dialog from "~/Dialog.astro";
|
||||||
|
|
||||||
const result = Astro.getActionResult(actions.guestbook.addEntry);
|
const result = Astro.getActionResult(actions.guestbook.addEntry);
|
||||||
|
const entries = await db.select().from(guestbookTable).orderBy(desc(guestbookTable.published));
|
||||||
|
entries.slice(0, 10);
|
||||||
const inputErrors = isInputError(result?.error) ? result.error.fields : {};
|
const inputErrors = isInputError(result?.error) ? result.error.fields : {};
|
||||||
---
|
---
|
||||||
<Layout title="haetae, guestbook">
|
<Layout title="haetae, guestbook">
|
||||||
@ -42,7 +46,7 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {};
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Entries server:defer>
|
<Entries entries={entries} server:defer>
|
||||||
<p slot="fallback">Loading...</p>
|
<p slot="fallback">Loading...</p>
|
||||||
</Entries>
|
</Entries>
|
||||||
|
|
||||||
@ -52,12 +56,7 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {};
|
|||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style define:vars={{ border: `url(${speech.src})` }}>
|
<style>
|
||||||
:root {
|
|
||||||
--speech-bg-color: #f8f8f8;
|
|
||||||
--speech-fg-color: #404040;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
max-width: clamp(75ch, 80ch, 100%);
|
max-width: clamp(75ch, 80ch, 100%);
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
|
|||||||
@ -1,15 +1,9 @@
|
|||||||
import { statSync } from "fs";
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
export function modifiedTime() {
|
export function modifiedTime() {
|
||||||
return function (_tree, file) {
|
return function (_tree, { data, history }) {
|
||||||
const path = file.history[0];
|
const path = history[0];
|
||||||
const result = statSync(path);
|
const result = execSync(`git log -1 --pretty="format:%cI" "${path}"`);
|
||||||
file.data.astro.frontmatter.lastModified = result.mtime;
|
data.astro.frontmatter.lastModified = result.toString();
|
||||||
// try {
|
|
||||||
// const result = statSync(path);
|
|
||||||
// file.data.astro.frontmatter.lastModified = result.mtime;
|
|
||||||
// } catch (error) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user