Bestehende App plus Gitea-Actions-Pipeline: Tag v* loest Build, Tests und Container-Neustart auf dem VPS aus. - .gitea/workflows/deploy.yml: npm ci/build/test als Gate, danach docker compose up -d --build. Die .env wird aus dem Repo-Secret DOTENV erzeugt, damit auf dem Host keine Secret-Datei gepflegt werden muss. - docker-compose.yml: fester Projektname (der CI-Job hat ein anderes Arbeitsverzeichnis als der Host), Image mit Versionstag fuer Rollback, fester Volume-Name. - README: Deployment- und Rollback-Abschnitt, Token-Rotation angepasst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
119 lines
4.6 KiB
Markdown
119 lines
4.6 KiB
Markdown
# busyfeed
|
|
|
|
Belegtzeiten aus dem Exchange-Kalender auf dem iPhone als ICS-Feed fuer Cal.com.
|
|
|
|
Nach aussen gehen **ausschliesslich Start- und Endzeitpunkte**. Keine Titel, keine
|
|
Orte, keine Teilnehmer, keine Notizen. Das ist keine Einstellung, die man falsch
|
|
setzen kann: Der Kurzbefehl liest diese Felder gar nicht erst aus, der Server kann
|
|
sie also weder speichern noch ausliefern.
|
|
|
|
```
|
|
iPhone (Kurzbefehl, 4x taeglich) -> POST /api/sync -> GET /feed/<token>.ics -> Cal.com
|
|
```
|
|
|
|
## Server einrichten
|
|
|
|
Zwei Tokens erzeugen:
|
|
|
|
```bash
|
|
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))" # SYNC_TOKEN
|
|
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))" # FEED_TOKEN
|
|
```
|
|
|
|
Dann in Gitea ein Repo-Secret `DOTENV` anlegen (Settings -> Actions -> Secrets)
|
|
mit dem kompletten `.env`-Inhalt - Vorlage ist `.env.example`. Auf dem Server
|
|
liegt keine `.env`; das Deployment erzeugt sie daraus, siehe
|
|
[Deployment](#deployment).
|
|
|
|
Im nginx proxy manager einen Proxy Host anlegen: `busy.example.de` -> `busyfeed:8080`,
|
|
Let's-Encrypt-Zertifikat ausstellen, "Force SSL" aktivieren. Das Compose-File hat
|
|
bewusst **kein** `ports:`-Mapping - NPM erreicht den Container ueber das gemeinsame
|
|
Netz `nginx-proxy-manager_default`, es liegt nichts offen am Host.
|
|
|
|
## Der Kurzbefehl
|
|
|
|
Kurzbefehle-App, neuer Kurzbefehl `Belegtzeiten senden`:
|
|
|
|
1. **Kalendereignisse suchen**
|
|
- Filter: `Kalender` ist *[dein Exchange-Kalender]*
|
|
- Filter: `Startdatum` ist nach `heute -1 Tag` **und** vor `heute +90 Tage`
|
|
- Limit: aus
|
|
2. **Wiederholen mit jedem Element**
|
|
- `Details von Kalendereignissen abrufen` -> *Startdatum* -> `Datum formatieren` -> ISO 8601
|
|
- dasselbe fuer *Enddatum*
|
|
- `Text`: `[Startdatum]|[Enddatum]`
|
|
3. **Text kombinieren**, Trennzeichen: neue Zeile
|
|
4. **Inhalt von URL abrufen**
|
|
- URL `https://busy.example.de/api/sync`, Methode `POST`
|
|
- Header `Authorization: Bearer <SYNC_TOKEN>`
|
|
- Request Body `JSON`, ein Feld `events` (Text) -> die kombinierte Textvariable
|
|
|
|
Ein einzelnes Textfeld statt eines echten JSON-Arrays ist Absicht: Arrays in
|
|
Shortcuts zu bauen ist muehsam und fehleranfaellig.
|
|
|
|
**Automationen:** Kurzbefehle -> Automation -> Tageszeit, vier Stueck
|
|
(z. B. 07:00 / 11:00 / 15:00 / 19:00), jeweils "Vor dem Ausfuehren fragen" **aus**.
|
|
Stuendlich geht nicht - Tageszeit-Automationen kennen nur taeglich/woechentlich/monatlich.
|
|
|
|
## Cal.com verbinden
|
|
|
|
Settings -> Calendars -> "Check for conflicts" -> +Add -> ICS Feed -> Feed-URL einfuegen.
|
|
|
|
## Verhalten im Fehlerfall
|
|
|
|
| Situation | Verhalten |
|
|
|---|---|
|
|
| Snapshot veraltet | Wird **weiter ausgeliefert**. Ein alter Belegt-Feed ist sicherer als ein leerer - leer hiesse "alles frei" und wuerde Doppelbuchungen verursachen. Das Alter steht im `X-WR-CALDESC`. |
|
|
| Sync liefert null Termine | Wird mit **409 abgelehnt**, der alte Snapshot bleibt. Sonst blankt ein kaputter Kurzbefehl den Feed. Bewusstes Leeren: `POST /api/sync?allowEmpty=1`. |
|
|
| Einzelne Zeilen unparsbar | Werden uebersprungen und in der Antwort als `skippedLines` zurueckgemeldet. |
|
|
| Falsches Feed-Token | `404`, nicht `401` - die Existenz des Feeds soll ohne Token nicht erkennbar sein. |
|
|
|
|
## Nicht verhandelbar
|
|
|
|
- **Alle Zeiten als UTC mit `Z`, niemals `TZID`.** Cal.com interpretiert `TZID` falsch
|
|
([calcom/cal.com#14664](https://github.com/calcom/cal.com/issues/14664), offen seit
|
|
April 2024). Termine landen sonst zur falschen Uhrzeit und es entstehen Doppelbuchungen.
|
|
- **Kein `RRULE`.** Der Kurzbefehl liefert Serientermine bereits als Einzelvorkommen.
|
|
|
|
Beides ist durch Tests abgesichert: `npm test`.
|
|
|
|
## Sicherheit
|
|
|
|
Die Feed-URL ist eine **Capability-URL** - ICS-Abos koennen in vielen Clients keine
|
|
Authentifizierung, der Schutz ist allein der unratbare Token im Pfad. Diese URL ist
|
|
faktisch ein Passwort und gehoert in keinen Chat und kein Ticket. Zum Rotieren:
|
|
`FEED_TOKEN` im Secret `DOTENV` aendern, neuen Tag pushen, Abo in Cal.com neu anlegen.
|
|
|
|
## Deployment
|
|
|
|
Deployt wird per Tag. Kein SCP, kein manuelles Neubauen.
|
|
|
|
```bash
|
|
git tag v1.0.1
|
|
git push origin v1.0.1
|
|
```
|
|
|
|
Der Gitea-Runner auf dem VPS baut, testet und startet den Container neu.
|
|
**Rote Tests brechen den Lauf ab, bevor irgendetwas deployt wird.**
|
|
|
|
Vorausgesetzt: das Repo-Secret `DOTENV` ist gesetzt und auf dem VPS laeuft ein
|
|
registrierter `act_runner` mit Zugriff auf den Docker-Socket.
|
|
|
|
Rollback auf eine aeltere Version, direkt auf dem VPS:
|
|
|
|
```bash
|
|
IMAGE_TAG=v1.0.0 docker compose up -d
|
|
```
|
|
|
|
Ohne `--build` - das alte Image liegt noch lokal.
|
|
|
|
## Lokal entwickeln
|
|
|
|
```bash
|
|
npm install
|
|
npm run build
|
|
npm test
|
|
SYNC_TOKEN=lokal-test-token-1234 FEED_TOKEN=lokal-feed-token-1234 \
|
|
DATA_DIR=./.local-data PORT=8099 node dist/server.js
|
|
```
|