who needs allat. maybe i want to live simply
This commit is contained in:
		
							parent
							
								
									db51f003bb
								
							
						
					
					
						commit
						776fc1b38e
					
				| @ -17,12 +17,6 @@ export default defineConfig({ | ||||
|   adapter: node({ | ||||
|     mode: "standalone", | ||||
|   }), | ||||
|   session: { | ||||
|     driver: "localstorage", | ||||
|     options: { | ||||
|       base: "app:", | ||||
|     }, | ||||
|   }, | ||||
|   experimental: { | ||||
|     fonts: [ | ||||
|       { | ||||
|  | ||||
							
								
								
									
										14
									
								
								db/config.ts
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								db/config.ts
									
									
									
									
									
								
							| @ -12,19 +12,7 @@ const Guestbook = defineTable({ | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const Comment = defineTable({ | ||||
|   columns: { | ||||
|     id: column.number({ primaryKey: true }), | ||||
|     postId: column.text(), | ||||
|     replyId: column.number(), | ||||
|     username: column.text(), | ||||
|     website: column.text({ optional: true }), | ||||
|     comment: column.text({ multiline: true }), | ||||
|     published: column.date({ default: NOW }), | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| // https://astro.build/db/config
 | ||||
| export default defineDb({ | ||||
|   tables: { Guestbook, Comment }, | ||||
|   tables: { Guestbook }, | ||||
| }); | ||||
|  | ||||
| @ -1,21 +0,0 @@ | ||||
| import { defineAction } from "astro:actions"; | ||||
| import { z } from "astro:content"; | ||||
| import sanitize from "sanitize-html"; | ||||
| 
 | ||||
| export const comments = { | ||||
|   addComment: defineAction({ | ||||
|     accept: "form", | ||||
|     input: z.object({ | ||||
|       postId: z.string(), | ||||
|       replyId: z.number().optional(), | ||||
|       name: z.string(), | ||||
|       website: z.string().url().optional(), | ||||
|       comment: z.string(), | ||||
|     }), | ||||
|     handler: async (input) => { | ||||
|       // sanitize but allow line breaks
 | ||||
|       sanitize(input.comment); | ||||
|       // post to comment server
 | ||||
|     }, | ||||
|   }), | ||||
| }; | ||||
| @ -1,7 +1,6 @@ | ||||
| import { ActionError, defineAction } from "astro:actions"; | ||||
| import { z } from "astro:content"; | ||||
| import { db, eq, Guestbook } from "astro:db"; | ||||
| import bcrypt from "bcryptjs"; | ||||
| import sanitize from "sanitize-html"; | ||||
| 
 | ||||
| export const guestbook = { | ||||
| @ -14,6 +13,8 @@ export const guestbook = { | ||||
|     }), | ||||
|     handler: async ({ username, website, message }) => { | ||||
|       // figure out how to add line breaks and THEN sanitize message
 | ||||
|       const addLine = message.replaceAll("/n", "<br/>"); | ||||
|       sanitize(addLine); | ||||
| 
 | ||||
|       const entry = await db.insert(Guestbook).values({ | ||||
|         username, | ||||
| @ -31,7 +32,7 @@ export const guestbook = { | ||||
|       reply: z.string(), | ||||
|     }), | ||||
|     handler: async ({ id, reply }, context) => { | ||||
|       if (!context.session?.get("pwd")) { | ||||
|       if (context.url.hostname !== "127.0.0.1" || "localhost") { | ||||
|         throw new ActionError({ code: "UNAUTHORIZED" }); | ||||
|       } | ||||
| 
 | ||||
| @ -44,32 +45,9 @@ export const guestbook = { | ||||
|       } | ||||
|        | ||||
|       // sanitize reply here
 | ||||
| 
 | ||||
|        | ||||
|       const update = await db.update(Guestbook).set({ reply }).where(eq(Guestbook.id, id)).returning(); | ||||
|       return update[0]; | ||||
|     }, | ||||
|   }), | ||||
|   login: defineAction({ | ||||
|     accept: "form", | ||||
|     input: z.object({ | ||||
|       password: z.string(), | ||||
|     }), | ||||
|     handler: async ({ password }, context) => { | ||||
|       // find env var here
 | ||||
|       if (password !== "super secret password") { | ||||
|         throw new ActionError({ code: "UNAUTHORIZED" }); | ||||
|       } | ||||
| 
 | ||||
|       const hash = await bcrypt.hash(password, 10); | ||||
|       context.session?.set("pwd", hash); | ||||
|       return { code: 200, message: "set the thing" }; | ||||
|     } | ||||
|   }), | ||||
|   logout: defineAction({ | ||||
|     accept: "form", | ||||
|     handler: async (_input, context) => { | ||||
|       context.session?.destroy(); | ||||
|       return { code: 200, message: "set the thing" }; | ||||
|     } | ||||
|   }), | ||||
| }; | ||||
| @ -1,9 +1,7 @@ | ||||
| import { comments } from "./comment"; | ||||
| import { contact } from "./contact"; | ||||
| import { guestbook } from "./guestbook"; | ||||
| 
 | ||||
| export const server = { | ||||
|   comments, | ||||
|   contact, | ||||
|   guestbook, | ||||
| }; | ||||
| @ -12,34 +12,6 @@ | ||||
|   --ko-font: GulimChe, DotumChe, Gulim, Dotum, system-ui-ko, system-ui-ja, system-ui-zh-cn, system-ui-zh-tw, system-ui-zh-hk, monospace, sans-serif; | ||||
|   --dotum-11-font: var(--dotumche-11), var(--dotum-11), var(--ko-font); | ||||
|   --dotum-12-font: var(--dotumche-12), var(--dotum-12), var(--ko-font); | ||||
| 
 | ||||
|   @media screen and (prefers-color-scheme: light) { | ||||
|     --bg-color: #e6f2ef; | ||||
|     --fg-color: #151640; | ||||
|     --accent-color: #f783b0; | ||||
|     --secondary-color: #3f6d9e; | ||||
|   } | ||||
| 
 | ||||
|   @media screen and (prefers-color-scheme: dark) { | ||||
|     --bg-color: #555568; | ||||
|     --fg-color: #f3eded; | ||||
|     --accent-color: #eeb9c7; | ||||
|     --secondary-color: #b9eedc; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .light { | ||||
|   --bg-color: #e6f2ef; | ||||
|   --fg-color: #151640; | ||||
|   --accent-color: #f783b0; | ||||
|   --secondary-color: #3f6d9e; | ||||
| } | ||||
| 
 | ||||
| .dark { | ||||
|   --bg-color: #555568; | ||||
|   --fg-color: #f3eded; | ||||
|   --accent-color: #eeb9c7; | ||||
|   --secondary-color: #b9eedc; | ||||
| } | ||||
| 
 | ||||
| body { | ||||
| @ -124,22 +96,4 @@ button, .button { | ||||
|     border-color: var(--bg-color); | ||||
|     box-shadow: 0 0 0 var(--fg-color); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .dark { | ||||
|   button, .button { | ||||
|     background-color: var(--accent-color); | ||||
|     color: var(--bg-color); | ||||
| 
 | ||||
|     &:hover { | ||||
|       background-color: hsl(from var(--accent-color) h s calc(l - 10)); | ||||
|       color: var(--bg-color); | ||||
|     } | ||||
| 
 | ||||
|     &:active, &:focus { | ||||
|       color: var(--bg-color); | ||||
|       background-color: var(--secondary-color); | ||||
|       border-color: var(--bg-color); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/assets/styles/themes.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/assets/styles/themes.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| :root { | ||||
|   @media screen and (prefers-color-scheme: light) { | ||||
|     --bg-color: #e6f2ef; | ||||
|     --fg-color: #151640; | ||||
|     --accent-color: #f783b0; | ||||
|     --secondary-color: #3f6d9e; | ||||
|   } | ||||
| 
 | ||||
|   @media screen and (prefers-color-scheme: dark) { | ||||
|     --bg-color: #555568; | ||||
|     --fg-color: #f3eded; | ||||
|     --accent-color: #eeb9c7; | ||||
|     --secondary-color: #b9eedc; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .light { | ||||
|   --bg-color: #e6f2ef; | ||||
|   --fg-color: #151640; | ||||
|   --accent-color: #f783b0; | ||||
|   --secondary-color: #3f6d9e; | ||||
| } | ||||
| 
 | ||||
| .dark { | ||||
|   --bg-color: #555568; | ||||
|   --fg-color: #f3eded; | ||||
|   --accent-color: #eeb9c7; | ||||
|   --secondary-color: #b9eedc; | ||||
| } | ||||
| 
 | ||||
| /* custom theme styling */ | ||||
| 
 | ||||
| /* dark-specific styles */ | ||||
| .dark { | ||||
|   button, .button { | ||||
|     background-color: var(--accent-color); | ||||
|     color: var(--bg-color); | ||||
| 
 | ||||
|     &:hover { | ||||
|       background-color: hsl(from var(--accent-color) h s calc(l - 10)); | ||||
|       color: var(--bg-color); | ||||
|     } | ||||
| 
 | ||||
|     &:active, &:focus { | ||||
|       color: var(--bg-color); | ||||
|       background-color: var(--secondary-color); | ||||
|       border-color: var(--bg-color); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										3
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | ||||
| interface Window { | ||||
|   Alpine: import("alpinejs").Alpine; | ||||
| } | ||||
| @ -1,5 +1,6 @@ | ||||
| --- | ||||
| import "$/styles/base.css"; | ||||
| import "$/styles/themes.css"; | ||||
| import { Font } from "astro:assets"; | ||||
| 
 | ||||
| interface Props { title?: string; } | ||||
| @ -11,7 +12,6 @@ const { title = "haetae" }: Props = Astro.props; | ||||
| 	<head> | ||||
| 		<meta charset="UTF-8" /> | ||||
| 		<meta name="viewport" content="width=device-width" /> | ||||
| 		<meta name="generator" content={Astro.generator} /> | ||||
| 		<meta name="pinterest" content="nopin nohover" /> | ||||
| 		<meta name="robots" content="noai, noimageai" /> | ||||
| 		<Font cssVariable="--sq" preload /> | ||||
|  | ||||
| @ -3,62 +3,71 @@ export const prerender = false; | ||||
| 
 | ||||
| import { actions } from "astro:actions"; | ||||
| import { db, desc, Guestbook } from "astro:db"; | ||||
| import Layout from "@/layouts/Layout.astro"; | ||||
| import formatDate from "@/utils/formatDate"; | ||||
| 
 | ||||
| const pwd = await Astro.session?.get("pwd"); | ||||
| 
 | ||||
| if (!pwd) { | ||||
|   const fromUrl = Astro.url.pathname + Astro.url.search; | ||||
|   return Astro.redirect(`/guestbook/login?redirect=${fromUrl}`); | ||||
| if (!import.meta.env.DEV) { | ||||
|   console.error("you shouldn't be here..."); | ||||
|   return Astro.redirect("/guestbook"); | ||||
| } | ||||
| 
 | ||||
| const entries = await db.select().from(Guestbook).orderBy(desc(Guestbook.published)); | ||||
| --- | ||||
| <h1>entries</h1> | ||||
| <Layout title="guestbook admin"> | ||||
|   <h1>entries</h1> | ||||
| 
 | ||||
| <section> | ||||
|   <table> | ||||
|     <thead> | ||||
|       <tr> | ||||
|         <th>username</th> | ||||
|         <th>website</th> | ||||
|         <th>message</th> | ||||
|         <th>published</th> | ||||
|         <th>reply</th> | ||||
|         <th>edit</th> | ||||
|       </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|       {entries.map(entry => ( | ||||
|         <tr id={`${entry.id}`}> | ||||
|           <td>{entry.username}</td> | ||||
|           <td>{entry.website}</td> | ||||
|           <td>{entry.message}</td> | ||||
|           <td>{entry.published}</td> | ||||
|           <td>{entry.reply}</td> | ||||
|           <td><button class="edit">edit</button></td> | ||||
|   <section> | ||||
|     <table> | ||||
|       <thead> | ||||
|         <tr> | ||||
|           <th>username</th> | ||||
|           <th>website</th> | ||||
|           <th>message</th> | ||||
|           <th>published</th> | ||||
|           <th>reply</th> | ||||
|           <th>edit</th> | ||||
|         </tr> | ||||
|       ))} | ||||
|     </tbody> | ||||
|   </table> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         {entries.map(entry => ( | ||||
|           <tr id={`${entry.id}`}> | ||||
|             <td>{entry.username}</td> | ||||
|             <td>{entry.website}</td> | ||||
|             <td>{entry.message}</td> | ||||
|             <td>{formatDate(entry.published, false, 'MMMM D, YYYY')}</td> | ||||
|             <td>{entry.reply}</td> | ||||
|             <td><button class="edit">edit</button></td> | ||||
|           </tr> | ||||
|         ))} | ||||
|       </tbody> | ||||
|     </table> | ||||
| 
 | ||||
|   <dialog id="edit-entry"> | ||||
|     <form id="edit-entry-form" action={actions.guestbook.reply} method="post"> | ||||
|       <input type="hidden" name="id" value="" /> | ||||
|       <p id="entry-username"></p> | ||||
|       <p id="entry-website"></p> | ||||
|       <div id="entry-message"></div> | ||||
|       <p id="entry-published"></p> | ||||
|       <label for="reply">Reply</label> | ||||
|       <textarea name="reply" id="reply"></textarea> | ||||
|     <dialog id="edit-entry"> | ||||
|       <form id="edit-entry-form" action={actions.guestbook.reply} method="post"> | ||||
|         <input type="hidden" name="id" id="entryId" value="" /> | ||||
|         <p id="entry-username"></p> | ||||
|         <p id="entry-website"></p> | ||||
|         <div id="entry-message"></div> | ||||
|         <p id="entry-published"></p> | ||||
|         <label for="reply">Reply</label> | ||||
|         <textarea name="reply" id="reply"></textarea> | ||||
| 
 | ||||
|       <button type="submit">Reply</button> | ||||
|     </form> | ||||
|   </dialog> | ||||
| </section> | ||||
|         <button type="submit">Reply</button> | ||||
|       </form> | ||||
|     </dialog> | ||||
|   </section> | ||||
| </Layout> | ||||
| 
 | ||||
| <form action={actions.guestbook.logout} method="post"> | ||||
|   <button type="submit">logout</button> | ||||
| </form> | ||||
| <style> | ||||
|   table { | ||||
|     border-collapse: collapse; | ||||
| 
 | ||||
|     td { | ||||
|       max-width: 30ch; | ||||
|       text-overflow: ellipsis; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| 
 | ||||
| <script> | ||||
|   const modal = document.getElementById("edit-entry") as HTMLDialogElement; | ||||
| @ -70,7 +79,7 @@ const entries = await db.select().from(Guestbook).orderBy(desc(Guestbook.publish | ||||
|       const data = row.cells; | ||||
|       const fields = document.querySelectorAll("[id^='entry-']"); | ||||
| 
 | ||||
|       (document.getElementById("id") as HTMLInputElement).value = id; | ||||
|       (document.getElementById("entryId") as HTMLInputElement).value = id; | ||||
|       fields.forEach((field, i) => { | ||||
|         field.innerHTML = data.item(i)!.innerText; | ||||
|       }); | ||||
|  | ||||
| @ -1,16 +0,0 @@ | ||||
| --- | ||||
| export const prerender = false; | ||||
| 
 | ||||
| import bcrypt from "bcryptjs"; | ||||
| 
 | ||||
| if (Astro.request.method === "POST") { | ||||
|   const form = await Astro.request.formData(); | ||||
|   const pwd = form.get("password"); | ||||
| 
 | ||||
|   if (pwd) { | ||||
|     const hashed = bcrypt.hashSync(pwd.toString(), 10); | ||||
|     Astro.session?.set("pwd", hashed); | ||||
|     return Astro.redirect("/guestbook/admin"); | ||||
|   } | ||||
| } | ||||
| --- | ||||
| @ -30,7 +30,7 @@ export function ficsLoader(loader: Loader) { | ||||
|                 ...valueWithoutDigest.data, | ||||
|                 ...chapters.length > 1 && { chapters: chapters }, | ||||
|               }, | ||||
|             }); | ||||
|             });           | ||||
|             if (chapters.length === 1) { | ||||
|               // i've committed unspeakable atrocities here
 | ||||
|               const search = import.meta.glob<MarkdownInstance<any>>(`../content/fics/**/*.md`, { eager: true }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user