Add an opt-in SSR mode with a Node adapter behind PUBLIC_SSR
This commit is contained in:
parent
9cc82dc1fe
commit
54c0682dce
18 changed files with 4257 additions and 3552 deletions
|
|
@ -2,6 +2,9 @@ PUBLIC_RELAY_URL=ws://localhost:3334 # required — NIP-29 relay hosting the g
|
||||||
PUBLIC_MODE=simple # simple | full (single forum vs. many rooms)
|
PUBLIC_MODE=simple # simple | full (single forum vs. many rooms)
|
||||||
PUBLIC_GROUP_ID=mygrouprandomid # required in simple mode; the single forum's group id (ignored in full mode)
|
PUBLIC_GROUP_ID=mygrouprandomid # required in simple mode; the single forum's group id (ignored in full mode)
|
||||||
PUBLIC_TITLE= # top-bar title; empty falls back to the group name
|
PUBLIC_TITLE= # top-bar title; empty falls back to the group name
|
||||||
|
# Render on the server (Node deploy) instead of shipping a static SPA
|
||||||
|
PUBLIC_SSR=no
|
||||||
|
|
||||||
PUBLIC_SEARCH=no # yes | no — enable the search box (requires a relay with NIP-50 support)
|
PUBLIC_SEARCH=no # yes | no — enable the search box (requires a relay with NIP-50 support)
|
||||||
PUBLIC_LABELS= # comma-separated discussion labels (e.g., bug,feature,question)
|
PUBLIC_LABELS= # comma-separated discussion labels (e.g., bug,feature,question)
|
||||||
PUBLIC_BLOSSOM_URL= # Blossom server URL for uploads (e.g., https://blossom.primal.net)
|
PUBLIC_BLOSSOM_URL= # Blossom server URL for uploads (e.g., https://blossom.primal.net)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ Squalk is configured entirely through environment variables (all prefixed `PUBLI
|
||||||
| `PUBLIC_GROUP_ID` | in simple mode | — | The single forum's group id. Required when `PUBLIC_MODE=simple`; ignored in full mode, where rooms are selected at runtime. |
|
| `PUBLIC_GROUP_ID` | in simple mode | — | The single forum's group id. Required when `PUBLIC_MODE=simple`; ignored in full mode, where rooms are selected at runtime. |
|
||||||
| `PUBLIC_TITLE` | no | group name | Title shown in the top bar. When empty it falls back to the group's name. |
|
| `PUBLIC_TITLE` | no | group name | Title shown in the top bar. When empty it falls back to the group's name. |
|
||||||
| `PUBLIC_JOINCODE` | no | `no` | `yes` to show an invite-code field when a join request is rejected (for code-gated relays). |
|
| `PUBLIC_JOINCODE` | no | `no` | `yes` to show an invite-code field when a join request is rejected (for code-gated relays). |
|
||||||
|
| `PUBLIC_SSR` | no | `no` | `yes` to render pages on the server (crawlable HTML, real 404s). The build then targets Node (`node build`) instead of a static bundle; see [Deploying](#deploying). |
|
||||||
| `PUBLIC_SEARCH` | no | `no` | `yes` to show a search box at the top of the homepage. Requires a relay with NIP-50 search support. |
|
| `PUBLIC_SEARCH` | no | `no` | `yes` to show a search box at the top of the homepage. Requires a relay with NIP-50 search support. |
|
||||||
| `PUBLIC_LABELS` | no | — | Comma-separated discussion labels offered when composing, e.g. `bug,feature,question`. |
|
| `PUBLIC_LABELS` | no | — | Comma-separated discussion labels offered when composing, e.g. `bug,feature,question`. |
|
||||||
| `PUBLIC_BLOSSOM_URL` | no | — | Blossom server URL used for media uploads, e.g. `https://blossom.primal.net`. Uploads are disabled when unset. |
|
| `PUBLIC_BLOSSOM_URL` | no | — | Blossom server URL used for media uploads, e.g. `https://blossom.primal.net`. Uploads are disabled when unset. |
|
||||||
|
|
|
||||||
7739
package-lock.json
generated
7739
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -16,7 +16,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^2.0.4",
|
"@eslint/compat": "^2.0.4",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@sveltejs/adapter-auto": "^7.0.1",
|
"@sveltejs/adapter-node": "^5.5.7",
|
||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
"@sveltejs/kit": "^2.57.0",
|
"@sveltejs/kit": "^2.57.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Event } from "@nostr/tools";
|
import type { Event } from "@nostr/tools";
|
||||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||||
import { RELAY_URL } from "$lib/config";
|
import { RELAY_URL } from "$lib/config";
|
||||||
import { auth } from "$lib/auth.svelte";
|
import { auth } from "$lib/auth.svelte";
|
||||||
import { queryForum, publishForum, subscribeForum } from "$lib/relay";
|
import { queryForum, publishForum, subscribeForum } from "$lib/relay";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||||
import { tokenizeChat } from "$lib/linkify";
|
import { tokenizeChat } from "$lib/linkify";
|
||||||
import {
|
import {
|
||||||
resolveThreadRef,
|
resolveThreadRef,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tick } from "svelte";
|
import { tick } from "svelte";
|
||||||
import * as nip19 from "@nostr/tools/nip19";
|
import * as nip19 from "@nostr/tools/nip19";
|
||||||
import { loadRelayList } from "@nostr/gadgets/lists";
|
import { loadRelayList } from "$lib/gadgets";
|
||||||
import {
|
import {
|
||||||
profileStore,
|
profileStore,
|
||||||
searchLocalProfiles,
|
searchLocalProfiles,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||||
import * as nip19 from "@nostr/tools/nip19";
|
import * as nip19 from "@nostr/tools/nip19";
|
||||||
import {
|
import {
|
||||||
resolveThreadRef,
|
resolveThreadRef,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ export const GROUP_ID = env.PUBLIC_GROUP_ID ?? "";
|
||||||
if (MODE === "simple" && !GROUP_ID) {
|
if (MODE === "simple" && !GROUP_ID) {
|
||||||
throw new Error("PUBLIC_GROUP_ID is required in simple mode");
|
throw new Error("PUBLIC_GROUP_ID is required in simple mode");
|
||||||
}
|
}
|
||||||
|
// Server-side rendering: opt-in, and only meaningful when the build targets the
|
||||||
|
// Node adapter (svelte.config.js reads the same variable).
|
||||||
|
export const SSR_ENABLED = env.PUBLIC_SSR === "yes";
|
||||||
// Requires a relay with NIP-50 support.
|
// Requires a relay with NIP-50 support.
|
||||||
export const SEARCH_ENABLED = env.PUBLIC_SEARCH === "yes";
|
export const SEARCH_ENABLED = env.PUBLIC_SEARCH === "yes";
|
||||||
export const LABELS = (env.PUBLIC_LABELS ?? "")
|
export const LABELS = (env.PUBLIC_LABELS ?? "")
|
||||||
|
|
|
||||||
17
src/lib/gadgets.ts
Normal file
17
src/lib/gadgets.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import type { NostrUser } from "@nostr/gadgets/metadata";
|
||||||
|
import type { ListFetcher, RelayItem } from "@nostr/gadgets/lists";
|
||||||
|
|
||||||
|
export type { NostrUser, RelayItem };
|
||||||
|
|
||||||
|
// @nostr/gadgets reads localStorage when imported, so it can't be loaded on
|
||||||
|
// the server. Every consumer goes through these lazy wrappers; the library is
|
||||||
|
// pulled in on first use, in the browser only.
|
||||||
|
export async function loadNostrUser(pubkey: string): Promise<NostrUser> {
|
||||||
|
const m = await import("@nostr/gadgets/metadata");
|
||||||
|
return m.loadNostrUser(pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadRelayList: ListFetcher<RelayItem> = async (...args) => {
|
||||||
|
const m = await import("@nostr/gadgets/lists");
|
||||||
|
return m.loadRelayList(...args);
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import * as nip19 from "@nostr/tools/nip19";
|
import * as nip19 from "@nostr/tools/nip19";
|
||||||
import { loadRelayList } from "@nostr/gadgets/lists";
|
import { loadRelayList } from "$lib/gadgets";
|
||||||
|
|
||||||
const RELAY_HINT_TIMEOUT_MS = 1500;
|
const RELAY_HINT_TIMEOUT_MS = 1500;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { AbstractRelay } from "@nostr/tools/abstract-relay";
|
import type { AbstractRelay } from "@nostr/tools/abstract-relay";
|
||||||
import type { Event } from "@nostr/tools/core";
|
import type { Event } from "@nostr/tools/core";
|
||||||
import type { Filter } from "@nostr/tools/filter";
|
import type { Filter } from "@nostr/tools/filter";
|
||||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||||
import { ensureForumRelay } from "$lib/relay";
|
import { ensureForumRelay } from "$lib/relay";
|
||||||
import { ingestNostrUser } from "$lib/profiles.svelte";
|
import { ingestNostrUser } from "$lib/profiles.svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||||
import { RELAY_URL, GROUP_ID } from "$lib/config";
|
import { RELAY_URL, GROUP_ID } from "$lib/config";
|
||||||
import { queryForum, publishForum } from "$lib/relay";
|
import { queryForum, publishForum } from "$lib/relay";
|
||||||
import { threads as mockThreads } from "$lib/mock";
|
import { threads as mockThreads } from "$lib/mock";
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { AbstractRelay } from "@nostr/tools/abstract-relay";
|
import type { AbstractRelay } from "@nostr/tools/abstract-relay";
|
||||||
import type { Event } from "@nostr/tools/core";
|
import type { Event } from "@nostr/tools/core";
|
||||||
import type { Filter } from "@nostr/tools/filter";
|
import type { Filter } from "@nostr/tools/filter";
|
||||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||||
import { ensureForumRelay } from "$lib/relay";
|
import { ensureForumRelay } from "$lib/relay";
|
||||||
import { ingestNostrUser } from "$lib/profiles.svelte";
|
import { ingestNostrUser } from "$lib/profiles.svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,9 @@
|
||||||
<link rel="icon" href={favicon} />
|
<link rel="icon" href={favicon} />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if relayAccess.state !== "open"}
|
<!-- The server and the pre-probe client render the forum; the gate only
|
||||||
|
replaces it once a probe confirms the relay refuses this visitor. -->
|
||||||
|
{#if relayAccess.state !== "open" && relayAccess.state !== "checking"}
|
||||||
<RelayGate />
|
<RelayGate />
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
export const ssr = false;
|
import { SSR_ENABLED } from "$lib/config";
|
||||||
|
|
||||||
|
export const ssr = SSR_ENABLED;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
import MessageEditor from "$lib/components/MessageEditor.svelte";
|
import MessageEditor from "$lib/components/MessageEditor.svelte";
|
||||||
import PostContent from "$lib/components/PostContent.svelte";
|
import PostContent from "$lib/components/PostContent.svelte";
|
||||||
import Tag from "$lib/components/Tag.svelte";
|
import Tag from "$lib/components/Tag.svelte";
|
||||||
import { type NostrUser } from "@nostr/gadgets/metadata";
|
import type { NostrUser } from "$lib/gadgets";
|
||||||
|
|
||||||
type Author = { pubkey: string; name: string; picture?: string };
|
type Author = { pubkey: string; name: string; picture?: string };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
import adapter from "@sveltejs/adapter-static";
|
import { loadEnv } from "vite";
|
||||||
|
import adapterNode from "@sveltejs/adapter-node";
|
||||||
|
import adapterStatic from "@sveltejs/adapter-static";
|
||||||
|
|
||||||
|
// PUBLIC_SSR picks the deployment target: a Node server that renders pages
|
||||||
|
// (yes) or a static single-page bundle (no, the default). The same flag turns
|
||||||
|
// SSR on in +layout.ts.
|
||||||
|
const env = loadEnv(
|
||||||
|
process.env.NODE_ENV ?? "production",
|
||||||
|
process.cwd(),
|
||||||
|
"PUBLIC_",
|
||||||
|
);
|
||||||
|
const ssr = (process.env.PUBLIC_SSR ?? env.PUBLIC_SSR) === "yes";
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter({
|
adapter: ssr ? adapterNode() : adapterStatic({ fallback: "index.html" }),
|
||||||
fallback: "index.html",
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue