Compare commits
2 commits
f2acf36784
...
c0d1feaacd
| Author | SHA1 | Date | |
|---|---|---|---|
| c0d1feaacd | |||
| 8a9c56c3d5 |
52 changed files with 44 additions and 7205 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
.astro
|
.astro
|
||||||
.vercel
|
data
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
**/.env
|
**/.env
|
||||||
|
CLAUDE.md
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
import vercel from '@astrojs/vercel';
|
import node from '@astrojs/node';
|
||||||
import db from '@astrojs/db';
|
import db from '@astrojs/db';
|
||||||
import auth from 'auth-astro';
|
|
||||||
import remarkDirective from 'remark-directive';
|
import remarkDirective from 'remark-directive';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
import remarkSlug from 'remark-slug';
|
import remarkSlug from 'remark-slug';
|
||||||
|
|
@ -10,8 +9,8 @@ import remarkAside from './src/plugins/remark-aside.ts';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: 'static',
|
output: 'static',
|
||||||
adapter: vercel(),
|
adapter: node({ mode: 'standalone' }),
|
||||||
integrations: [db(), auth()],
|
integrations: [db()],
|
||||||
markdown: {
|
markdown: {
|
||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
remarkGfm,
|
remarkGfm,
|
||||||
14
db/seed.ts
Normal file
14
db/seed.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { db, Guestbook } from 'astro:db';
|
||||||
|
|
||||||
|
export default async function seed() {
|
||||||
|
await db.insert(Guestbook).values([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'test',
|
||||||
|
message: 'hello from dev',
|
||||||
|
url: null,
|
||||||
|
createdAt: new Date(),
|
||||||
|
approved: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
27
package.json
27
package.json
|
|
@ -1,11 +1,24 @@
|
||||||
{
|
{
|
||||||
"name": "ily",
|
"name": "wynne.rs",
|
||||||
"private": true,
|
"type": "module",
|
||||||
"packageManager": "pnpm@10.28.0",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:penfield": "pnpm --filter @ily/penfield dev",
|
"dev": "astro dev --port 4322",
|
||||||
"dev:www": "pnpm --filter @ily/www dev",
|
"build": "ASTRO_DATABASE_FILE=data/guestbook.db astro build && node scripts/generate-stats.js",
|
||||||
"build:penfield": "pnpm --filter @ily/penfield build",
|
"preview": "astro preview"
|
||||||
"build:www": "pnpm --filter @ily/www build"
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/db": "^0.20.1",
|
||||||
|
"@astrojs/node": "^10.0.4",
|
||||||
|
"@astrojs/rss": "^4.0.18",
|
||||||
|
"astro": "^6.1.3",
|
||||||
|
"js-yaml": "^4.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"remark-directive": "^3.0.0",
|
||||||
|
"remark-gfm": "^4.0.0",
|
||||||
|
"remark-slug": "^7.0.1",
|
||||||
|
"remark-smartypants": "^3.0.2",
|
||||||
|
"unist-util-visit": "^5.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { defineConfig } from 'astro/config';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
output: 'static'
|
|
||||||
});
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@ily/penfield",
|
|
||||||
"type": "module",
|
|
||||||
"packageManager": "pnpm@10.28.0",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "astro dev",
|
|
||||||
"build": "astro build",
|
|
||||||
"preview": "astro preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"astro": "^5.16.13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export const intros = [
|
|
||||||
{ text: "You wake up. Your <b>Penfield</b> thrums." },
|
|
||||||
{ text: "Your <b>Penfield Mood Organ</b> chimes." },
|
|
||||||
{ text: "Your <b>Penfield Mood Organ</b> wakes you." },
|
|
||||||
{ text: "You are awoken by your <b>Penfield Mood Organ</b>." },
|
|
||||||
];
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
export const moods = [
|
|
||||||
{ num: 3, text: "Desire to dial" },
|
|
||||||
{ num: 382, text: "Desire to engage in creative activity" },
|
|
||||||
{ num: 481, text: "Awareness of the manifold possibilities open in the future" },
|
|
||||||
{ num: 594, text: "Pleased acknowledgment of husband's superior wisdom" },
|
|
||||||
{ num: 670, text: "Long-deserved peace" },
|
|
||||||
{ num: 888, text: "Desire to watch TV, no matter what's on" },
|
|
||||||
{ num: 443, text: "Self-accusatory depression" },
|
|
||||||
{ num: 72, text: "Vague unease about tomorrow" },
|
|
||||||
{ num: 158, text: "Acceptance of routine" },
|
|
||||||
{ num: 291, text: "Brief contentment with material possessions" },
|
|
||||||
{ num: 407, text: "Suppressed awareness of mortality" },
|
|
||||||
{ num: 531, text: "Calm readiness to consume" },
|
|
||||||
{ num: 816, text: "Desire to return to bed" },
|
|
||||||
{ num: 952, text: "Resigned compliance" },
|
|
||||||
{ num: 64, text: "Faint hope that things will improve" },
|
|
||||||
];
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
import { intros } from '../data/intros';
|
|
||||||
import { moods } from '../data/moods';
|
|
||||||
const epoch = Math.floor(Date.now() / 3600000);
|
|
||||||
const start = Math.floor(new Date('2026-01-22T14:00:00Z').getTime() / 3600000);
|
|
||||||
const t = epoch; // for mood selection
|
|
||||||
const ring = epoch - start;
|
|
||||||
const intro: { text: string } = intros[t % intros.length];
|
|
||||||
const mood: { num: number, text: string } = moods[t % moods.length];
|
|
||||||
---
|
|
||||||
<!DOCTYPE html><link rel=icon href=data:,><style>body{margin:9%;text-align:center}</style><title>Penfield</title><p><b>Ring #{ring}.</b> <span set:html={intro.text} /></p><p><b>{mood.num}</b>. {mood.text}.</p>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"installCommand": "pnpm install",
|
|
||||||
"buildCommand": "pnpm --filter @ily/penfield build",
|
|
||||||
"outputDirectory": "dist"
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
packages:
|
|
||||||
- 'www'
|
|
||||||
- 'penfield'
|
|
||||||
|
|
@ -18,7 +18,6 @@ const posts = fs.existsSync(postsDir)
|
||||||
let postWords = 0;
|
let postWords = 0;
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
const content = fs.readFileSync(path.join(postsDir, post), 'utf-8');
|
const content = fs.readFileSync(path.join(postsDir, post), 'utf-8');
|
||||||
// Remove frontmatter
|
|
||||||
const body = content.replace(/^---[\s\S]*?---/, '');
|
const body = content.replace(/^---[\s\S]*?---/, '');
|
||||||
postWords += countWords(body);
|
postWords += countWords(body);
|
||||||
}
|
}
|
||||||
|
|
@ -41,20 +40,19 @@ const bookmarks = fs.existsSync(bookmarksFile)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Guestbook count - read from built JSON file
|
// Guestbook count - read from built JSON file
|
||||||
const guestbookJsonFile = path.join(root, '.vercel/output/static/guestbook-count.json');
|
const guestbookJsonFile = path.join(root, 'dist/client/guestbook-count.json');
|
||||||
let guestbookCount = 0;
|
let guestbookCount = 0;
|
||||||
if (fs.existsSync(guestbookJsonFile)) {
|
if (fs.existsSync(guestbookJsonFile)) {
|
||||||
const data = JSON.parse(fs.readFileSync(guestbookJsonFile, 'utf-8'));
|
const data = JSON.parse(fs.readFileSync(guestbookJsonFile, 'utf-8'));
|
||||||
guestbookCount = data.count;
|
guestbookCount = data.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate totals (excluding stats.txt words for now, we'll add them after generating)
|
// Calculate totals
|
||||||
const totalPages = 1 + posts.length + txtFiles.length; // home + individual post pages + txt files
|
const totalPages = 1 + posts.length + txtFiles.length;
|
||||||
|
|
||||||
// Read template from public/stats.txt and replace placeholders
|
// Read template from public/stats.txt and replace placeholders
|
||||||
const template = fs.readFileSync(path.join(root, 'public/stats.txt'), 'utf-8');
|
const template = fs.readFileSync(path.join(root, 'public/stats.txt'), 'utf-8');
|
||||||
|
|
||||||
// First pass: generate stats without stats.txt word count
|
|
||||||
let stats = template
|
let stats = template
|
||||||
.replace('[PAGES]', totalPages.toString())
|
.replace('[PAGES]', totalPages.toString())
|
||||||
.replace('[POSTS]', posts.length.toString())
|
.replace('[POSTS]', posts.length.toString())
|
||||||
|
|
@ -62,15 +60,13 @@ let stats = template
|
||||||
.replace('[BOOKMARKS]', bookmarks.length.toString())
|
.replace('[BOOKMARKS]', bookmarks.length.toString())
|
||||||
.replace('[GUESTBOOK]', guestbookCount.toString());
|
.replace('[GUESTBOOK]', guestbookCount.toString());
|
||||||
|
|
||||||
// Count words in the stats file itself (before adding [WORDS])
|
|
||||||
const statsWords = countWords(stats.replace('[WORDS]', '0'));
|
const statsWords = countWords(stats.replace('[WORDS]', '0'));
|
||||||
const totalWords = postWords + txtWords + statsWords;
|
const totalWords = postWords + txtWords + statsWords;
|
||||||
|
|
||||||
// Final pass: replace [WORDS] with actual total
|
|
||||||
stats = stats.replace('[WORDS]', totalWords.toString());
|
stats = stats.replace('[WORDS]', totalWords.toString());
|
||||||
|
|
||||||
// Write to Vercel output
|
// Write to build output
|
||||||
const outputDir = path.join(root, '.vercel/output/static');
|
const outputDir = path.join(root, 'dist/client');
|
||||||
if (!fs.existsSync(outputDir)) {
|
if (!fs.existsSync(outputDir)) {
|
||||||
fs.mkdirSync(outputDir, { recursive: true });
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
}
|
}
|
||||||
0
www/src/env.d.ts → src/env.d.ts
vendored
0
www/src/env.d.ts → src/env.d.ts
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
import GitHub from '@auth/core/providers/github';
|
|
||||||
import { defineConfig } from 'auth-astro';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
providers: [
|
|
||||||
GitHub({
|
|
||||||
clientId: import.meta.env.GITHUB_CLIENT_ID,
|
|
||||||
clientSecret: import.meta.env.GITHUB_CLIENT_SECRET,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
callbacks: {
|
|
||||||
jwt({ token, account, profile }) {
|
|
||||||
if (account && profile) {
|
|
||||||
token.id = profile.id;
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
},
|
|
||||||
session({ session, token }) {
|
|
||||||
if (session.user && token.id) {
|
|
||||||
(session.user as any).id = String(token.id);
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import { db, Guestbook } from 'astro:db';
|
|
||||||
|
|
||||||
export default async function seed() {
|
|
||||||
await db.insert(Guestbook).values([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'lisek',
|
|
||||||
message: ':)',
|
|
||||||
url: null,
|
|
||||||
createdAt: new Date('2026-03-23'),
|
|
||||||
approved: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'stripes',
|
|
||||||
message: 'yay signing',
|
|
||||||
url: null,
|
|
||||||
createdAt: new Date('2026-03-21'),
|
|
||||||
approved: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'Evan',
|
|
||||||
message: 'Queue has four silent letters o_O',
|
|
||||||
url: null,
|
|
||||||
createdAt: new Date('2026-01-23'),
|
|
||||||
approved: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: 'your good pal chev',
|
|
||||||
message: 'howdy howdy',
|
|
||||||
url: 'https://youtu.be/dQw4w9WgXcQ?si=lmJDP_U9yTySGD-_',
|
|
||||||
createdAt: new Date('2025-11-19'),
|
|
||||||
approved: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: 'Farofa',
|
|
||||||
message: 'Thinking on what to write holdon',
|
|
||||||
url: null,
|
|
||||||
createdAt: new Date('2025-11-03'),
|
|
||||||
approved: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
name: 'luna',
|
|
||||||
message: 'we love lewis from primal gaming',
|
|
||||||
url: null,
|
|
||||||
createdAt: new Date('2025-08-23'),
|
|
||||||
approved: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
6861
www/package-lock.json
generated
6861
www/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@ily/www",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "astro dev --port 4322",
|
|
||||||
"build": "astro build --remote && node scripts/generate-stats.js",
|
|
||||||
"preview": "astro preview",
|
|
||||||
"serve": "pnpm build && npx serve .vercel/output/static -l 4322"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@astrojs/db": "^0.19.0",
|
|
||||||
"@astrojs/rss": "^4.0.15",
|
|
||||||
"@astrojs/vercel": "^9.0.4",
|
|
||||||
"@auth/core": "^0.37.4",
|
|
||||||
"astro": "^5.16.13",
|
|
||||||
"auth-astro": "^4.2.0",
|
|
||||||
"js-yaml": "^4.1.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/js-yaml": "^4.0.9",
|
|
||||||
"remark-directive": "^3.0.0",
|
|
||||||
"remark-gfm": "^4.0.0",
|
|
||||||
"remark-slug": "^7.0.1",
|
|
||||||
"remark-smartypants": "^3.0.2",
|
|
||||||
"unist-util-visit": "^5.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import { getSession } from 'auth-astro/server';
|
|
||||||
|
|
||||||
export type Session = { user?: { id?: string; name?: string | null } };
|
|
||||||
|
|
||||||
export type AuthResult =
|
|
||||||
| { status: 'admin'; session: Session }
|
|
||||||
| { status: 'unauthenticated' }
|
|
||||||
| { status: 'forbidden' }
|
|
||||||
| { status: 'error' };
|
|
||||||
|
|
||||||
export async function getAdminSession(request: Request): Promise<AuthResult> {
|
|
||||||
let session: Session | null;
|
|
||||||
try {
|
|
||||||
session = await getSession(request);
|
|
||||||
} catch {
|
|
||||||
return { status: 'error' };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!session) return { status: 'unauthenticated' };
|
|
||||||
|
|
||||||
if (session.user?.id !== import.meta.env.ADMIN_GITHUB_ID) {
|
|
||||||
return { status: 'forbidden' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { status: 'admin', session };
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
---
|
|
||||||
export const prerender = false;
|
|
||||||
|
|
||||||
import { getPendingEntries, type GuestbookEntry } from '../lib/db';
|
|
||||||
import { getAdminSession } from '../lib/auth';
|
|
||||||
import Layout from '../layouts/Layout.astro';
|
|
||||||
import { formatDate } from '../lib/format';
|
|
||||||
|
|
||||||
const auth = await getAdminSession(Astro.request);
|
|
||||||
if (auth.status === 'error') return new Response('Auth not configured', { status: 500 });
|
|
||||||
if (auth.status === 'unauthenticated') return Astro.redirect('/api/auth/signin');
|
|
||||||
if (auth.status !== 'admin') return new Response('Forbidden', { status: 403 });
|
|
||||||
const { session } = auth;
|
|
||||||
|
|
||||||
let entries: GuestbookEntry[] = [];
|
|
||||||
try {
|
|
||||||
entries = await getPendingEntries();
|
|
||||||
} catch {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
---
|
|
||||||
<Layout title="admin - guestbook" showHeader={false}>
|
|
||||||
|
|
||||||
<h1>guestbook admin</h1>
|
|
||||||
<p>logged in as {session.user?.name} <a href="/api/auth/signout">sign out</a></p>
|
|
||||||
|
|
||||||
<p><button id="deploy">redeploy site</button> <span id="deploy-status"></span></p>
|
|
||||||
|
|
||||||
{entries.length === 0 ? (
|
|
||||||
<p class="muted">no pending entries</p>
|
|
||||||
) : (
|
|
||||||
<ul id="entries">
|
|
||||||
{entries.map(e => (
|
|
||||||
<li data-id={e.id}>
|
|
||||||
<span class="muted">{formatDate(e.createdAt)}</span>
|
|
||||||
<strong>{e.name}</strong>
|
|
||||||
{e.url && <a href={e.url}>{e.url}</a>}
|
|
||||||
<p>{e.message}</p>
|
|
||||||
<button class="approve">approve</button>
|
|
||||||
<button class="reject">reject</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.getElementById('deploy')?.addEventListener('click', async (e) => {
|
|
||||||
const btn = e.target as HTMLButtonElement;
|
|
||||||
const status = document.getElementById('deploy-status');
|
|
||||||
btn.disabled = true;
|
|
||||||
if (status) status.textContent = 'deploying...';
|
|
||||||
|
|
||||||
const res = await fetch('/api/deploy', { method: 'POST' });
|
|
||||||
if (res.ok) {
|
|
||||||
if (status) status.textContent = 'deploy triggered!';
|
|
||||||
} else {
|
|
||||||
const data = await res.json();
|
|
||||||
if (status) status.textContent = data.error || 'deploy failed';
|
|
||||||
}
|
|
||||||
btn.disabled = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll('#entries li').forEach(li => {
|
|
||||||
const id = li.getAttribute('data-id');
|
|
||||||
|
|
||||||
li.querySelector('.approve')?.addEventListener('click', async () => {
|
|
||||||
const res = await fetch(`/api/guestbook/${id}`, { method: 'PATCH' });
|
|
||||||
if (res.ok) li.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
li.querySelector('.reject')?.addEventListener('click', async () => {
|
|
||||||
const res = await fetch(`/api/guestbook/${id}`, { method: 'DELETE' });
|
|
||||||
if (res.ok) li.remove();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</Layout>
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import type { APIRoute } from 'astro';
|
|
||||||
import { jsonResponse, errorResponse } from '../../lib/api';
|
|
||||||
import { getAdminSession } from '../../lib/auth';
|
|
||||||
|
|
||||||
export const prerender = false;
|
|
||||||
|
|
||||||
export const POST: APIRoute = async ({ request }) => {
|
|
||||||
const auth = await getAdminSession(request);
|
|
||||||
if (auth.status !== 'admin') return errorResponse('Unauthorized', 403);
|
|
||||||
|
|
||||||
const hookUrl = import.meta.env.VERCEL_DEPLOY_HOOK;
|
|
||||||
if (!hookUrl) return errorResponse('Deploy hook not configured', 500);
|
|
||||||
|
|
||||||
const res = await fetch(hookUrl, { method: 'POST' });
|
|
||||||
if (!res.ok) return errorResponse('Failed to trigger deploy', 502);
|
|
||||||
|
|
||||||
return jsonResponse({ success: true });
|
|
||||||
};
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import type { APIRoute } from 'astro';
|
|
||||||
import { approveEntry, deleteEntry } from '../../../lib/db';
|
|
||||||
import { jsonResponse, errorResponse } from '../../../lib/api';
|
|
||||||
import { getAdminSession } from '../../../lib/auth';
|
|
||||||
|
|
||||||
export const prerender = false;
|
|
||||||
|
|
||||||
export const PATCH: APIRoute = async ({ params, request }) => {
|
|
||||||
const auth = await getAdminSession(request);
|
|
||||||
if (auth.status !== 'admin') return errorResponse('Unauthorized', 403);
|
|
||||||
|
|
||||||
const id = parseInt(params.id!, 10);
|
|
||||||
if (isNaN(id)) return errorResponse('Invalid ID', 400);
|
|
||||||
|
|
||||||
await approveEntry(id);
|
|
||||||
return jsonResponse({ success: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DELETE: APIRoute = async ({ params, request }) => {
|
|
||||||
const auth = await getAdminSession(request);
|
|
||||||
if (auth.status !== 'admin') return errorResponse('Unauthorized', 403);
|
|
||||||
|
|
||||||
const id = parseInt(params.id!, 10);
|
|
||||||
if (isNaN(id)) return errorResponse('Invalid ID', 400);
|
|
||||||
|
|
||||||
await deleteEntry(id);
|
|
||||||
return jsonResponse({ success: true });
|
|
||||||
};
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"installCommand": "pnpm install",
|
|
||||||
"buildCommand": "pnpm --filter @ily/www build",
|
|
||||||
"outputDirectory": "dist",
|
|
||||||
"redirects": [
|
|
||||||
{ "source": "/txt/now.txt", "destination": "/now.txt", "permanent": true }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue