Skip to content

Commit b15a748

Browse files
committed
fix: fixed missing not having license stuff
1 parent 8c2c55e commit b15a748

3 files changed

Lines changed: 157 additions & 6 deletions

File tree

.github/workflows/release.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ jobs:
8282
fi
8383
8484
- name: Build
85+
# PANO_LICENSE_SERVER tells the theme's prebuild script which panomc.com
86+
# license server to fetch the verification public key from. Free themes
87+
# (manifest.premium=false) ignore this; premium themes require it (or a
88+
# PANO_LICENSE_PUBLIC_KEY secret). main branch → prod, anything else → dev.
89+
env:
90+
PANO_LICENSE_SERVER: ${{ steps.extract_branch.outputs.branch == 'main' && 'prod' || 'dev' }}
8591
run: bun run build
8692

8793
- name: Update manifest.json version

README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Pano Vanilla Theme
2+
3+
Pano'nun varsayılan teması. Aynı zamanda yeni tema oluştururken **template** olarak kullanılır — bütün license/DRM altyapısı bu klasörde hazır, yeni temayı bunu kopyalayarak başlatırsan otomatik miras alır.
4+
5+
## Geliştirme
6+
7+
```bash
8+
bun install
9+
bun run dev # vite dev — :3000
10+
bun run build # production build → build/
11+
```
12+
13+
`bun run dev` sırasında license/DRM altyapısı **devre dışı**, premium check'ler no-op olur. License doğrulaması sadece `bun run build` ile çalışan prebuild adımından sonra devreye girer.
14+
15+
## Bir temayı premium yapmak
16+
17+
İki şey değişir, başka hiçbir şey yapmana gerek yok:
18+
19+
1. `manifest.json` içinde:
20+
```diff
21+
- "premium": false // ya da bu satır yok
22+
+ "premium": true
23+
```
24+
25+
2. CI ortamında `PANO_LICENSE_SERVER` env değişkeninin set olduğundan emin ol (zaten release.yml'da `branch == 'main' ? 'prod' : 'dev'` olarak ayarlanmış, dokunmana gerek yok).
26+
27+
`bun run build` çalıştırıldığında:
28+
- prebuild script (`scripts/license/generate-license-constants.js`) panomc.com'dan **public key**'i çeker, `src/lib/server/license-constants.generated.js`'e gömer
29+
- vite build sonrası fingerprint plugin'i tüm `build/` dosyalarının kümülatif SHA-256'sını hesaplayıp `build/manifest.json`'a `fileFingerprint` olarak yazar
30+
- Pano host bu temayı kurarken/başlatırken aynı hash'i tekrar hesaplar, eşleşmiyorsa reddeder
31+
- Çalışma zamanında tema kendi dosyalarını da bağımsız olarak doğrular
32+
33+
## License server seçimi (PANO_LICENSE_SERVER)
34+
35+
prebuild script şu sırayla seçim yapar:
36+
37+
| Öncelik | Env değişkeni | Davranış |
38+
|---|---|---|
39+
| 1 | `PANO_LICENSE_PUBLIC_KEY` | Public key'i direkt env'den alır (CI secret olarak), sunucuya hiç bağlanmaz |
40+
| 2 | `PANO_LICENSE_SERVER=prod` | `https://api.panomc.com`'dan key fetch eder |
41+
| 2 | `PANO_LICENSE_SERVER=dev` | `https://api-dev.panomc.com`'dan key fetch eder |
42+
| 2 | `PANO_LICENSE_SERVER=http://localhost:8087` | Verilen URL'den key fetch eder (lokal backend testi için) |
43+
| 3 | (hiçbiri set değil) | Git branch'ten otomatik tespit eder: `main`/`master`/`release-*` → prod, `dev`/`develop`/`feat-*`/`fix-*`/`hotfix-*` → dev |
44+
| 4 | (yine yok) | Premium tema build'i **fail eder** (sessizce free build'e düşmez) |
45+
46+
Bonus: `PANO_LICENSE_ISSUER` — JWT `iss` doğrulaması için override (genelde gerekmez, server'dan otomatik türetilir).
47+
48+
## GitHub Actions
49+
50+
Mevcut `.github/workflows/release.yml` zaten her şeyi hallediyor:
51+
52+
```yaml
53+
- name: Build
54+
env:
55+
PANO_LICENSE_SERVER: ${{ steps.extract_branch.outputs.branch == 'main' && 'prod' || 'dev' }}
56+
run: bun run build
57+
```
58+
59+
- `dev` branch'e push → otomatik dev sunucudan key fetch
60+
- `main` branch'e push (veya release) → prod sunucudan key fetch
61+
62+
Free temalarda bu env zararsız (manifest.premium=false olduğu için prebuild script onu görmezden gelir).
63+
64+
### CI'da gizli key ile build almak istersen (offline / restricted runner)
65+
66+
Public key'i bir kez prod'dan alıp GH repository secret'a koy:
67+
```
68+
Settings → Secrets → Actions → New repository secret
69+
Name: PANO_LICENSE_PUBLIC_KEY
70+
Value: <base64 RSA-2048 key, panomc.com'dan>
71+
```
72+
73+
Sonra workflow:
74+
```yaml
75+
- name: Build
76+
env:
77+
PANO_LICENSE_PUBLIC_KEY: ${{ secrets.PANO_LICENSE_PUBLIC_KEY }}
78+
run: bun run build
79+
```
80+
81+
Bu durumda prebuild hiçbir HTTP çağrısı yapmaz, key'i secret'tan okur.
82+
83+
## Lokal'de premium tema build almak
84+
85+
```bash
86+
# Lokal Pano backend'ini :8087'de çalıştırıyorsan
87+
PANO_LICENSE_SERVER=http://localhost:8087 bun run build
88+
89+
# Veya panomc.com'un dev sunucusunu kullanmak istersen
90+
PANO_LICENSE_SERVER=dev bun run build
91+
92+
# Geçici olarak free build (test için)
93+
PANO_LICENSE_REQUIRED=false bun run build
94+
```
95+
96+
## Yeni tema oluşturmak
97+
98+
```bash
99+
cp -r vanilla-theme shadow-theme
100+
cd shadow-theme
101+
102+
# 1. manifest.json düzenle: id, title, version, screenshots
103+
# 2. Premium olacaksa: "premium": true ekle
104+
# 3. (varsa) .github/workflows/release.yml içinde zip ismini güncelle
105+
106+
bun install
107+
bun run dev
108+
```
109+
110+
`scripts/license/` ve `src/lib/server/license-runtime.js` zaten bütün premium altyapıyı kapsıyor; sadece manifest'i değiştirmek yeterli.

scripts/license/generate-license-constants.js

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,43 @@ function resolveServerBaseUrl(server) {
5555
return server.trim().replace(/\/$/, "");
5656
}
5757

58+
/**
59+
* Hostname of a URL with the leading "api." or "api-<label>." stripped, so a custom
60+
* license server URL maps to the JWT `iss` Pano host would compute via
61+
* com.panomc.platform.config.PanoConfig.resolvedLicenseJwtIssuer:
62+
* https://api.example.com → example.com
63+
* https://api-dev.example.com → dev.example.com
64+
* http://localhost:8087 → localhost
65+
* anything malformed/empty → ""
66+
*/
67+
function hostnameForIssuer(url) {
68+
try {
69+
const u = new URL(url);
70+
const host = u.hostname;
71+
if (!host) return "";
72+
const apiPrefix = host.match(/^api\.(.+)$/);
73+
if (apiPrefix) return apiPrefix[1];
74+
const apiLabel = host.match(/^api-([^.]+)\.(.+)$/);
75+
if (apiLabel) return `${apiLabel[1]}.${apiLabel[2]}`;
76+
return host;
77+
} catch {
78+
return "";
79+
}
80+
}
81+
5882
function issuerHintFor(server, baseUrl) {
5983
const norm = (server || "").trim().toLowerCase();
6084
if (norm === "dev" || norm === "development" || norm === "staging") return "dev.panomc.com";
6185
if (norm === "prod" || norm === "production") return "panomc.com";
62-
// For custom URL, leave empty so the runtime falls back to derived hostname.
6386
if (baseUrl === DEV_URL) return "dev.panomc.com";
6487
if (baseUrl === PROD_URL) return "panomc.com";
88+
// Custom URL: derive the hostname the same way the Pano host would, so a local
89+
// backend at http://localhost:8087 ends up with iss="localhost", matching what the
90+
// host stamps into the JWT.
91+
if (baseUrl) {
92+
const fromUrl = hostnameForIssuer(baseUrl);
93+
if (fromUrl) return fromUrl;
94+
}
6595
return "";
6696
}
6797

@@ -247,13 +277,18 @@ async function main() {
247277
resolvedServer = choice.baseUrl || choice.serverKeyword || "(explicit key)";
248278
}
249279

250-
// Derive expected issuer: explicit env > server keyword/url > none (runtime falls back).
280+
// Derive expected issuer: explicit env > PANO_LICENSE_SERVER (set even when KEY is too)
281+
// > branch detection > "". Build-time issuer hint is just a default; the Pano host
282+
// always sets PANO_LICENSE_ISSUER on the bun process from its own config, so this only
283+
// matters for theme runtimes that boot without the host env (e.g. local debugging).
251284
const explicitIssuer = (process.env.PANO_LICENSE_ISSUER || "").trim();
285+
const explicitServerForIssuer = (process.env.PANO_LICENSE_SERVER || "").trim();
252286
const issuerHint = explicitIssuer || (required
253-
? issuerHintFor(
254-
resolvedSource === "explicit-server" ? process.env.PANO_LICENSE_SERVER : (resolvedServer === "(explicit key)" ? "" : resolvedServer ?? ""),
255-
resolvedServer === "(explicit key)" ? null : (resolvedServer ?? null)
256-
)
287+
? (explicitServerForIssuer
288+
? issuerHintFor(explicitServerForIssuer, resolveServerBaseUrl(explicitServerForIssuer))
289+
: (detectedBranch
290+
? issuerHintFor("", resolvedSource === "branch" ? resolvedServer : null)
291+
: ""))
257292
: "");
258293

259294
fs.mkdirSync(path.dirname(OUTPUT_FILE), { recursive: true });

0 commit comments

Comments
 (0)