✨ feat: update deployment scripts and configuration; enhance CI/CD process with new scripts for staging and production
This commit is contained in:
@@ -1,9 +1,6 @@
|
|||||||
name: deploy to production
|
name: deploy to production
|
||||||
|
|
||||||
on: "push"
|
on: "push"
|
||||||
# push:
|
|
||||||
# branches:
|
|
||||||
# - master
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
@@ -14,7 +11,7 @@ jobs:
|
|||||||
volumes:
|
volumes:
|
||||||
- /data:/data
|
- /data:/data
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
@@ -23,11 +20,10 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
git fetch --force --tags
|
git fetch --force --tags
|
||||||
|
|
||||||
# setup node 20
|
- name: setup node
|
||||||
- name: setup node 20
|
uses: actions/setup-node@v4
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 22
|
||||||
|
|
||||||
- name: install dependencies
|
- name: install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -35,40 +31,7 @@ jobs:
|
|||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
- name: modify config
|
- name: modify config
|
||||||
run: |
|
run: ./scripts/ci-modify-config.sh
|
||||||
sed -i 's#\(sentryEnvironment.*\)".*"#\1"${GITHUB_REF_NAME}"#g' frontend/src/config.ts
|
|
||||||
sed -i 's#//\( sentry\\.init.*\)#\1#g' frontend/src/config.ts
|
|
||||||
sed -i 's#metrictCall = false#metrictCall = true#g' frontend/src/config.ts
|
|
||||||
|
|
||||||
set -o allexport
|
|
||||||
. ./.env
|
|
||||||
echo "PROJECT_RELEASE=${SENTRY_PROJECT}.r`git rev-list HEAD --count`-`git describe --all --long | sed 's+/+-+'`" >> .env
|
|
||||||
. ./.env
|
|
||||||
set +o allexport
|
|
||||||
echo ______ .env ______
|
|
||||||
cat .env
|
|
||||||
echo
|
|
||||||
sed -i 's#\(const release = \).*#\1"'${PROJECT_RELEASE}'"#g' api/hooks/config-client.js
|
|
||||||
sed -i 's#\(const originURL = \).*#\1"'${LIVE_URL}'"#g' api/hooks/config-client.js
|
|
||||||
|
|
||||||
# bash scripts/preload-meta.sh frontend/spa.html
|
|
||||||
# bash scripts/preload-meta.sh frontend/spa.html > frontend/_spa.html
|
|
||||||
# cp frontend/_spa.html frontend/spa.html
|
|
||||||
|
|
||||||
export stamp=`date +%s`
|
|
||||||
sed -i s/__TIMESTAMP__/$stamp/g frontend/spa.html
|
|
||||||
# sed -i s/__TIMESTAMP__/$stamp/g frontend/serviceworker.js
|
|
||||||
# cat frontend/serviceworker.js
|
|
||||||
|
|
||||||
test -d api/templates && test -L api/templates/spa.html && rm api/templates/spa.html || test -d api/templates && test -f api/templates/spa.html && rm api/templates/spa.html
|
|
||||||
test -d api/templates && cp frontend/spa.html api/templates/spa.html
|
|
||||||
cp frontend/spa.html api/templates/spa.html
|
|
||||||
echo ______ frontend/spa.html ______
|
|
||||||
cat frontend/spa.html
|
|
||||||
|
|
||||||
sed -i 's#\(PREVIEW_URL=\).*#\1'${LIVE_URL}/preview'#g' api/config.yml.env
|
|
||||||
echo ______ api/config.yml.env ______
|
|
||||||
cat api/config.yml.env
|
|
||||||
|
|
||||||
- name: build
|
- name: build
|
||||||
env:
|
env:
|
||||||
@@ -88,114 +51,22 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
yarn build:server
|
yarn build:server
|
||||||
|
|
||||||
- name: build legacy
|
- name: upload sourcemaps to sentry
|
||||||
env:
|
env:
|
||||||
FORCE_COLOR: "true"
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
run: |
|
run: ./scripts/ci-upload-sourcemaps.sh
|
||||||
yarn build:legacy
|
|
||||||
|
|
||||||
- name: staging
|
- name: staging
|
||||||
# only if branch is dev
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
env:
|
env:
|
||||||
# /data/ORG/PROJECT/BRANCH
|
|
||||||
API_BASEDIR: /data/${{ github.repository }}/${{ github.ref_name }}
|
API_BASEDIR: /data/${{ github.repository }}/${{ github.ref_name }}
|
||||||
COMPOSE_PROJECT_NAME: ${{ github.repository }}-${{ github.ref_name }}
|
COMPOSE_PROJECT_NAME: ${{ github.repository }}-${{ github.ref_name }}
|
||||||
run: |
|
run: ./scripts/ci-staging.sh
|
||||||
# read .env
|
|
||||||
set -o allexport
|
|
||||||
. ./.env
|
|
||||||
. ./api/config.yml.env
|
|
||||||
set +o allexport
|
|
||||||
|
|
||||||
# replace / with -
|
|
||||||
COMPOSE_PROJECT_NAME=`echo $COMPOSE_PROJECT_NAME | sed 's+/+-+g'`
|
|
||||||
|
|
||||||
mkdir -p $API_BASEDIR/frontend
|
|
||||||
rsync -av api $API_BASEDIR/
|
|
||||||
rsync -av frontend/dist $API_BASEDIR/frontend/
|
|
||||||
rsync -av frontend/assets $API_BASEDIR/frontend/
|
|
||||||
sed -i 's#\(PREVIEW_URL=\).*#\1'${STAGING_URL}/preview'#g' $API_BASEDIR/api/config.yml.env
|
|
||||||
|
|
||||||
docker compose -f docker-compose-staging.yml -p $COMPOSE_PROJECT_NAME up -d --build --remove-orphans
|
|
||||||
|
|
||||||
reloadUrl=${STAGING_URL}/api/_/admin/reload
|
|
||||||
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
|
||||||
|
|
||||||
# clear ssr cache
|
|
||||||
ssrUrl=${STAGING_URL}/api/ssr
|
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
|
||||||
|
|
||||||
- name: deploy
|
- name: deploy
|
||||||
# only if branch is master
|
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
env:
|
env:
|
||||||
RSYNC_USER: ${{ github.repository }}
|
RSYNC_USER: ${{ github.repository }}
|
||||||
RSYNC_PASS: ${{ github.token }}
|
RSYNC_PASS: ${{ github.token }}
|
||||||
BRANCH: ${{ github.ref_name }}
|
BRANCH: ${{ github.ref_name }}
|
||||||
run: |
|
run: ./scripts/ci-deploy.sh
|
||||||
# read .env
|
|
||||||
set -o allexport
|
|
||||||
. ./.env
|
|
||||||
. ./api/config.yml.env
|
|
||||||
set +o allexport
|
|
||||||
|
|
||||||
|
|
||||||
# if RSYNC_USER or RSYNC_KEY is not set, exit
|
|
||||||
if [ -z "${RSYNC_USER}" ]; then
|
|
||||||
echo "RSYNC_USER missing, exiting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${RSYNC_PASS}" ]; then
|
|
||||||
echo "RSYNC_PASS missing, exiting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${RSYNC_HOST}" ]; then
|
|
||||||
echo "RSYNC_HOST missing, exiting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${RSYNC_PORT}" ]; then
|
|
||||||
echo "RSYNC_PORT missing, exiting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Deploying ${BRANCH} to ${RSYNC_HOST} on port ${RSYNC_PORT} as ${RSYNC_USER}"
|
|
||||||
|
|
||||||
excludes=""
|
|
||||||
if [ "${BRANCH}" = "master" ]; then
|
|
||||||
excludes='--exclude=src --exclude=*.map'
|
|
||||||
echo "master deploy, excluding $excludes"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SSH_CMD="sshpass -p ${RSYNC_PASS} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${RSYNC_PORT} -l ${RSYNC_USER}"
|
|
||||||
|
|
||||||
# sync frontend
|
|
||||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
|
||||||
--delete \
|
|
||||||
-e "${SSH_CMD}" \
|
|
||||||
$excludes \
|
|
||||||
frontend/ \
|
|
||||||
${RSYNC_HOST}:./frontend/ \
|
|
||||||
|
|
||||||
# sync api config
|
|
||||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
|
||||||
--delete \
|
|
||||||
-e "${SSH_CMD}" \
|
|
||||||
api/ \
|
|
||||||
${RSYNC_HOST}:./api/
|
|
||||||
|
|
||||||
# create media directory
|
|
||||||
mkdir media
|
|
||||||
chmod 770 media
|
|
||||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
|
||||||
-e "${SSH_CMD}" \
|
|
||||||
media \
|
|
||||||
${RSYNC_HOST}:./
|
|
||||||
|
|
||||||
|
|
||||||
reloadUrl=${LIVE_URL}/api/_/admin/reload
|
|
||||||
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
|
||||||
|
|
||||||
# clear ssr cache
|
|
||||||
ssrUrl=${LIVE_URL}/api/ssr
|
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
|
||||||
|
|||||||
32
AGENTS.md
32
AGENTS.md
@@ -12,6 +12,8 @@ Tibi CMS starter template — Svelte 5 SPA with esbuild, SSR via goja, and Playw
|
|||||||
|
|
||||||
## Setup commands
|
## Setup commands
|
||||||
|
|
||||||
|
> **New project from this template?** Follow the step-by-step guide in [README.md](README.md).
|
||||||
|
|
||||||
- Install deps: `yarn install`
|
- Install deps: `yarn install`
|
||||||
- Start dev: `make docker-up && make docker-start`
|
- Start dev: `make docker-up && make docker-start`
|
||||||
- Start with mock data: set `MOCK=1` in `.env`, then `make docker-up && make docker-start`
|
- Start with mock data: set `MOCK=1` in `.env`, then `make docker-up && make docker-start`
|
||||||
@@ -59,6 +61,36 @@ Tibi CMS starter template — Svelte 5 SPA with esbuild, SSR via goja, and Playw
|
|||||||
- API access to collections uses the reverse proxy: `CODING_URL/api/<collection>` (e.g. `CODING_URL/api/content`).
|
- API access to collections uses the reverse proxy: `CODING_URL/api/<collection>` (e.g. `CODING_URL/api/content`).
|
||||||
- Auth via `Token` header with ADMIN_TOKEN from `api/config.yml.env`.
|
- Auth via `Token` header with ADMIN_TOKEN from `api/config.yml.env`.
|
||||||
|
|
||||||
|
## Reference repositories
|
||||||
|
|
||||||
|
These sibling repos in the workspace provide documentation, types, and reference implementations:
|
||||||
|
|
||||||
|
| Repository | Path | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **tibi-types** | `../../cms/tibi-types` | TypeScript type definitions for hooks, collections, permissions, etc. Included via `tsconfig.json` — **read-only, do not modify**. Key file: `index.d.ts`. JSON schemas in `schemas/api-config/`. |
|
||||||
|
| **tibi-server** | `../../cms/tibi-server` | Go source code of the server. `docs/` has detailed documentation (16 files), `examples/` has sample projects. |
|
||||||
|
| **tibi-admin-nova** | `../../cms/tibi-admin-nova` | Admin UI reference project. Key file: `types/admin.d.ts` (1147 lines — all admin types: `AdminCollection`, `AdminCollectionField`, `AdminCollectionMeta`, `AdminDashboard`, etc.). Use as reference for collection field configs, dashboard setup, and fieldLists. |
|
||||||
|
|
||||||
|
### When to consult which repo
|
||||||
|
|
||||||
|
- **Write collection YAML** → `tibi-server/docs/04-collections.md` + `tibi-types/schemas/api-config/collection.json` (JSON schema)
|
||||||
|
- **Write/debug hooks** → `tibi-server/docs/06-hooks.md` + `tibi-types/index.d.ts` (context types)
|
||||||
|
- **Configure admin UI** → `tibi-admin-nova/types/admin.d.ts` (field types, meta, dashboard) + `tibi-admin-nova/api/collections/` (real examples)
|
||||||
|
- **Permissions** → `tibi-server/docs/05-authentication.md`
|
||||||
|
- **Realtime/SSE** → `tibi-server/docs/07-realtime.md`
|
||||||
|
- **Images/uploads** → `tibi-server/docs/08-file-upload-images.md`
|
||||||
|
- **LLM integration** → `tibi-server/docs/09-llm-integration.md`
|
||||||
|
|
||||||
|
### TypeScript types
|
||||||
|
|
||||||
|
`tibi-types` is included via `tsconfig.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"include": ["frontend/src/**/*", "types/**/*", "./../../cms/tibi-types", "api/**/*"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Project-specific types (e.g. `Ssr`, `ApiOptions`, `ContentEntry`) live in `types/global.d.ts`.
|
||||||
|
|
||||||
## Code style
|
## Code style
|
||||||
|
|
||||||
- Follow the existing code style and conventions used in the project.
|
- Follow the existing code style and conventions used in the project.
|
||||||
|
|||||||
209
README.md
209
README.md
@@ -2,87 +2,170 @@
|
|||||||
|
|
||||||
Starter Kit für SPAs(s) `;)` mit Svelte und TibiCMS inkl. SSR
|
Starter Kit für SPAs(s) `;)` mit Svelte und TibiCMS inkl. SSR
|
||||||
|
|
||||||
## Neues Projekt starten
|
## Neues Projekt erstellen — Schritt-für-Schritt
|
||||||
|
|
||||||
Nachdem du dieses Repository als Vorlage geklont hast, passe die Platzhalter an dein Projekt an:
|
Diese Anleitung ist so geschrieben, dass ein autonomer Agent oder ein Entwickler sie 1:1 abarbeiten kann, um ein eigenständiges tibi-Projekt mit eigener Code-Server-Subdomain zu erstellen.
|
||||||
|
|
||||||
1. **`.env`**: Ersetze `__PROJECT_NAME__` mit deinem Projektnamen (z.B. `mein-projekt`).
|
### Voraussetzungen
|
||||||
Die folgenden URLs werden automatisch abgeleitet:
|
|
||||||
- `CODING_URL=https://mein-projekt.code.testversion.online`
|
- Zugang zum Code-Server unter `*.code.testversion.online`
|
||||||
- `STAGING_URL=https://dev-mein-projekt.staging.testversion.online`
|
- `git`, `yarn`, `make`, `docker compose` verfügbar
|
||||||
2. **`frontend/spa.html`** / **`api/templates/spa.html`**: Ersetze `__PROJECT_TITLE__` mit dem Seitentitel.
|
- Traefik-Reverse-Proxy läuft auf dem Host (verwaltet `*.code.testversion.online`-Subdomains automatisch via Docker-Labels)
|
||||||
3. **`api/config.yml.env`**: Passe `ADMIN_TOKEN`, Datenbank-Name und weitere Secrets an.
|
|
||||||
4. **`docker-compose-local.yml`**: Suche nach `project_name__` und ersetze mit deinem Projektnamen (Container-Benennung).
|
### 1. Repository klonen (falls noch nicht geschehen)
|
||||||
5. **Demo-Inhalte entfernen**: Die Demo-Seite besteht aus diesen Dateien, die für ein echtes Projekt entfernt/ersetzt werden können:
|
|
||||||
- `frontend/src/blocks/` — Block-Komponenten (HeroBlock, FeaturesBlock, RichtextBlock, AccordionBlock, ContactFormBlock, BlockRenderer, NotFound)
|
Wenn du bereits in einem geklonten Projekt arbeitest, überspringe diesen Schritt und gehe direkt zu **Schritt 2**.
|
||||||
- `frontend/mocking/content.json` — Demo-Mockdaten für Content
|
|
||||||
- `frontend/mocking/navigation.json` — Demo-Mockdaten für Navigation
|
|
||||||
- `api/collections/content.yml` — Content-Collection-Konfiguration
|
|
||||||
- `api/collections/navigation.yml` — Navigation-Collection-Konfiguration
|
|
||||||
- `tests/e2e/demo.spec.ts` — Demo-E2E-Tests
|
|
||||||
- `video-tours/tours/demo-showcase.tour.ts` — Demo-Video-Tour
|
|
||||||
6. **`frontend/src/App.svelte`**: Passe Header, Footer und Content-Loading an dein Datenmodell an.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Platzhalter ersetzen (Beispiel für Linux/Mac):
|
# Im Workspace-Verzeichnis (z.B. /WM_Dev/src/gitbase.de/cms/)
|
||||||
sed -i 's/__PROJECT_NAME__/mein-projekt/g' .env
|
git clone https://gitbase.de/cms/tibi-svelte-starter.git mein-projekt
|
||||||
sed -i 's/__PROJECT_TITLE__/Mein Projekt/g' frontend/spa.html api/templates/spa.html
|
cd mein-projekt
|
||||||
|
git remote rename origin template
|
||||||
|
# Neues Remote-Repo anlegen (z.B. auf gitbase.de) und als origin setzen:
|
||||||
|
# git remote add origin https://gitbase.de/org/mein-projekt.git
|
||||||
```
|
```
|
||||||
|
|
||||||
## Wozu?
|
### 2. Platzhalter ersetzen
|
||||||
|
|
||||||
Via Svelte wird eine SPA (Single-Page-App) programmiert. Dazu wird der Code einmal für den Browser aufgebreitet und außerdem für den Server kompiliert und transpiliert. Der Server-Code wird in einem tibi-server SSR-Hook (server side rendering) eingebunden und generiert dort fertiges HTML anhand der aktuelle Route für SEO und optimierte Ladezeiten.
|
Alle Platzhalter müssen durch die echten Projektwerte ersetzt werden. Hier die vollständige Liste:
|
||||||
|
|
||||||
Die Navigation innerhalb der APP im Browser löst dagegen nur API-Aufrufe aus ohne jedesmal einen SSR-Prozess anzustoßen.
|
| Platzhalter | Wo | Ersetzen durch | Beispiel |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
Um die SSR-Last so gering wie möglich zu halten, wurde ein Caching in der "ssr"-Collection der API implementiert.
|
| `__PROJECT_NAME__` | `.env` | Projektname (kebab-case, wird für URLs und Container verwendet) | `mein-projekt` |
|
||||||
|
| `__TIBI_NAMESPACE__` | `.env` | Tibi-Namespace (snake_case, wird als DB-Prefix und in API-URLs verwendet) | `mein_projekt` |
|
||||||
## Toolchain
|
| `__NAMESPACE__` | `api/config.yml`, `frontend/.htaccess` | Gleicher Wert wie `TIBI_NAMESPACE` | `mein_projekt` |
|
||||||
|
|
||||||
### git
|
|
||||||
|
|
||||||
nach `git clone ...`
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git lfs install
|
# Projektname (kebab-case) — für URLs, Docker-Container, Subdomains
|
||||||
git lfs pull
|
PROJECT=mein-projekt
|
||||||
|
# Tibi-Namespace (snake_case) — für DB, API-Pfade
|
||||||
|
NAMESPACE=mein_projekt
|
||||||
|
|
||||||
|
sed -i "s/__PROJECT_NAME__/$PROJECT/g" .env
|
||||||
|
sed -i "s/__TIBI_NAMESPACE__/$NAMESPACE/g" .env
|
||||||
|
sed -i "s/__NAMESPACE__/$NAMESPACE/g" api/config.yml frontend/.htaccess
|
||||||
```
|
```
|
||||||
|
|
||||||
### Abhängigkeiten laden
|
**Ergebnis in `.env`:**
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
PROJECT_NAME=mein-projekt
|
||||||
|
TIBI_NAMESPACE=mein_projekt
|
||||||
|
CODING_URL=https://mein-projekt.code.testversion.online
|
||||||
|
STAGING_URL=https://dev-mein-projekt.staging.testversion.online
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Seitentitel anpassen
|
||||||
|
|
||||||
|
Der Seitentitel wird dynamisch über `<svelte:head>` in `frontend/src/App.svelte` gesetzt. Die Demo-App verwendet dafür die Konstante `SITE_NAME`. In einem neuen Projekt wird `App.svelte` typischerweise komplett neu geschrieben — dabei einfach sicherstellen, dass `<svelte:head>` mit einem passenden `<title>` vorhanden ist. SSR injiziert ihn dann automatisch über den `<!--HEAD-->`-Platzhalter in `spa.html`.
|
||||||
|
|
||||||
|
### 4. Admin-Token prüfen
|
||||||
|
|
||||||
|
`api/config.yml.env` enthält bereits ein `ADMIN_TOKEN`. Für Produktionsprojekte durch ein sicheres Token ersetzen:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
echo "ADMIN_TOKEN=$(openssl rand -hex 20)" > api/config.yml.env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Subdomains prüfen
|
||||||
|
|
||||||
|
Traefik erkennt die Docker-Labels automatisch. Nach `make docker-up` sind folgende URLs verfügbar:
|
||||||
|
|
||||||
|
| Dienst | URL |
|
||||||
|
| --- | --- |
|
||||||
|
| Website (BrowserSync) | `https://{PROJECT_NAME}.code.testversion.online/` |
|
||||||
|
| Tibi Admin | `https://{PROJECT_NAME}-tibiadmin.code.testversion.online/` |
|
||||||
|
| Tibi Server API | `https://{PROJECT_NAME}-tibiserver.code.testversion.online/` |
|
||||||
|
| Maildev | `https://{PROJECT_NAME}-maildev.code.testversion.online/` |
|
||||||
|
|
||||||
|
**Es ist KEINE manuelle Traefik-Konfiguration nötig.** Die Subdomains werden automatisch über das Docker-Label `online.testversion.code.subdomain=${PROJECT_NAME}` registriert. Traefik überwacht Docker-Events und erstellt die Routen dynamisch.
|
||||||
|
|
||||||
|
### 6. Starten
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn install
|
yarn install
|
||||||
```
|
make docker-up # Stack im Hintergrund starten
|
||||||
|
|
||||||
### Entwickeln auf dem Code-Server mit Docker Compose Stack
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make docker-start
|
|
||||||
# oder
|
# oder
|
||||||
make docker-up
|
make docker-start # Stack im Vordergrund (CTRL-C zum Stoppen)
|
||||||
make docker-down
|
|
||||||
|
|
||||||
# "make help" zeigt alle Kommandos
|
|
||||||
```
|
```
|
||||||
|
|
||||||
| UI | URL |
|
Prüfe, ob alles läuft:
|
||||||
| --- | --- |
|
|
||||||
| Website | <https://tibi-svelte-starter.code.testversion.online/> |
|
|
||||||
| Tibi Admin | <https://tibi-svelte-starter-tibiadmin.code.testversion.online/> |
|
|
||||||
| Maildev | <https://tibi-svelte-starter-maildev.code.testversion.online/> |
|
|
||||||
|
|
||||||
### Bauen
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# moderne Browser
|
make docker-ps # Container-Status anzeigen
|
||||||
yarn build
|
make docker-logs # Logs verfolgen
|
||||||
|
```
|
||||||
# alte Browser (IE11)
|
|
||||||
yarn build:legacy
|
### 7. Mock-Modus (Frontend ohne tibi-server)
|
||||||
|
|
||||||
# serverseitiges Rendering
|
Für schnelle UI-Entwicklung ohne Backend:
|
||||||
yarn build:server
|
|
||||||
|
```sh
|
||||||
# Admin-Module
|
# In .env setzen:
|
||||||
yarn build:admin
|
MOCK=1
|
||||||
|
# Dann:
|
||||||
|
make docker-up
|
||||||
|
```
|
||||||
|
|
||||||
|
Mock-Daten liegen in `frontend/mocking/` als JSON-Dateien. Fehlende Endpunkte liefern 404.
|
||||||
|
|
||||||
|
### 8. Demo-Inhalte entfernen
|
||||||
|
|
||||||
|
Für ein echtes Projekt die Demo-Dateien entfernen/ersetzen:
|
||||||
|
|
||||||
|
| Datei/Ordner | Inhalt |
|
||||||
|
| --- | --- |
|
||||||
|
| `frontend/src/blocks/` | Demo-Block-Komponenten (HeroBlock, FeaturesBlock, etc.) |
|
||||||
|
| `frontend/mocking/content.json` | Demo-Mockdaten für Content |
|
||||||
|
| `frontend/mocking/navigation.json` | Demo-Mockdaten für Navigation |
|
||||||
|
| `api/collections/content.yml` | Content-Collection-Konfiguration |
|
||||||
|
| `api/collections/navigation.yml` | Navigation-Collection-Konfiguration |
|
||||||
|
| `tests/e2e/` | Demo-E2E-Tests |
|
||||||
|
| `video-tours/tours/` | Demo-Video-Tour |
|
||||||
|
|
||||||
|
Danach `frontend/src/App.svelte` (Header, Footer, Content-Loading) an das eigene Datenmodell anpassen.
|
||||||
|
|
||||||
|
### 9. Build und Deployment
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn build # Frontend für moderne Browser
|
||||||
|
yarn build:server # SSR-Bundle (für tibi-server goja-Hooks)
|
||||||
|
yarn build:admin # Admin-Module (optional)
|
||||||
|
yarn validate # TypeScript + Svelte prüfen (muss 0 Fehler/Warnungen zeigen)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checkliste für autonome Agents
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ ] Repository geklont und Remote gesetzt
|
||||||
|
[ ] __PROJECT_NAME__ in .env ersetzt
|
||||||
|
[ ] __TIBI_NAMESPACE__ in .env ersetzt
|
||||||
|
[ ] __NAMESPACE__ in api/config.yml und frontend/.htaccess ersetzt
|
||||||
|
[ ] App.svelte mit eigenem Template und <svelte:head> (inkl. <title>) erstellt
|
||||||
|
[ ] ADMIN_TOKEN in api/config.yml.env gesetzt
|
||||||
|
[ ] yarn install ausgeführt
|
||||||
|
[ ] make docker-up gestartet
|
||||||
|
[ ] Website unter https://{PROJECT_NAME}.code.testversion.online/ erreichbar
|
||||||
|
[ ] yarn validate zeigt 0 Fehler und 0 Warnungen
|
||||||
|
[ ] yarn build und yarn build:server erfolgreich
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
|
||||||
|
Via Svelte wird eine SPA (Single-Page-App) programmiert. Der Code wird einmal für den Browser aufbereitet und außerdem für den Server kompiliert und transpiliert. Der Server-Code wird in einem tibi-server SSR-Hook eingebunden und generiert fertiges HTML anhand der aktuellen Route — für SEO und optimierte Ladezeiten.
|
||||||
|
|
||||||
|
Die Navigation innerhalb der App löst nur API-Aufrufe aus, ohne jedes Mal SSR anzustoßen. Ein Cache in der `ssr`-Collection minimiert die SSR-Last.
|
||||||
|
|
||||||
|
**SSR-Details:**
|
||||||
|
|
||||||
|
- `<title>` und `<meta description>` kommen über `<svelte:head>` aus `App.svelte` → SSR injiziert sie in den `<!--HEAD-->`-Platzhalter von `spa.html`
|
||||||
|
- `<html lang>` wird vom SSR-Hook (`api/hooks/ssr/get_read.js`) anhand der URL-Sprache gesetzt
|
||||||
|
- SSR-Bundle wird mit `yarn build:server` erstellt und landet in `api/hooks/lib/app.server.js`
|
||||||
|
|
||||||
|
**Weiteres Build-Target:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn build:admin # Admin-Module
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -131,23 +131,6 @@ services:
|
|||||||
- ./tmp/mongo-data:/data/db
|
- ./tmp/mongo-data:/data/db
|
||||||
user: ${CODER_UID}:${CODER_GID}
|
user: ${CODER_UID}:${CODER_GID}
|
||||||
|
|
||||||
adminmongo:
|
|
||||||
profiles:
|
|
||||||
- tibi
|
|
||||||
- tibi-dev
|
|
||||||
image: gitbase.de/server/adminmongo
|
|
||||||
environment:
|
|
||||||
CONN_NAME: mongo
|
|
||||||
DB_HOST: mongo
|
|
||||||
PORT: 1234
|
|
||||||
expose:
|
|
||||||
- 1234
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- online.testversion.code.subdomain=${PROJECT_NAME}-adminmongo
|
|
||||||
- traefik.http.routers.${PROJECT_NAME}-adminmongo.middlewares=${PROJECT_NAME}-adminmongo
|
|
||||||
- traefik.http.middlewares.${PROJECT_NAME}-adminmongo.basicauth.usersfile=${PWD}/.basic-auth-code
|
|
||||||
|
|
||||||
maildev:
|
maildev:
|
||||||
profiles:
|
profiles:
|
||||||
- tibi
|
- tibi
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="appContainer"><!--HTML--></div>
|
<div id="appContainer"><!--HTML--></div>
|
||||||
<script type="module" src="/dist/index.mjs?t=__TIMESTAMP__"></script>
|
<script type="module" src="/dist/index.mjs?t=__TIMESTAMP__"></script>
|
||||||
<script nomodule src="/dist/index.es5.js?t=__TIMESTAMP__"></script>
|
|
||||||
<!-- <script
|
<!-- <script
|
||||||
src="//cc.webmakers.de/cc.js"
|
src="//cc.webmakers.de/cc.js"
|
||||||
defer
|
defer
|
||||||
|
|||||||
62
scripts/ci-deploy.sh
Executable file
62
scripts/ci-deploy.sh
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# CI step: deploy to production via rsync
|
||||||
|
# Called from .gitea/workflows/deploy.yml
|
||||||
|
# Required env: RSYNC_USER, RSYNC_PASS, BRANCH
|
||||||
|
|
||||||
|
# Read .env
|
||||||
|
set -o allexport
|
||||||
|
. ./.env
|
||||||
|
. ./api/config.yml.env
|
||||||
|
set +o allexport
|
||||||
|
|
||||||
|
# Validate required env vars
|
||||||
|
for var in RSYNC_USER RSYNC_PASS RSYNC_HOST RSYNC_PORT; do
|
||||||
|
if [ -z "${!var:-}" ]; then
|
||||||
|
echo "${var} missing, exiting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Deploying ${BRANCH} to ${RSYNC_HOST} on port ${RSYNC_PORT} as ${RSYNC_USER}"
|
||||||
|
|
||||||
|
# Exclude source and sourcemaps on master deploy
|
||||||
|
excludes=""
|
||||||
|
if [ "${BRANCH}" = "master" ]; then
|
||||||
|
excludes='--exclude=src --exclude=*.map'
|
||||||
|
echo "master deploy, excluding ${excludes}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SSH_CMD="sshpass -p ${RSYNC_PASS} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${RSYNC_PORT} -l ${RSYNC_USER}"
|
||||||
|
|
||||||
|
# Sync frontend
|
||||||
|
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||||
|
--delete \
|
||||||
|
-e "${SSH_CMD}" \
|
||||||
|
${excludes} \
|
||||||
|
frontend/ \
|
||||||
|
"${RSYNC_HOST}":./frontend/
|
||||||
|
|
||||||
|
# Sync API config and hooks
|
||||||
|
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||||
|
--delete \
|
||||||
|
-e "${SSH_CMD}" \
|
||||||
|
api/ \
|
||||||
|
"${RSYNC_HOST}":./api/
|
||||||
|
|
||||||
|
# Ensure media directory exists on remote
|
||||||
|
mkdir -p media
|
||||||
|
chmod 770 media
|
||||||
|
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||||
|
-e "${SSH_CMD}" \
|
||||||
|
media \
|
||||||
|
"${RSYNC_HOST}":./
|
||||||
|
|
||||||
|
# Reload tibi-server config
|
||||||
|
reloadUrl="${LIVE_URL}/api/_/admin/reload"
|
||||||
|
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
||||||
|
|
||||||
|
# Clear SSR cache
|
||||||
|
ssrUrl="${LIVE_URL}/api/ssr"
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
||||||
45
scripts/ci-modify-config.sh
Executable file
45
scripts/ci-modify-config.sh
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# CI step: modify config for deployment
|
||||||
|
# Called from .gitea/workflows/deploy.yml
|
||||||
|
|
||||||
|
# Enable sentry
|
||||||
|
sed -i 's#\(sentryEnvironment.*\)".*"#\1"'"${GITHUB_REF_NAME}"'"#g' frontend/src/config.ts
|
||||||
|
sed -i 's#//\( sentry\\.init.*\)#\1#g' frontend/src/config.ts
|
||||||
|
sed -i 's#metrictCall = false#metrictCall = true#g' frontend/src/config.ts
|
||||||
|
|
||||||
|
# Build release string from git info
|
||||||
|
set -o allexport
|
||||||
|
. ./.env
|
||||||
|
echo "PROJECT_RELEASE=${SENTRY_PROJECT}.r$(git rev-list HEAD --count)-$(git describe --all --long | sed 's+/+-+')" >> .env
|
||||||
|
. ./.env
|
||||||
|
set +o allexport
|
||||||
|
|
||||||
|
echo "______ .env ______"
|
||||||
|
cat .env
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Inject release and origin URL into hooks
|
||||||
|
sed -i 's#\(const release = \).*#\1"'"${PROJECT_RELEASE}"'"#g' api/hooks/config-client.js
|
||||||
|
sed -i 's#\(const originURL = \).*#\1"'"${LIVE_URL}"'"#g' api/hooks/config-client.js
|
||||||
|
|
||||||
|
# Inject cache-busting timestamp
|
||||||
|
stamp=$(date +%s)
|
||||||
|
sed -i "s/__TIMESTAMP__/${stamp}/g" frontend/spa.html
|
||||||
|
|
||||||
|
# Copy spa.html to api/templates (remove symlink or file first)
|
||||||
|
if [ -d api/templates ]; then
|
||||||
|
[ -L api/templates/spa.html ] && rm api/templates/spa.html
|
||||||
|
[ -f api/templates/spa.html ] && rm api/templates/spa.html
|
||||||
|
cp frontend/spa.html api/templates/spa.html
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "______ frontend/spa.html ______"
|
||||||
|
cat frontend/spa.html
|
||||||
|
|
||||||
|
# Set preview URL
|
||||||
|
sed -i 's#\(PREVIEW_URL=\).*#\1'"${LIVE_URL}"'/preview#g' api/config.yml.env
|
||||||
|
|
||||||
|
echo "______ api/config.yml.env ______"
|
||||||
|
cat api/config.yml.env
|
||||||
37
scripts/ci-staging.sh
Executable file
37
scripts/ci-staging.sh
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# CI step: deploy to staging environment
|
||||||
|
# Called from .gitea/workflows/deploy.yml
|
||||||
|
# Required env: API_BASEDIR, COMPOSE_PROJECT_NAME
|
||||||
|
|
||||||
|
# Read .env
|
||||||
|
set -o allexport
|
||||||
|
. ./.env
|
||||||
|
. ./api/config.yml.env
|
||||||
|
set +o allexport
|
||||||
|
|
||||||
|
# Sanitize compose project name (replace / with -)
|
||||||
|
COMPOSE_PROJECT_NAME=$(echo "${COMPOSE_PROJECT_NAME}" | sed 's+/+-+g')
|
||||||
|
export COMPOSE_PROJECT_NAME
|
||||||
|
|
||||||
|
# Sync files to staging directory
|
||||||
|
mkdir -p "${API_BASEDIR}/frontend"
|
||||||
|
rsync -av api "${API_BASEDIR}/"
|
||||||
|
rsync -av frontend/dist "${API_BASEDIR}/frontend/"
|
||||||
|
rsync -av frontend/assets "${API_BASEDIR}/frontend/"
|
||||||
|
sed -i 's#\(PREVIEW_URL=\).*#\1'"${STAGING_URL}"'/preview#g' "${API_BASEDIR}/api/config.yml.env"
|
||||||
|
|
||||||
|
# Start staging stack
|
||||||
|
docker compose -f docker-compose-staging.yml -p "${COMPOSE_PROJECT_NAME}" up -d --build --remove-orphans
|
||||||
|
|
||||||
|
# Wait for API to be ready
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Reload tibi-server config
|
||||||
|
reloadUrl="${STAGING_URL}/api/_/admin/reload"
|
||||||
|
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
||||||
|
|
||||||
|
# Clear SSR cache
|
||||||
|
ssrUrl="${STAGING_URL}/api/ssr"
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
||||||
32
scripts/ci-upload-sourcemaps.sh
Executable file
32
scripts/ci-upload-sourcemaps.sh
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# CI step: upload sourcemaps to Sentry
|
||||||
|
# Called from .gitea/workflows/deploy.yml
|
||||||
|
# Required env: SENTRY_AUTH_TOKEN (passed from workflow secrets)
|
||||||
|
|
||||||
|
# Read .env
|
||||||
|
set -o allexport
|
||||||
|
. ./.env
|
||||||
|
set +o allexport
|
||||||
|
|
||||||
|
# Skip gracefully if no token is configured
|
||||||
|
if [ -z "${SENTRY_AUTH_TOKEN:-}" ]; then
|
||||||
|
echo "SENTRY_AUTH_TOKEN not set, skipping sourcemap upload"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "SENTRY_URL=${SENTRY_URL:-}"
|
||||||
|
echo "SENTRY_ORG=${SENTRY_ORG:-}"
|
||||||
|
echo "SENTRY_PROJECT=${SENTRY_PROJECT:-}"
|
||||||
|
echo "PROJECT_RELEASE=${PROJECT_RELEASE:-}"
|
||||||
|
|
||||||
|
echo "Injecting debug IDs..."
|
||||||
|
npx sentry-cli sourcemaps --log-level info inject frontend/dist
|
||||||
|
|
||||||
|
echo "Creating release and uploading sourcemaps..."
|
||||||
|
npx sentry-cli sourcemaps --log-level info upload \
|
||||||
|
--release="${PROJECT_RELEASE:-${SENTRY_PROJECT}.dirty}" \
|
||||||
|
--url-prefix='~/dist' \
|
||||||
|
--ext js --ext mjs --ext map \
|
||||||
|
frontend/dist
|
||||||
Reference in New Issue
Block a user