Build, futtatás, Azure üzemeltetés
BC-087 döntés óta (2026-11-03, késő este) a Forrás-Code desktop-elsődleges: az
Electron app az egyetlen tényleges kliens, a web app csak marketing + vásárlás. A
kurzusvideó Azure Blob Storage-ban van (l. BC-013, Craftingtable), a Strapi saját
media library-ján keresztül feltöltve, aláírt (SAS) URL-lel kiszolgálva — ugyanazt az
URL-t használja az online lejátszás (<video>) és a Go sidecar offline letöltése is.
1. Előfeltételek
| Eszköz | Verzió | Mire kell |
|---|---|---|
| Node.js | 20+ (a repo 22-t használ) | backend, web, desktop |
| pnpm | corepack enable elég | monorepo package manager |
| Go | 1.22+ | a desktop Go sidecar fordításához (apps/desktop/sidecar) |
| Docker | — | Postgres, Adminer, Azurite (lokális Azure Blob emulátor) |
cd business-apps/forras-code
pnpm install
Backend env: másold apps/backend/.env.example-t apps/backend/.env-re, és töltsd ki
(l. 3. és 4. pont a konkrét Azure/Azurite értékekhez).
2. Lokális fejlesztői futtatás (mind a három app egyszerre)
pnpm run dev
Ez (package.json gyökér predev/dev szkriptje):
docker compose up -d— elindítja a Postgres-t (és az Adminer-t, Azurite-ot, ha fel van véve adocker-compose.yml-be, l. 4. pont).- Párhuzamosan (
concurrently) elindítja:- a Strapi backend-et (
:1337), - a web app Vite dev szerverét (
:5173), - és — amint a
:5173felállt (wait-on) — az Electron desktop appot, ami a Vite dev szerverre töltődik be (FORRAS_CODE_WEB_DEV_URL, alapértelmezettenhttp://localhost:5173).
- a Strapi backend-et (
Az Electron dev szkriptje (apps/desktop/package.json) minden indításkor lefordítja
a Go sidecar-t (go build) — ezért kell lokálisan telepített Go. Sikeres bejelentkezés
után a renderer elindítja a sidecar-t (sidecar:start IPC), ami egy helyi, csak localhost-on
figyelő, Bearer-tokennel védett HTTP szervert indít (l. a
SKL-electron-desktop-csomagolas-es-offline-first-eloirasok Craftingtable skillt).
Csak egy-egy app külön:
pnpm run dev:backend # Strapi :1337
pnpm run dev:web # Vite :5173
pnpm run dev:desktop # Electron (a web dev szerverre csatlakozik)
3. Lokális Azure Blob reprodukálás Docker-ben (Azurite)
Nem kell valós Azure fiók a fejlesztéshez — a docker-compose.yml tartalmaz egy
Azurite szolgáltatást, ami a Blob Storage API-t pontosan úgy emulálja, mint a
valós Azure. A backend kódja (src/utils/azure-blob.ts,
strapi-provider-upload-azure-storage-v5) változtatás nélkül, ugyanazokkal az
env-kulcsokkal megy Azurite ellen, mint valós Azure ellen — csak az URL-ek különböznek.
docker compose up -d azurite
apps/backend/.env (l. .env.example "Opció A" blokk — ez Azurite publikus,
mindenki gépén azonos fejlesztői alapértelmezése, NEM titok):
AZURE_STORAGE_ACCOUNT=devstoreaccount1
AZURE_STORAGE_ACCOUNT_KEY=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
AZURE_STORAGE_CONTAINER=forras-code-videos
AZURE_STORAGE_URL=http://127.0.0.1:10000/devstoreaccount1
Indítsd újra a backendet — a konténer automatikusan létrejön (a provider
createIfNotExists()-et hív induláskor). Ha a Strapi admin panelen (Platform
Settings) a videoMockMode be van kapcsolva, kapcsold ki, különben a chapter
controller mindig mock módban marad (l. isVideoMockModeEnabled()), és sosem próbál
tényleges Azure/Azurite hívást.
Két buktató, amire a bevezetés közben derült fény (mindkettő már megoldva a kódban/compose-ban, csak dokumentálva, hogy miért van úgy, ahogy van):
- Azurite API-verzió — az
@azure/storage-blobSDK újabb API-verziót küld, mint amit egy adott Azurite image ismer, ezért adocker-compose.yml-ben az Azurite parancs--skipApiVersionCheckflaggel fut. Nélküle minden kérés"API version ... is not supported by Azurite"hibával esik el. - SAS protokoll — a valós Azure Blob Storage mindig HTTPS, de Azurite lokálisan
sima HTTP-n fut. A
generateSignedVideoUrl()(src/utils/azure-blob.ts) ezért a SASprotocolparaméterét aserviceBaseURLalapján választja:https://eseténSASProtocol.Https, minden más esetén (pl. Azuritehttp://127.0.0.1:10000/...)SASProtocol.HttpsAndHttp. Éles Azure ellen ez nem változtat semmit (aserviceBaseURLott mindighttps://).
Végig tesztelve (2026-11-03): Strapi upload → Azurite → aláírt SAS URL → teljes
fájl letöltés (byte-exact) → Range (206 Partial Content) kérés → Go sidecar
/downloads végpontja ugyanerről a SAS URL-ről darabolva letölt, AES-256-GCM-mel
titkosítva tárol, és dekódolva, byte-exact módon szolgál ki.
Blobok megtekintése Azurite-ban (opcionális, hibakereséshez): Azure Storage Explorer → "Attach to a local emulator", vagy Azure CLI-vel:
az storage container list \
--connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"
4. Teljes (production-szerű) build
pnpm run build:backend # Strapi admin + TS build (apps/backend/dist)
pnpm run build:web # Vite production build (apps/web/dist)
pnpm run build:desktop # Go sidecar build + Electron TS build
Desktop csomagolás (telepítő előállítása az adott OS-re):
cd apps/desktop
pnpm run package # electron-builder — mac: .dmg, win: nsis .exe, linux: AppImage/.deb
A kimenet apps/desktop/release/ alatt jön létre. Az electron-builder konfig
(package.json "build" blokk) a lefordított web app-ot (../web/dist) és a
lefordított Go sidecar binárist (sidecar/dist) is becsomagolja — tehát előbb
build:web és a sidecar build (build:desktop ezt már megcsinálja) kell, hogy
lefusson.
Docker image-ek (backend + web — a web image jelenleg nem a tényleges futó klienshez kell, hiszen a web app csak marketing+vásárlás, de CI-ban leteszteli, hogy a build reprodukálható):
docker build -t forras-code-backend -f apps/backend/Dockerfile apps/backend
docker build -t forras-code-web -f apps/web/Dockerfile apps/web
CI-ban ez már megy (forras-code-backend-ci.yml, forras-code-web-ci.yml) —
jelenleg szándékosan nem push-olnak sehová (nincs még konfigurált éles
registry/hoszt, l. 5.2 pont).
5. Mit kell létrehozni/konfigurálni Azure-ban (éles üzemhez)
5.1. Azure Storage Account + Blob Container (kurzusvideó) — ez kell feltétlenül
Azure Portal:
- Create a resource → Storage account → Standard performance, LRS (vagy GRS, ha több régiós redundancia kell) → hozd létre.
- A resource → Containers → + Container → név (pl.
forras-code-videos) → Access level: Private (fontos — a videó sosem publikus, csak a saját SAS-tokenünkkel kiszolgált). - A resource → Access keys → key1 → Show → ez lesz
AZURE_STORAGE_ACCOUNT_KEY, a resource neveAZURE_STORAGE_ACCOUNT.
Vagy Azure CLI-vel egyszerűbben:
az group create -n forras-code-prod -l westeurope
az storage account create \
-n <storage-account-neve> -g forras-code-prod -l westeurope \
--sku Standard_LRS --kind StorageV2
az storage container create \
--account-name <storage-account-neve> \
--name forras-code-videos --public-access off
az storage account keys list \
--account-name <storage-account-neve> -g forras-code-prod -o table
A kapott értékeket töltsd a (production) .env-be:
AZURE_STORAGE_ACCOUNT=<storage-account-neve>
AZURE_STORAGE_ACCOUNT_KEY=<key1 értéke>
AZURE_STORAGE_CONTAINER=forras-code-videos
# AZURE_STORAGE_URL nincs megadva → a kód a https://<account>.blob.core.windows.net
# alapértelmezést használja, ami éles Azure-nál mindig helyes.
CORS-ot NEM kell beállítani a Storage Account-on. A videó lejátszás egy sima
<video src="..."> tag-gel megy (VideoPlayer.tsx) — ehhez a böngésző nem követeli
meg CORS header-t (az csak fetch()/canvas/XHR-szintű hozzáféréshez kellene). A Go
sidecar letöltése sem böngészőből megy, hanem natív Go HTTP kliensből — a CORS egy
tisztán böngésző-oldali mechanizmus, oda nem vonatkozik.
5.2. Hol fusson a Strapi backend éles üzemben (javasolt, külön munka)
Ehhez a session-höz nem tartozott hozzá — a backend jelenleg csak CI-ban buildelődik
(push: false), nincs még élesben futó hosztja. A monorepóban már van egy bevált
minta hasonló (stateful, Postgres-hez kötött) szolgáltatásra — a Fider
(infra/azure-deploy.md 5. pont) Azure Container Apps-ra megy. Javasolt ugyanez
a Strapi backendnek is:
- Azure Container Apps — a meglévő
apps/backend/Dockerfileimage-et futtatja, HTTPS-t, scale-to-zero-t és custom domaint ad natívan. - Azure Database for PostgreSQL – Flexible Server (legkisebb Burstable tier) —
DATABASE_HOST/DATABASE_PASSWORDide mutat,DATABASE_SSL=true. - Image push: Azure Container Registry (ACR) vagy GitHub Container Registry — a
meglévő
forras-code-backend-ci.ymlworkflow-t egydocker/build-push-action+az containerapp updatelépéssel kellene kiegészíteni. - A feltöltött fájlok (
public/uploads) ÉRDEMES volume-ba tenni Container Apps-nál is — bár videó már nem oda megy (Azure Blob-ba), a Strapi admin média (pl. kurzus thumbnail) helyben tárolódna konténer-újraindításkor elveszve, ha nincs volume. Alternatíva: állítsd azuploadprovider-t MINDEN médiatípusra Azure Blob-ra, ne csak a chapter videóra (jelenleg csak avideomezőn megy keresztül a custom controller logika, de aconfig/plugins.tsglobálisan konfigurálja a providert, tehát a thumbnail/materialsFile is Azure-ba menne, ha be van állítva).
Ez egy önálló, külön átgondolást igénylő feladat (registry választás, Container Apps env/scaling, CI workflow bővítés) — szólj, ha ezt is lépésről lépésre végig akarod vinni, az egy új munkamenet/session legyen.
5.3. M365 SMTP — nem Azure resource, hanem a tenant beállítása
Ez a Microsoft 365 admin center-ben van, nem az Azure Portalon: Postafiók → Beállítások → SMTP AUTH engedélyezve. Utána:
SMTP_HOST=smtp.office365.com
SMTP_PORT=587
SMTP_USERNAME=<az M365 postafiók email címe>
SMTP_PASSWORD=<az M365 postafiók jelszava, vagy app password, ha MFA van>
SMTP_DEFAULT_FROM=<ugyanaz, mint SMTP_USERNAME>
Amíg ez üres, a rendszer jsonTransport/mock módban fut (l. config/plugins.ts
email blokk) — nem küld valós emailt, csak logol. Production-ban (NODE_ENV=production)
a bootstrap (src/index.ts) hard-fail-t dob, ha ez hiányzik — szándékosan, hogy a
jelszó-visszaállítás sose tűnjön "sikeresnek" anélkül, hogy valódi emailt küldene.
5.4. Lemon Squeezy — nem Azure resource
A fizetés/előfizetés külső szolgáltatás (LEMONSQUEEZY_* env-ek,
app.lemonsqueezy.com) — ehhez nincs Azure-oldali
teendő, csak a webhook endpoint-nak kell élesben elérhetőnek lennie (l. 5.2 — ha a
backend nincs hostolva, a webhook sem tud megérkezni).
6. Ismert korlátok / nyitott pontok (ezt a build/deploy leírást nem érintik, de ide tartoznak)
- A Go sidecar letöltött videójának lejátszása közvetlenül a lokális cache-ből
(
<video>elem a sidecar lokális szerverét érje el, auth token nélkül a renderer-ben) még nincs bekötve — jelenleg csak a letöltés/tárolás kész, l.apps/web/src/lib/offlineDownload.tsmegjegyzése. - A feliratok (captions)
.vtttartalma még nincs tárolva, csak jelző mezők (hasCaptions/captionsLang) — l.uploadCaptionscontroller megjegyzése. Chapter.video(a media reláció) jelenleg nincspopulate-elve afetchChapterslista-hívásban — ezért a tartalomjegyzék "▶" jelzése azure-videós fejezeteknél nem jelenik meg (csak mock-nál). Nem biztonsági probléma (avideo.urlamúgy sem lenne SAS-aláírt, tehát nem kellene publikusan populate-elni élesben sem) — ha a jelzés fontos, egy külön, entitlement-mentes "van-e videó" boolean mezőt érdemes felvenni, NEM populate-elni a teljes media relációt.