Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
VITE_API_BASE_URL=http://localhost:3000
# Leave empty for local environment / no api authentication
API_URL=http://localhost:3000

# Set to production
NODE_ENV=development

# Leaving this empty will generate a new unique random session secret at start
SESSION_SECRET=
1 change: 0 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export default [
},
{
rules: {
'no-console': 'error',
'svelte/no-unused-svelte-ignore': 'off',
},
},
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,8 @@
"src/**/*.{ts,svelte}": [
"eslint --fix"
]
},
"dependencies": {
"svelte-kit-sessions": "catalog:core"
}
}
25 changes: 25 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ catalogs:
svelte: ^5.48.0
svelte-check: ^4.3.5
vite: ^7.3.1
svelte-kit-sessions: "^0.4.0"
css:
'@alexanderniebuhr/prettier-plugin-unocss': ^0.0.4
'@unocss/extractor-svelte': ^66.6.3
Expand Down
6 changes: 6 additions & 0 deletions src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ declare global {
}
}

declare module 'svelte-kit-sessions' {
interface SessionData {
path: string;
}
}

export {};
5 changes: 0 additions & 5 deletions src/demo.spec.ts

This file was deleted.

24 changes: 22 additions & 2 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { env } from '$env/dynamic/private';
import { paraglideMiddleware } from '$lib/paraglide/server';
import type { Handle } from '@sveltejs/kit';
import { type Handle, redirect } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
import * as crypto from 'node:crypto';
import { sveltekitSessionHandle } from 'svelte-kit-sessions';

if (!env.SESSION_SECRET) {
env.SESSION_SECRET = crypto.randomBytes(20).toString('hex');
console.log(`SESSION_SECRET not found, generating a temporary one: ${env.SESSION_SECRET}`);
}

const sessionHandle = sveltekitSessionHandle({
secret: env.SESSION_SECRET,
});

const checkAuthorizationHandle: Handle = async ({ event, resolve }) => {
if (!event.locals.session.data.path && event.url.pathname !== '/load-project') {
throw redirect(302, '/load-project');
}
return resolve(event);
};

const handleParaglide: Handle = ({ event, resolve }) =>
paraglideMiddleware(event.request, ({ request, locale }) => {
Expand All @@ -10,4 +30,4 @@ const handleParaglide: Handle = ({ event, resolve }) =>
});
});

export const handle: Handle = handleParaglide;
export const handle: Handle = sequence(sessionHandle, handleParaglide, checkAuthorizationHandle);
54 changes: 33 additions & 21 deletions src/utils/http-client/http-client.ts → src/lib/server/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,77 @@ export interface MiddlewareParams {
options: RequestOptions;
}

export type MiddlewareNext = (params?: MiddlewareParams) => Promise<Response>;
export type FullResponse = Response & { content: any };

export type MiddlewareNext = (params?: MiddlewareParams) => Promise<FullResponse>;

export type Middleware = (
params: MiddlewareParams,
next: MiddlewareNext,
) => Promise<Response | undefined> | undefined;
) => Promise<FullResponse | undefined> | undefined;

type BaseRequest = (path: string, options: RequestOptions) => Promise<Response>;
type BaseRequest = (path: string, options: RequestOptions) => Promise<FullResponse>;

export class HttpClient {
private readonly _baseUrl: string;
private readonly _baseOptions: RequestOptions;
private readonly _middlewares: Middleware[];

constructor(baseUrl: string) {
constructor(baseUrl: string, options?: RequestOptions) {
this._baseUrl = baseUrl;
this._baseOptions = {};
this._baseOptions = options ?? {
headers: {
'Content-Type': 'application/json',
},
};
this._middlewares = [];
}

get(path: string, options?: RequestOptions): Promise<Response> {
get(path: string, options?: RequestOptions): Promise<FullResponse> {
return this._applyMiddlewares(path, options, (newPath, newOptions) => {
return fetch(newPath, {
return this._request(newPath, {
...newOptions,
method: 'GET',
});
});
}

post(path: string, body?: any, options?: RequestOptions): Promise<Response> {
post(path: string, body?: string, options?: RequestOptions): Promise<FullResponse> {
return this._applyMiddlewares(path, options, (newPath, newOptions) => {
return fetch(newPath, {
return this._request(newPath, {
...newOptions,
method: 'POST',
body: body,
});
});
}

put(path: string, body?: any, options?: RequestOptions): Promise<Response> {
put(path: string, body?: string, options?: RequestOptions): Promise<FullResponse> {
return this._applyMiddlewares(path, options, (newPath, newOptions) => {
return fetch(newPath, {
return this._request(newPath, {
...newOptions,
method: 'PUT',
body: body,
});
});
}

patch(path: string, body?: any, options?: RequestOptions): Promise<Response> {
return this._applyMiddlewares(path, options, (newPath, newOptions) => {
return fetch(newPath, {
patch(path: string, body?: string, options?: RequestOptions): Promise<FullResponse> {
return this._applyMiddlewares(path, options, async (newPath, newOptions) => {
return this._request(newPath, {
...newOptions,
method: 'PATCH',
body: body,
});
});
}

delete(path: string, options?: RequestOptions): Promise<Response> {
delete(path: string, options?: RequestOptions): Promise<FullResponse> {
return this._applyMiddlewares(path, options, (newPath, newOptions) => {
return fetch(newPath, {
return this._request(newPath, {
...newOptions,
method: 'DELETE',
});
}) as Promise<FullResponse>;
});
}

Expand All @@ -79,11 +85,17 @@ export class HttpClient {
return this;
}

private async _request(path: string, request: RequestInit): Promise<FullResponse> {
const res = (await fetch(path, request)) as FullResponse;
res.content = null;
return res;
}

private _applyMiddlewares(
path: string,
options: RequestOptions | undefined,
callback: BaseRequest,
): Promise<Response> {
): Promise<FullResponse> {
const baseParams = {
path,
fullPath: this._getUrl(path),
Expand All @@ -93,14 +105,14 @@ export class HttpClient {
},
};
const middlewares = this._middlewares.slice();
let response: Response;
let response: FullResponse;

const execution = async (params?: MiddlewareParams): Promise<Response> => {
const execution = async (params?: MiddlewareParams): Promise<FullResponse> => {
if (!params) params = baseParams;

const middleware = middlewares.shift();

if (!middleware) response = (await callback(params.fullPath, params.options)) as Response;
if (!middleware) response = (await callback(params.fullPath, params.options)) as FullResponse;
else response = (await middleware(params, execution)) ?? response;

return response;
Expand Down
7 changes: 7 additions & 0 deletions src/lib/server/utils/file-system/file-system-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class FileSystemError extends Error {
message: string;
constructor(message: string) {
super();
this.message = message;
}
}
Loading
Loading