Skip to content

Commit 9f78211

Browse files
authored
feat: add SSG support for public pages (#630)
Pre-render public routes (home, privacy, imprint, login, register, forgot-password) as static HTML for better SEO and faster initial load. Generates sitemap.xml at build time. Adds SSR guards for browser-only APIs (localStorage, document, navigator).
1 parent 0180cef commit 9f78211

12 files changed

Lines changed: 472 additions & 360 deletions

File tree

bun.lock

Lines changed: 61 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"dev": "SHOPMON_API_URL=http://127.0.0.1:5789 vite --port 3000 --host",
8-
"build": "vite build",
8+
"build": "vite-ssg build",
99
"preview": "vite preview",
1010
"test": "vitest",
1111
"test:ui": "vitest --ui",
@@ -49,12 +49,15 @@
4949
"@iconify-json/octicon": "^1.2.19",
5050
"@iconify-json/ri": "^1.2.6",
5151
"@trpc/server": "^11.7.2",
52+
"@unhead/vue": "^2.1.12",
5253
"@vitejs/plugin-vue": "6.0.5",
5354
"@vitest/ui": "^4.0.18",
5455
"@vue/test-utils": "^2.4.6",
56+
"beasties": "^0.3.5",
5557
"jsdom": "^29.0.0",
5658
"typescript": "catalog:",
5759
"vite": "8.0.1",
60+
"vite-ssg": "^28.3.0",
5861
"vitest": "^4.0.18"
5962
}
6063
}

frontend/public/robots.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
User-agent: *
22
Allow: /
3+
4+
Sitemap: https://shopmon.fos.gg/sitemap.xml

frontend/src/composables/useDarkMode.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export function useDarkMode(): {
4444
}
4545

4646
function initializeDarkMode() {
47+
if (import.meta.env.SSR) {
48+
return;
49+
}
50+
4751
// Get initial preference
4852
const stored = localStorage.getItem(DARK_MODE_STORAGE_KEY);
4953
if (stored !== null) {
@@ -79,6 +83,9 @@ function initializeDarkMode() {
7983
}
8084

8185
function updateDarkModeClass(isDark: boolean): void {
86+
if (import.meta.env.SSR) {
87+
return;
88+
}
8289
if (isDark) {
8390
document.documentElement.classList.add("dark");
8491
} else {

frontend/src/composables/useLocale.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ export function useLocale() {
77

88
function setLocale(lang: string) {
99
locale.value = lang;
10-
localStorage.setItem(LOCALE_STORAGE_KEY, lang);
11-
document.documentElement.lang = lang;
10+
if (!import.meta.env.SSR) {
11+
localStorage.setItem(LOCALE_STORAGE_KEY, lang);
12+
document.documentElement.lang = lang;
13+
}
1214
}
1315

1416
function toggleLocale() {

frontend/src/i18n.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import de from "./locales/de.json";
55
const LOCALE_STORAGE_KEY = "shopmon-locale";
66

77
function getInitialLocale(): string {
8+
if (import.meta.env.SSR) {
9+
return "en";
10+
}
811
const stored = localStorage.getItem(LOCALE_STORAGE_KEY);
912
if (stored && ["en", "de"].includes(stored)) {
1013
return stored;

frontend/src/main.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
import { createApp } from "vue";
1+
import { ViteSSG } from "vite-ssg";
22

33
import "./style.css";
44

55
import App from "./App.vue";
6-
import { router } from "./router";
6+
import { routes, setupRouterGuards } from "./router";
77
import { i18n } from "./i18n";
88

9-
const app = createApp(App);
9+
export const createApp = ViteSSG(
10+
App,
11+
{
12+
routes,
13+
linkActiveClass: "active",
14+
},
15+
({ app, router, isClient }) => {
16+
app.use(i18n as any);
1017

11-
app.use(i18n as any);
12-
app.use(router);
13-
14-
app.mount("#app");
18+
if (isClient) {
19+
setupRouterGuards(router);
20+
}
21+
},
22+
);

0 commit comments

Comments
 (0)