giga refactor of the API calls
This commit is contained in:
parent
526763b795
commit
b6d9d45398
29 changed files with 743 additions and 933 deletions
1099
package-lock.json
generated
1099
package-lock.json
generated
File diff suppressed because it is too large
Load diff
13
package.json
13
package.json
|
@ -2,6 +2,9 @@
|
|||
"name": "dipper",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
@ -16,17 +19,17 @@
|
|||
"@sveltejs/kit": "^1.20.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte": "^2.30.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"svelte": "^4.0.0",
|
||||
"svelte-check": "^3.4.3",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.3.6",
|
||||
"sveltekit-adapter-deno": "^0.10.1"
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.0",
|
||||
"sveltekit-adapter-deno": "^0.10.2"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {}
|
||||
|
|
44
src/lib/api/auth.ts
Normal file
44
src/lib/api/auth.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import type { Cookies } from '@sveltejs/kit';
|
||||
import { callJSON, callText } from './request';
|
||||
|
||||
interface LoginParameters {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface LoginResponse {
|
||||
expires_at: string;
|
||||
session_id: string;
|
||||
}
|
||||
|
||||
interface OkResponse {
|
||||
ok: true;
|
||||
}
|
||||
|
||||
export const login = (cookies: Cookies, username: string, password: string) =>
|
||||
callJSON<LoginResponse, LoginParameters>(
|
||||
'POST',
|
||||
'auth/login',
|
||||
{
|
||||
username,
|
||||
password
|
||||
},
|
||||
cookies
|
||||
);
|
||||
|
||||
export const logout = (cookies: Cookies) =>
|
||||
callJSON<OkResponse>('POST', 'auth/logout', undefined, cookies);
|
||||
export const getLoggedInUser = (cookies: Cookies) => callText('GET', 'auth/me', undefined, cookies);
|
||||
|
||||
export async function isLoggedIn(cookies: Cookies) {
|
||||
const session = cookies.get('session');
|
||||
if (session) {
|
||||
try {
|
||||
// Check that login is valid
|
||||
await getLoggedInUser(cookies);
|
||||
return true;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
21
src/lib/api/collections.ts
Normal file
21
src/lib/api/collections.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import type { Cookies } from '@sveltejs/kit';
|
||||
import type { PaginatedWithCursor, TimePaginationQuery } from './pagination';
|
||||
import type { Post } from './posts';
|
||||
import { callJSON, withQuery } from './request';
|
||||
|
||||
export interface Collection {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
parent: string | null;
|
||||
}
|
||||
|
||||
export const getCollectionPosts = (
|
||||
name: string,
|
||||
collection: string,
|
||||
pagination?: TimePaginationQuery
|
||||
) =>
|
||||
callJSON<PaginatedWithCursor<Post, string>>(
|
||||
'GET',
|
||||
withQuery(`collections/${name}/${collection}/posts`, pagination)
|
||||
);
|
82
src/lib/api/cookies.ts
Normal file
82
src/lib/api/cookies.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { PUBLIC_COOKIES_SECURE, PUBLIC_DOMAIN } from '$env/static/public';
|
||||
import { parseBool } from '$lib/env';
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
|
||||
interface CookieInfo {
|
||||
name: string;
|
||||
value: string;
|
||||
options: {
|
||||
SameSite?: 'Strict' | 'Lax' | 'None';
|
||||
Secure: boolean;
|
||||
Partitioned: boolean;
|
||||
HttpOnly: boolean;
|
||||
Expires?: Date;
|
||||
'Max-Age'?: number;
|
||||
Path?: string;
|
||||
Domain?: string;
|
||||
};
|
||||
}
|
||||
|
||||
function parseCookieHeader(headerValue: string): CookieInfo {
|
||||
const [nv, ...options] = headerValue.split(';');
|
||||
const [name, value] = nv.split('=').map((v) => v.trim());
|
||||
const cookie: CookieInfo = {
|
||||
name,
|
||||
value,
|
||||
options: {
|
||||
Secure: false,
|
||||
Partitioned: false,
|
||||
HttpOnly: false
|
||||
}
|
||||
};
|
||||
|
||||
options.forEach((option) => {
|
||||
const [key, value] = option.split('=').map((v) => v.trim());
|
||||
switch (key) {
|
||||
case 'Secure':
|
||||
cookie.options.Secure = true;
|
||||
break;
|
||||
case 'SameSite':
|
||||
cookie.options.SameSite = value as 'Strict' | 'Lax' | 'None';
|
||||
break;
|
||||
case 'Partitioned':
|
||||
cookie.options.Partitioned = true;
|
||||
break;
|
||||
case 'HttpOnly':
|
||||
cookie.options.HttpOnly = true;
|
||||
break;
|
||||
case 'Expires':
|
||||
cookie.options.Expires = new Date(value);
|
||||
break;
|
||||
case 'Max-Age':
|
||||
cookie.options['Max-Age'] = parseInt(value, 10);
|
||||
break;
|
||||
case 'Path':
|
||||
cookie.options.Path = value;
|
||||
break;
|
||||
case 'Domain':
|
||||
cookie.options.Domain = value;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
export function passthroughSession(response: Response, cookies: Cookies) {
|
||||
if (response.ok) {
|
||||
const cookieHeader = response.headers.get('Set-Cookie');
|
||||
if (!cookieHeader) {
|
||||
return;
|
||||
}
|
||||
const info = parseCookieHeader(cookieHeader);
|
||||
cookies.set(info.name, info.value, {
|
||||
httpOnly: info.options.HttpOnly,
|
||||
secure: parseBool(PUBLIC_COOKIES_SECURE),
|
||||
sameSite: 'strict',
|
||||
domain: PUBLIC_DOMAIN,
|
||||
expires: info.options.Expires,
|
||||
maxAge: info.options['Max-Age']
|
||||
});
|
||||
}
|
||||
}
|
11
src/lib/api/pagination.ts
Normal file
11
src/lib/api/pagination.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { QuerySerializable } from './request';
|
||||
|
||||
export interface PaginatedWithCursor<T, C> {
|
||||
items: T[];
|
||||
next_cursor: C | null;
|
||||
}
|
||||
|
||||
export interface TimePaginationQuery extends QuerySerializable {
|
||||
limit: number;
|
||||
before: Date | null;
|
||||
}
|
19
src/lib/api/posts.ts
Normal file
19
src/lib/api/posts.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { callJSON } from './request';
|
||||
|
||||
export interface Post {
|
||||
id: string;
|
||||
site: string;
|
||||
author: string;
|
||||
author_display_name: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
tags: string[];
|
||||
blocks: [];
|
||||
created_at: string;
|
||||
modified_at: string | null;
|
||||
deleted_at: string | null;
|
||||
}
|
||||
|
||||
export const getPost = async (site: string, slug: string) =>
|
||||
callJSON<Post>('GET', `posts/${site}/${slug}`);
|
91
src/lib/api/request.ts
Normal file
91
src/lib/api/request.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { PUBLIC_API_BASE } from '$env/static/public';
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
import { passthroughSession } from './cookies';
|
||||
|
||||
export class APIError extends Error {
|
||||
readonly code?: string;
|
||||
|
||||
constructor(readonly response: Response, body?: { code: string; message: string }) {
|
||||
super(body?.message ?? response.statusText);
|
||||
this.code = body?.code;
|
||||
Object.setPrototypeOf(this, APIError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
export type Stringer = { toString(): string };
|
||||
export type QuerySerializable = { [key: string | number | symbol]: Stringer | null | undefined };
|
||||
|
||||
export function withQuery(path: string, query?: QuerySerializable) {
|
||||
return query ? `${path}?${asQueryParams(query).toString()}` : path;
|
||||
}
|
||||
|
||||
export function asQueryParams<T extends QuerySerializable>(obj: T) {
|
||||
const params = new URLSearchParams();
|
||||
for (const key of Object.getOwnPropertyNames(obj)) {
|
||||
const value = obj[key];
|
||||
if (value === null || value === undefined || typeof value === 'undefined') continue;
|
||||
params.append(key, value.toString());
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
type Body = { [key: string]: unknown } | object | void;
|
||||
export async function apiCall<B extends Body>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: B,
|
||||
cookies?: Cookies
|
||||
) {
|
||||
const session = cookies?.get('session');
|
||||
const response = await fetch(`${PUBLIC_API_BASE}/${path}`, {
|
||||
method,
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Cookie: session ? `session=${session}` : ''
|
||||
}
|
||||
});
|
||||
if (cookies) {
|
||||
passthroughSession(response, cookies);
|
||||
}
|
||||
if (response.ok) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Check for content
|
||||
if (response.headers.get('Content-Type')?.includes('application/json')) {
|
||||
const body = await response.json();
|
||||
throw new APIError(response, body);
|
||||
}
|
||||
|
||||
throw new APIError(response);
|
||||
}
|
||||
|
||||
export async function callJSON<R, B extends Body = void>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: B,
|
||||
cookies?: Cookies
|
||||
) {
|
||||
return apiCall(method, path, body, cookies).then((response) => response.json() as R);
|
||||
}
|
||||
|
||||
export async function callText<B extends Body = void>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: B,
|
||||
cookies?: Cookies
|
||||
) {
|
||||
return apiCall(method, path, body, cookies).then((response) => response.text());
|
||||
}
|
||||
|
||||
export async function callBytes<B extends Body = void>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: B,
|
||||
cookies?: Cookies
|
||||
) {
|
||||
return apiCall(method, path, body, cookies).then((response) => response.blob());
|
||||
}
|
17
src/lib/api/sites.ts
Normal file
17
src/lib/api/sites.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { Collection } from './collections';
|
||||
import { callJSON } from './request';
|
||||
|
||||
export interface Site {
|
||||
name: string;
|
||||
owner: string;
|
||||
owner_display_name: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
collections: Collection[];
|
||||
default_collection: string;
|
||||
created_at: string;
|
||||
modified_at: string | null;
|
||||
deleted_at: string | null;
|
||||
}
|
||||
|
||||
export const getSiteInfo = (name: string) => callJSON<Site>('GET', `sites/${name}`);
|
16
src/lib/env.ts
Normal file
16
src/lib/env.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export function parseBool(str: string, defaultValue: boolean = false): boolean {
|
||||
switch (str.toLowerCase().trim()) {
|
||||
case 'true':
|
||||
case 'yes':
|
||||
case 'on':
|
||||
case '1':
|
||||
return true;
|
||||
case 'false':
|
||||
case 'no':
|
||||
case 'off':
|
||||
case '0':
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
26
src/lib/errors.ts
Normal file
26
src/lib/errors.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import { APIError } from './api/request';
|
||||
|
||||
/// Maps an error from the backend to a SvelteKit error.
|
||||
export function mapBackendError(e: unknown) {
|
||||
if (e instanceof APIError) {
|
||||
switch (e.response.status) {
|
||||
case 404:
|
||||
return error(404, 'Not Found');
|
||||
default:
|
||||
// TODO handle better
|
||||
return error(500, e.response.statusText);
|
||||
}
|
||||
}
|
||||
|
||||
return error(502, 'Platform is down');
|
||||
}
|
||||
|
||||
/// Ensures that a promise resolves to a value, or throws the appropriate error otherwise.
|
||||
export async function must<T>(promise: Promise<T>): Promise<T> {
|
||||
try {
|
||||
return await promise;
|
||||
} catch (err) {
|
||||
throw mapBackendError(err);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
export interface Site {
|
||||
name: string;
|
||||
owner: string;
|
||||
owner_display_name: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
collections: Collection[];
|
||||
default_collection: string;
|
||||
created_at: string;
|
||||
modified_at: string | null;
|
||||
deleted_at: string | null;
|
||||
}
|
||||
|
||||
export interface Collection {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
parent: string | null;
|
||||
}
|
||||
|
||||
export interface Post {
|
||||
id: string;
|
||||
site: string;
|
||||
author: string;
|
||||
author_display_name: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
tags: string[];
|
||||
blocks: [];
|
||||
created_at: string;
|
||||
modified_at: string | null;
|
||||
deleted_at: string | null;
|
||||
}
|
||||
|
||||
export interface PaginatedWithCursor<T, C> {
|
||||
items: T[];
|
||||
next_cursor: C | null;
|
||||
}
|
|
@ -3,3 +3,10 @@ import { PUBLIC_DOMAIN } from '$env/static/public';
|
|||
export function getSiteName(origin: string) {
|
||||
return origin.substring(0, origin.indexOf(PUBLIC_DOMAIN)).replaceAll('.', '');
|
||||
}
|
||||
|
||||
export function getLoginURL(base: URL) {
|
||||
const copy = new URL(base);
|
||||
copy.hostname = PUBLIC_DOMAIN;
|
||||
copy.pathname = '/login';
|
||||
return copy.toString();
|
||||
}
|
||||
|
|
3
src/routes/(admin-panel)/+layout.svelte
Normal file
3
src/routes/(admin-panel)/+layout.svelte
Normal file
|
@ -0,0 +1,3 @@
|
|||
<h2>cenere!!!</h2>
|
||||
|
||||
<slot />
|
9
src/routes/(admin-panel)/admin/+layout.server.ts
Normal file
9
src/routes/(admin-panel)/admin/+layout.server.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { redirect } from '@sveltejs/kit';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
import { isLoggedIn } from '$lib/api/auth';
|
||||
|
||||
export const load = (async ({ cookies }) => {
|
||||
if (!(await isLoggedIn(cookies))) {
|
||||
throw redirect(302, '/login');
|
||||
}
|
||||
}) satisfies LayoutServerLoad;
|
5
src/routes/(admin-panel)/admin/+layout.svelte
Normal file
5
src/routes/(admin-panel)/admin/+layout.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<slot />
|
||||
|
||||
<form method="POST" action="/login?/logout">
|
||||
<button type="submit">logout</button>
|
||||
</form>
|
1
src/routes/(admin-panel)/admin/+page.svelte
Normal file
1
src/routes/(admin-panel)/admin/+page.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
aaaa
|
47
src/routes/(admin-panel)/login/+page.server.ts
Normal file
47
src/routes/(admin-panel)/login/+page.server.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { PUBLIC_API_BASE } from '$env/static/public';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
import { APIError } from '$lib/api/request';
|
||||
import { getLoggedInUser, login, logout } from '$lib/api/auth';
|
||||
|
||||
export const load = async function ({ url, cookies }) {
|
||||
const session = cookies.get('session');
|
||||
|
||||
// Check that login is valid
|
||||
if (session) {
|
||||
let user = null;
|
||||
try {
|
||||
user = await getLoggedInUser(cookies);
|
||||
} catch (e) {}
|
||||
|
||||
if (user) {
|
||||
throw redirect(302, url.searchParams.get('then') || '/admin');
|
||||
}
|
||||
}
|
||||
} satisfies PageServerLoad;
|
||||
|
||||
export const actions = {
|
||||
login: async ({ cookies, request, url }) => {
|
||||
const data = await request.formData();
|
||||
const username = data.get('username')?.toString();
|
||||
const password = data.get('password')?.toString();
|
||||
if (!username || !password) return { error: 'missing credentials' };
|
||||
|
||||
try {
|
||||
await login(cookies, username, password);
|
||||
throw redirect(302, url.searchParams.get('then') || '/admin');
|
||||
} catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
switch (e.code) {
|
||||
case 'invalid-login':
|
||||
return { error: 'invalid credentials' };
|
||||
}
|
||||
}
|
||||
return { error: 'unknown internal error' };
|
||||
}
|
||||
},
|
||||
logout: async ({ url, cookies }) => {
|
||||
await logout(cookies);
|
||||
throw redirect(302, url.searchParams.get('then') || '/login');
|
||||
}
|
||||
} satisfies Actions;
|
15
src/routes/(admin-panel)/login/+page.svelte
Normal file
15
src/routes/(admin-panel)/login/+page.svelte
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { ActionData } from './$types';
|
||||
|
||||
export let form: ActionData;
|
||||
</script>
|
||||
|
||||
{#if form}
|
||||
OH NOES: {form.error}
|
||||
{/if}
|
||||
|
||||
<form method="POST" action="/login?/login">
|
||||
<input name="username" placeholder="user id" />
|
||||
<input name="password" type="password" placeholder="password" />
|
||||
<button type="submit">sign in</button>
|
||||
</form>
|
11
src/routes/(user-website)/+layout.svelte
Normal file
11
src/routes/(user-website)/+layout.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import type { LayoutData } from './$types';
|
||||
|
||||
export let data: LayoutData;
|
||||
</script>
|
||||
|
||||
<header><h2><a href="/">{data.site.title}</a></h2></header>
|
||||
<slot />
|
||||
<footer>
|
||||
<a href={data.links.login}>login</a>
|
||||
</footer>
|
22
src/routes/(user-website)/+layout.ts
Normal file
22
src/routes/(user-website)/+layout.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { PUBLIC_WEBSITE_URL } from '$env/static/public';
|
||||
import { getLoginURL, getSiteName } from '$lib/url';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { getSiteInfo } from '$lib/api/sites';
|
||||
import { must } from '$lib/errors';
|
||||
|
||||
export const load = (async ({ url }) => {
|
||||
const site = getSiteName(url.hostname);
|
||||
if (!site) {
|
||||
throw redirect(302, PUBLIC_WEBSITE_URL);
|
||||
}
|
||||
|
||||
const siteData = await must(getSiteInfo(site));
|
||||
|
||||
return {
|
||||
site: siteData,
|
||||
links: {
|
||||
login: getLoginURL(url)
|
||||
}
|
||||
};
|
||||
}) satisfies LayoutLoad;
|
|
@ -8,7 +8,7 @@
|
|||
{#each data.pages.items as post}
|
||||
<article>
|
||||
<header>
|
||||
<h2>{post.title}</h2>
|
||||
<h2><a href="/p/{post.slug}">{post.title}</a></h2>
|
||||
<h4>by {post.author_display_name} on {new Date(post.created_at).toLocaleDateString()}</h4>
|
||||
</header>
|
||||
</article>
|
11
src/routes/(user-website)/+page.ts
Normal file
11
src/routes/(user-website)/+page.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { getCollectionPosts } from '$lib/api/collections';
|
||||
import { must } from '$lib/errors';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async ({ parent }) => {
|
||||
const { site } = await parent();
|
||||
|
||||
return {
|
||||
pages: await must(getCollectionPosts(site.name, site.default_collection))
|
||||
};
|
||||
}) satisfies PageLoad;
|
11
src/routes/(user-website)/p/[slug]/+page.ts
Normal file
11
src/routes/(user-website)/p/[slug]/+page.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { PageLoad } from './$types';
|
||||
import { getPost } from '$lib/api/posts';
|
||||
import { must } from '$lib/errors';
|
||||
|
||||
export const load = (async ({ params, parent }) => {
|
||||
const { site } = await parent();
|
||||
|
||||
return {
|
||||
page: await must(getPost(site.name, params.slug))
|
||||
};
|
||||
}) satisfies PageLoad;
|
|
@ -1,8 +0,0 @@
|
|||
<script lang="ts">
|
||||
import type { LayoutData } from './$types';
|
||||
|
||||
export let data: LayoutData;
|
||||
</script>
|
||||
|
||||
<header><h2>{data.site.title}</h2></header>
|
||||
<slot />
|
|
@ -1,17 +0,0 @@
|
|||
import { PUBLIC_API_BASE, PUBLIC_WEBSITE_URL } from '$env/static/public';
|
||||
import type { Site } from '$lib/mabel-types';
|
||||
import { getSiteName } from '$lib/url';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load = (async ({ url }) => {
|
||||
const site = getSiteName(url.hostname);
|
||||
if (!site) throw redirect(301, PUBLIC_WEBSITE_URL);
|
||||
|
||||
const siteData = await fetch(`${PUBLIC_API_BASE}/sites/${site}`);
|
||||
if (siteData.status === 404) throw error(404, 'Not Found');
|
||||
|
||||
return {
|
||||
site: (await siteData.json()) as Site
|
||||
};
|
||||
}) satisfies LayoutLoad;
|
|
@ -1,15 +0,0 @@
|
|||
import { PUBLIC_API_BASE } from '$env/static/public';
|
||||
import type { PaginatedWithCursor, Post } from '$lib/mabel-types';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async ({ parent }) => {
|
||||
const { site } = await parent();
|
||||
|
||||
const data = await fetch(
|
||||
`${PUBLIC_API_BASE}/collections/${site.name}/${site.default_collection}/posts`
|
||||
);
|
||||
|
||||
return {
|
||||
pages: (await data.json()) as PaginatedWithCursor<Post, string>
|
||||
};
|
||||
}) satisfies PageLoad;
|
|
@ -1,14 +0,0 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
import { PUBLIC_API_BASE } from '$env/static/public';
|
||||
import type { Post } from '$lib/mabel-types';
|
||||
|
||||
export const load = (async ({ params, parent }) => {
|
||||
const { site } = await parent();
|
||||
const pageData = await fetch(`${PUBLIC_API_BASE}/posts/${site.name}/${params.slug}`);
|
||||
if (pageData.status === 404) throw error(404, 'Not Found');
|
||||
|
||||
return {
|
||||
page: (await pageData.json()) as Post
|
||||
};
|
||||
}) satisfies PageLoad;
|
Loading…
Reference in a new issue