diff --git a/flake.nix b/flake.nix index b4e0e7a..401db06 100644 --- a/flake.nix +++ b/flake.nix @@ -38,7 +38,6 @@ pkgs = nixpkgs.legacyPackages.${system}; in { - module-test = self.nixosConfigurations.module-test.config.system.build.vm; dev-vm = self.nixosConfigurations.dev-vm.config.system.build.vm; default = pkgs.callPackage ./nix/package.nix { }; @@ -56,33 +55,17 @@ program = "${pkgs.writeShellScriptBin "api-setup" '' ${pkgs.postgresql_16}/bin/psql postgres://postgres@localhost:15432/archtika -c "ALTER DATABASE archtika SET \"app.jwt_secret\" TO 'a42kVyAhTImYxZeebZkApoAZLmf0VtDA'" - ${pkgs.dbmate}/bin/dbmate --url postgres://postgres@localhost:15432/archtika?sslmode=disable --migrations-dir ${ - self.packages.${system}.default - }/rest-api/db/migrations up + echo "OUT PATH: ${self.outPath}" + + ${pkgs.dbmate}/bin/dbmate --url postgres://postgres@localhost:15432/archtika?sslmode=disable --migrations-dir ${self.outPath}/rest-api/db/migrations up PGRST_DB_SCHEMAS="api" PGRST_DB_ANON_ROLE="anon" PGRST_OPENAPI_MODE="ignore-privileges" PGRST_DB_URI="postgres://authenticator@localhost:15432/archtika" PGRST_JWT_SECRET="a42kVyAhTImYxZeebZkApoAZLmf0VtDA" ${pkgs.postgrest}/bin/postgrest ''}/bin/api-setup"; }; - - web = { - type = "app"; - program = "${pkgs.writeShellScriptBin "web-wrapper" '' - ORIGIN=http://localhost:4000 HOST=127.0.0.1 PORT=4000 ${pkgs.nodejs_22}/bin/node ${ - self.packages.${system}.default - }/web-app - ''}/bin/web-wrapper"; - }; } ); nixosConfigurations = { - module-test = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - ./nix/module-test.nix - { _module.args.localArchtikaPackage = self.packages."x86_64-linux".default; } - ]; - }; dev-vm = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./nix/dev-vm.nix ]; diff --git a/nix/dev-vm.nix b/nix/dev-vm.nix index ffa742e..6c2d511 100644 --- a/nix/dev-vm.nix +++ b/nix/dev-vm.nix @@ -39,8 +39,8 @@ } { from = "host"; - host.port = 80; - guest.port = 80; + host.port = 18000; + guest.port = 1800; } ]; }; @@ -63,7 +63,7 @@ listen = [ { addr = "0.0.0.0"; - port = 80; + port = 1800; } ]; locations = { diff --git a/nix/module-test.nix b/nix/module-test.nix deleted file mode 100644 index d938e88..0000000 --- a/nix/module-test.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ - pkgs, - lib, - modulesPath, - localArchtikaPackage, - ... -}: -{ - imports = [ - "${modulesPath}/virtualisation/qemu-vm.nix" - ./module.nix - ]; - - networking = { - hostName = "archtika-module-test"; - firewall.enable = false; - }; - - nix.settings.experimental-features = [ "nix-command flakes" ]; - - users.users.dev = { - isNormalUser = true; - extraGroups = [ "wheel" ]; - password = "dev"; - }; - - virtualisation = { - graphics = false; - # Alternatively a bridge network for QEMU could be setup, but requires much more effort - forwardPorts = [ - { - from = "host"; - host.port = 5000; - guest.port = 5000; - } - { - from = "host"; - host.port = 10000; - guest.port = 10000; - } - { - from = "host"; - host.port = 15000; - guest.port = 15000; - } - ]; - }; - - services.archtika = { - enable = true; - package = localArchtikaPackage; - jwtSecret = "a42kVyAhTImYxZeebZkApoAZLmf0VtDA"; - }; - - system.stateVersion = "24.05"; -} diff --git a/web-app/src/hooks.server.ts b/web-app/src/hooks.server.ts index d7fda69..4e24146 100644 --- a/web-app/src/hooks.server.ts +++ b/web-app/src/hooks.server.ts @@ -1,8 +1,9 @@ import { redirect } from "@sveltejs/kit"; +import { API_BASE_PREFIX } from "$lib/utils"; export const handle = async ({ event, resolve }) => { if (!event.url.pathname.startsWith("/api/")) { - const userData = await event.fetch(`/api/account`, { + const userData = await event.fetch(`${API_BASE_PREFIX}/account`, { method: "GET", headers: { "Content-Type": "application/json", diff --git a/web-app/src/lib/utils.ts b/web-app/src/lib/utils.ts index 4de3089..b2709ee 100644 --- a/web-app/src/lib/utils.ts +++ b/web-app/src/lib/utils.ts @@ -1,5 +1,6 @@ import markdownit from "markdown-it"; import hljs from "highlight.js"; +import { dev } from "$app/environment"; export const sortOptions = [ { value: "creation-time", text: "Creation time" }, @@ -23,3 +24,6 @@ export const md = markdownit({ return ""; } }); + +export const API_BASE_PREFIX = dev ? "http://localhost:3000" : "/api"; +export const NGINX_BASE_PREFIX = dev ? "http://localhost:18000" : "/user-websites"; diff --git a/web-app/src/routes/(anonymous)/login/+page.server.ts b/web-app/src/routes/(anonymous)/login/+page.server.ts index b7994c1..283c479 100644 --- a/web-app/src/routes/(anonymous)/login/+page.server.ts +++ b/web-app/src/routes/(anonymous)/login/+page.server.ts @@ -1,10 +1,11 @@ import type { Actions } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const actions: Actions = { default: async ({ request, cookies, fetch }) => { const data = await request.formData(); - const res = await fetch(`/api/rpc/login`, { + const res = await fetch(`${API_BASE_PREFIX}/rpc/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ diff --git a/web-app/src/routes/(anonymous)/register/+page.server.ts b/web-app/src/routes/(anonymous)/register/+page.server.ts index 35c87dc..82cd079 100644 --- a/web-app/src/routes/(anonymous)/register/+page.server.ts +++ b/web-app/src/routes/(anonymous)/register/+page.server.ts @@ -1,10 +1,11 @@ import type { Actions } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const actions: Actions = { default: async ({ request, fetch }) => { const data = await request.formData(); - const res = await fetch(`/api/rpc/register`, { + const res = await fetch(`${API_BASE_PREFIX}/rpc/register`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ diff --git a/web-app/src/routes/(authenticated)/+page.server.ts b/web-app/src/routes/(authenticated)/+page.server.ts index 17985b8..7c6d456 100644 --- a/web-app/src/routes/(authenticated)/+page.server.ts +++ b/web-app/src/routes/(authenticated)/+page.server.ts @@ -1,4 +1,5 @@ import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ fetch, cookies, url }) => { const searchQuery = url.searchParams.get("website_search_query"); @@ -6,7 +7,7 @@ export const load: PageServerLoad = async ({ fetch, cookies, url }) => { const params = new URLSearchParams(); - const baseFetchUrl = `/api/website`; + const baseFetchUrl = `${API_BASE_PREFIX}/website`; if (searchQuery) { params.append("title", `ilike.*${searchQuery}*`); @@ -63,7 +64,7 @@ export const actions: Actions = { createWebsite: async ({ request, fetch, cookies }) => { const data = await request.formData(); - const res = await fetch(`/api/rpc/create_website`, { + const res = await fetch(`${API_BASE_PREFIX}/rpc/create_website`, { method: "POST", headers: { "Content-Type": "application/json", @@ -85,7 +86,7 @@ export const actions: Actions = { updateWebsite: async ({ request, cookies, fetch }) => { const data = await request.formData(); - const res = await fetch(`/api/website?id=eq.${data.get("id")}`, { + const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, { method: "PATCH", headers: { "Content-Type": "application/json", @@ -106,7 +107,7 @@ export const actions: Actions = { deleteWebsite: async ({ request, cookies, fetch }) => { const data = await request.formData(); - const res = await fetch(`/api/website?id=eq.${data.get("id")}`, { + const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, { method: "DELETE", headers: { "Content-Type": "application/json", diff --git a/web-app/src/routes/(authenticated)/account/+page.server.ts b/web-app/src/routes/(authenticated)/account/+page.server.ts index adc0862..2177733 100644 --- a/web-app/src/routes/(authenticated)/account/+page.server.ts +++ b/web-app/src/routes/(authenticated)/account/+page.server.ts @@ -1,4 +1,5 @@ import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ locals }) => { return { @@ -15,7 +16,7 @@ export const actions: Actions = { deleteAccount: async ({ request, fetch, cookies }) => { const data = await request.formData(); - const res = await fetch(`/api/rpc/delete_account`, { + const res = await fetch(`${API_BASE_PREFIX}/rpc/delete_account`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts index 94a0549..af51676 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts @@ -1,7 +1,8 @@ import type { LayoutServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: LayoutServerLoad = async ({ params, fetch, cookies }) => { - const websiteData = await fetch(`/api/website?id=eq.${params.websiteId}`, { + const websiteData = await fetch(`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}`, { method: "GET", headers: { "Content-Type": "application/json", @@ -10,7 +11,7 @@ export const load: LayoutServerLoad = async ({ params, fetch, cookies }) => { } }); - const homeData = await fetch(`/api/home?website_id=eq.${params.websiteId}`, { + const homeData = await fetch(`${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, { method: "GET", headers: { "Content-Type": "application/json", diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts index 04054a5..e9343e0 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts @@ -1,7 +1,20 @@ import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ params, fetch, cookies, url }) => { - const globalSettingsData = await fetch(`/api/settings?website_id=eq.${params.websiteId}`, { + const globalSettingsData = await fetch( + `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${cookies.get("session_token")}`, + Accept: "application/vnd.pgrst.object+json" + } + } + ); + + const headerData = await fetch(`${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, { method: "GET", headers: { "Content-Type": "application/json", @@ -10,16 +23,7 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url }) => { } }); - const headerData = await fetch(`/api/header?website_id=eq.${params.websiteId}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${cookies.get("session_token")}`, - Accept: "application/vnd.pgrst.object+json" - } - }); - - const footerData = await fetch(`/api/footer?website_id=eq.${params.websiteId}`, { + const footerData = await fetch(`${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, { method: "GET", headers: { "Content-Type": "application/json", @@ -44,7 +48,7 @@ export const actions: Actions = { const data = await request.formData(); const faviconFile = data.get("favicon") as File; - const uploadedImageData = await fetch(`/api/rpc/upload_file`, { + const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { method: "POST", headers: { "Content-Type": "application/octet-stream", @@ -63,7 +67,7 @@ export const actions: Actions = { return { success: false, message: uploadedImage.message }; } - const res = await fetch(`/api/settings?website_id=eq.${params.websiteId}`, { + const res = await fetch(`${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`, { method: "PATCH", headers: { "Content-Type": "application/json", @@ -90,7 +94,7 @@ export const actions: Actions = { const data = await request.formData(); const logoImage = data.get("logo-image") as File; - const uploadedImageData = await fetch(`/api/rpc/upload_file`, { + const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { method: "POST", headers: { "Content-Type": "application/octet-stream", @@ -109,7 +113,7 @@ export const actions: Actions = { return { success: false, message: uploadedImage.message }; } - const res = await fetch(`/api/header?website_id=eq.${params.websiteId}`, { + const res = await fetch(`${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, { method: "PATCH", headers: { "Content-Type": "application/json", @@ -135,7 +139,7 @@ export const actions: Actions = { updateHome: async ({ request, fetch, cookies, params }) => { const data = await request.formData(); - const res = await fetch(`/api/home?website_id=eq.${params.websiteId}`, { + const res = await fetch(`${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, { method: "PATCH", headers: { "Content-Type": "application/json", @@ -156,7 +160,7 @@ export const actions: Actions = { updateFooter: async ({ request, fetch, cookies, params }) => { const data = await request.formData(); - const res = await fetch(`/api/footer?website_id=eq.${params.websiteId}`, { + const res = await fetch(`${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, { method: "PATCH", headers: { "Content-Type": "application/json", diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte index 5bca128..fe3e319 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte @@ -5,6 +5,7 @@ import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import type { ActionData, PageServerData } from "./$types"; import Modal from "$lib/components/Modal.svelte"; + import { API_BASE_PREFIX } from "$lib/utils"; const { data, form } = $props<{ data: PageServerData; form: ActionData }>(); @@ -55,7 +56,10 @@ {#if data.globalSettings.favicon_image} - + {/if} @@ -106,7 +110,7 @@ {#if data.header.logo_image} - + {/if} diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.server.ts index ef8a427..892be91 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.server.ts +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.server.ts @@ -1,4 +1,5 @@ import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent }) => { const searchQuery = url.searchParams.get("article_search_query"); @@ -6,7 +7,7 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent const parameters = new URLSearchParams(); - const baseFetchUrl = `/api/article?website_id=eq.${params.websiteId}&select=id,title`; + const baseFetchUrl = `${API_BASE_PREFIX}/article?website_id=eq.${params.websiteId}&select=id,title`; if (searchQuery) { parameters.append("title", `ilike.*${searchQuery}*`); @@ -66,7 +67,7 @@ export const actions: Actions = { createArticle: async ({ request, fetch, cookies, params, locals }) => { const data = await request.formData(); - const res = await fetch(`/api/article`, { + const res = await fetch(`${API_BASE_PREFIX}/article`, { method: "POST", headers: { "Content-Type": "application/json", @@ -89,7 +90,7 @@ export const actions: Actions = { deleteArticle: async ({ request, fetch, cookies }) => { const data = await request.formData(); - const res = await fetch(`/api/article?id=eq.${data.get("id")}`, { + const res = await fetch(`${API_BASE_PREFIX}/article?id=eq.${data.get("id")}`, { method: "DELETE", headers: { "Content-Type": "application/json", diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts index 9e240ae..49b6d57 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts @@ -1,7 +1,8 @@ import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ parent, params, cookies, fetch }) => { - const articleData = await fetch(`/api/article?id=eq.${params.articleId}`, { + const articleData = await fetch(`${API_BASE_PREFIX}/article?id=eq.${params.articleId}`, { method: "GET", headers: { "Content-Type": "application/json", @@ -21,7 +22,7 @@ export const actions: Actions = { const data = await request.formData(); const coverFile = data.get("cover-image") as File; - const uploadedImageData = await fetch(`/api/rpc/upload_file`, { + const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { method: "POST", headers: { "Content-Type": "application/octet-stream", @@ -40,7 +41,7 @@ export const actions: Actions = { return { success: false, message: uploadedImage.message }; } - const res = await fetch(`/api/article?id=eq.${params.articleId}`, { + const res = await fetch(`${API_BASE_PREFIX}/article?id=eq.${params.articleId}`, { method: "PATCH", headers: { "Content-Type": "application/json", diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte index 3d8ad86..9fe1c94 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte @@ -5,6 +5,7 @@ import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import type { ActionData, PageServerData } from "./$types"; import Modal from "$lib/components/Modal.svelte"; + import { API_BASE_PREFIX } from "$lib/utils"; const { data, form } = $props<{ data: PageServerData; form: ActionData }>(); @@ -68,7 +69,10 @@ {#if data.article.cover_image} - + {/if} diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts index 474a8c2..ed782b6 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts @@ -1,10 +1,11 @@ import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ parent, params, fetch, cookies }) => { const { website, home } = await parent(); const collabData = await fetch( - `/api/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)`, + `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)`, { method: "GET", headers: { @@ -27,7 +28,7 @@ export const actions: Actions = { addCollaborator: async ({ request, fetch, cookies, params }) => { const data = await request.formData(); - const res = await fetch(`/api/collab`, { + const res = await fetch(`${API_BASE_PREFIX}/collab`, { method: "POST", headers: { "Content-Type": "application/json", @@ -51,7 +52,7 @@ export const actions: Actions = { const data = await request.formData(); const res = await fetch( - `/api/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`, + `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`, { method: "PATCH", headers: { @@ -75,7 +76,7 @@ export const actions: Actions = { const data = await request.formData(); const res = await fetch( - `/api/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`, + `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`, { method: "DELETE", headers: { diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts index e1b08e9..cf6cca6 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts @@ -2,22 +2,26 @@ import { readFile, mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { md } from "$lib/utils"; import type { Actions, PageServerLoad } from "./$types"; +import { API_BASE_PREFIX, NGINX_BASE_PREFIX } from "$lib/utils"; export const load: PageServerLoad = async ({ params, fetch, cookies, locals }) => { - const websiteOverviewData = await fetch(`/api/website_overview?id=eq.${params.websiteId}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${cookies.get("session_token")}`, - Accept: "application/vnd.pgrst.object+json" + const websiteOverviewData = await fetch( + `${API_BASE_PREFIX}/website_overview?id=eq.${params.websiteId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${cookies.get("session_token")}`, + Accept: "application/vnd.pgrst.object+json" + } } - }); + ); const websiteOverview = await websiteOverviewData.json(); generateStaticFiles(websiteOverview); - const websitePreviewUrl = `/user-websites/previews/${websiteOverview.user_id}/${websiteOverview.id}/index.html`; + const websitePreviewUrl = `${NGINX_BASE_PREFIX}/previews/${websiteOverview.user_id}/${websiteOverview.id}/index.html`; return { websiteOverview, @@ -122,7 +126,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true) ) .replace( "{{cover_image}}", - `` + `` ) .replace("{{title}}", `

${article.title}

`) .replace("{{publication_date}}", `

${article.publication_date}

`) diff --git a/web-app/src/routes/+layout.svelte b/web-app/src/routes/+layout.svelte index 22ccb01..c0b2f62 100644 --- a/web-app/src/routes/+layout.svelte +++ b/web-app/src/routes/+layout.svelte @@ -3,6 +3,7 @@ import { page } from "$app/stores"; import type { LayoutServerData } from "./$types"; import type { Snippet } from "svelte"; + import { dev } from "$app/environment"; const { data, children } = $props<{ data: LayoutServerData; children: Snippet }>();