SPK Workers - ้กน็ฎ่ง่
1.1 ้กน็ฎๅ็งฐไธๅฎไฝ
้กน็ฎๅ็งฐ : SPK Workers
้กน็ฎๅฎไฝ : ๅฐ SSPKS (Simple SPK Server) ไป PHP ้ๅไธบ Cloudflare Workers ็ๆฌ
ๆ ธๅฟๅ่ฝ : ๅ Synology NAS ่ฎพๅคๆไพ SPK ๅฎ่ฃ
ๅ
ๆๅก็่พน็ผ่ฎก็ฎ่งฃๅณๆนๆก
็ฑปๅซ
ๆๆฏ้ๅ
็ๆฌ่ฆๆฑ
่ฏญ่จ
TypeScript
>= 5.0
่ฟ่กๆถ
Cloudflare Workers
ๆๆฐ
ๅญๅจ
R2 Storage
-
็ผๅญ
Workers KV + D1
-
ๆจกๆฟๅผๆ
Mustache.js
^2.14
YAML่งฃๆ
yaml
^9.0
ZIPๅค็
JSZip
^3.10
ๆต่ฏๆกๆถ
Vitest + @cloudflare/vitest-pool-workers
^4.1
ๆๅปบๅทฅๅ
ท
Wrangler
^3.0
flowchart TB
subgraph ๅฎขๆท็ซฏๅฑ
A["Synology<br/>Package Center"]
B["Web Browser<br/>็จๆทๆต่งๅจ"]
C["Admin<br/>ไธไผ ็จๆท"]
end
subgraph Cloudflare่พน็ผ็ฝ็ป
D["CDN<br/>้ๆ่ตๆบ็ผๅญ"]
E["Workers<br/>่พน็ผ่ฎก็ฎ่็น"]
end
subgraph Handlerๅค็ๅจๅฑ
F["Router<br/>่ฏทๆฑ่ทฏ็ฑ"]
G["SynologyHandler<br/>APIๆฅๅฃ"]
H["BrowserHandler<br/>ๅค้กต้ข่ทฏ็ฑ"]
I["DownloadHandler<br/>ๆไปถไธ่ฝฝ"]
J["UploadHandler<br/>SPKไธไผ API"]
K["UploadPageHandler<br/>ไธไผ ้กต้ข"]
L["NotFoundHandler<br/>404ๅค็"]
end
subgraph Packageๅ
ๅค็ๅฑ
L["Package<br/>SPK่งฃๆ"]
M["PackageFinder<br/>ๅ
ๆฅๆพ"]
N["PackageFilter<br/>ๅ
่ฟๆปค"]
end
subgraph Utilsๅทฅๅ
ทๅฑ
LA["JSZip<br/>SPK่งฃๅ"]
LB["IniParser<br/>INI่งฃๆ"]
LC["UrlFixer<br/>URLไฟฎๅค"]
end
subgraph Cache็ผๅญๅฑ[Workers KV]
O["ๅ
็ดขๅผ็ผๅญ"]
P["ๅ
ๆฐๆฎ็ผๅญ"]
Q["้
็ฝฎ็ผๅญ"]
end
subgraph Storageๅญๅจๅฑ[R2]
R["packages/<br/>SPKๆไปถ"]
S["icons/<br/>ๅพๆ "]
T["INFO/<br/>ๅ
ๆฐๆฎ"]
end
subgraph Dataๆฐๆฎๅฑ
U["Config<br/>้
็ฝฎ็ฎก็"]
V["DeviceList<br/>่ฎพๅคๅ่กจ"]
end
A & B --> D
C --> E
D --> E
E --> F
F --> G & H & I & J & K
G & H & I --> L & M & N
J --> L
J --> LA
LA --> LB
LB --> P
L & M & N --> O & P & Q
L & M & N --> LA & LC
L & M & N --> U & V
O & P & Q --> R & S & T
LA --> R
style J fill:#f96,stroke:#333,stroke-width:3px
style LA fill:#9f9,stroke:#333,stroke-width:2px
style LB fill:#9f9,stroke:#333,stroke-width:2px
style P fill:#9ff,stroke:#333,stroke-width:2px
Loading
flowchart LR
subgraph ไธไผ ๆต็จ
A1["Admin"] --> A2["UploadHandler"]
A2 --> A3{"้ช่ฏ API Key"}
A3 -->|้่ฟ| A4["ๆฅๆถ SPK ๆไปถ"]
A3 -->|ๆ็ป| A5["่ฟๅ 401"]
A4 --> A6["JSZip ่งฃๆ SPK"]
A6 --> A7["ๆๅ INFO ๆไปถ"]
A7 --> A8["IniParser ่งฃๆๅ
ๆฐๆฎ"]
A8 --> A9["ๅๅ
ฅๅญๅจๅ็ซฏ (D1/KV)"]
A9 --> A10["ๅๅ
ฅ R2 Storage"]
A10 --> A11["่ฟๅๆๅ"]
end
subgraph Synology่ฏทๆฑๆต็จ
B1["Synology"] --> B2["SynologyHandler"]
B2 --> B3{"ๆฅ่ฏขๅญๅจๅ็ซฏ"}
B3 -->|ๅฝไธญ| B4["็ดๆฅ่ฟๅ"]
B3 -->|ๆชๅฝไธญ| B5["R2 ๅๅบๅ
"]
B5 --> B6["PackageFinder"]
B6 --> B7["Package ่งฃๆ"]
B7 --> B8["ๅๅ
ฅๅญๅจๅ็ซฏ"]
B8 --> B4
end
style A6 fill:#9f9
style A7 fill:#9f9
style A8 fill:#9f9
style A9 fill:#9ff
Loading
spk-workers/
โโโ src/
โ โโโ index.ts # Worker ๅ
ฅๅฃ
โ โโโ config/
โ โ โโโ Config.ts # ้
็ฝฎ็ฎก็
โ โโโ device/
โ โ โโโ DeviceList.ts # ่ฎพๅคๅ่กจ
โ โโโ package/
โ โ โโโ Package.ts # SPK ๅ
็ฑป
โ โ โโโ PackageFinder.ts # ๅ
ๆฅๆพ
โ โ โโโ PackageFilter.ts # ๅ
่ฟๆปค
โ โ โโโ PackageCacheManager.ts # ๅ
็ผๅญ็ฎก็
โ โ โโโ StorageManager.ts # ๅญๅจ็ฎก็
โ โโโ handlers/
โ โ โโโ AbstractHandler.ts # ๅค็ๅจๅบ็ฑป
โ โ โโโ Router.ts # ่ฏทๆฑ่ทฏ็ฑ
โ โ โโโ SynologyHandler.ts # Synology API
โ โ โโโ BrowserHandler.ts # ๆต่งๅจๅค้กต้ข
โ โ โโโ DownloadHandler.ts # SPK ไธ่ฝฝ
โ โ โโโ UploadHandler.ts # SPK ไธไผ API
โ โ โโโ UploadPageHandler.ts # ไธไผ ้กต้ข
โ โ โโโ DeleteHandler.ts # ๅ ้ค API
โ โ โโโ AssetsHandler.ts # ้ๆ่ตๆบ
โ โ โโโ IconHandler.ts # ๅพๆ API
โ โ โโโ NotFoundHandler.ts # 404 ๅค็
โ โโโ db/
โ โ โโโ IStorage.ts # ๅญๅจๆฅๅฃ
โ โ โโโ StorageFactory.ts # ๅญๅจๅทฅๅ
โ โ โโโ D1Storage.ts # D1 ๅญๅจๅฎ็ฐ
โ โ โโโ KVStorage.ts # KV ๅญๅจๅฎ็ฐ
โ โ โโโ HybridStorage.ts # ๆททๅๅญๅจๅฎ็ฐ
โ โโโ utils/
โ โ โโโ CacheKeyBuilder.ts # ็ผๅญ้ฎๆๅปบๅจ
โ โ โโโ CacheMonitor.ts # ็ผๅญ็ๆง
โ โ โโโ CacheWarmer.ts # ็ผๅญ้ข็ญ
โ โ โโโ CacheFallback.ts # ็ผๅญ้็บง
โ โ โโโ QuotaManager.ts # ้
้ข็ฎก็
โ โ โโโ ImageOptimization.ts # ๅพ็ไผๅ
โ โ โโโ Compression.ts # ๅ็ผฉๅทฅๅ
ท
โ โ โโโ TemplateCache.ts # ๆจกๆฟ็ผๅญ
โ โโโ output/
โ โโโ HtmlOutput.ts # HTML ๆธฒๆ
โ โโโ JsonOutput.ts # JSON ่พๅบ
โ โโโ Templates.ts # ็ผ่ฏๅๆจกๆฟ
โ โโโ UrlFixer.ts # URL ไฟฎๅค
โโโ templates/ # Mustache ๆจกๆฟ
โ โโโ partials/
โ โ โโโ html_head.mustache
โ โ โโโ html_tail.mustache
โ โโโ html_modellist.mustache # ่ฎพๅคๅ่กจ
โ โโโ html_modellist_error.mustache
โ โโโ html_modellist_none.mustache
โ โโโ html_packagelist.mustache # ๅ
ๅ่กจ
โ โโโ html_packagelist_all.mustache
โ โโโ html_package_detail.mustache # ๅ
่ฏฆๆ
โ โโโ html_upload.mustache # ไธไผ ้กต้ข
โโโ themes/ # ไธป้ข้ๆ่ตๆบ
โ โโโ material/
โ โ โโโ css/
โ โ โโโ js/
โ โ โโโ fonts/
โ โ โโโ images/
โ โโโ classic/
โ โโโ css/
โ โโโ js/
โ โโโ fonts/
โ โโโ images/
โโโ tests/ # ๆต่ฏๆไปถ
โ โโโ unit/
โ โ โโโ Config.test.ts
โ โ โโโ DeviceList.test.ts
โ โ โโโ Package.test.ts
โ โ โโโ PackageFilter.test.ts
โ โโโ integration/
โโโ conf/ # ้
็ฝฎๆไปถ
โ โโโ sspks.yaml
โ โโโ synology_models.yaml
โโโ scripts/ # ้จ็ฝฒ่ๆฌ
โโโ package.json
โโโ tsconfig.json
โโโ wrangler.toml
โโโ vitest.config.ts
โโโ .eslintrc.json
็ซฏ็น
ๆนๆณ
่ฏดๆ
ๅๅบๆ ผๅผ
/
GET
ไธป้กต/่ฎพๅคๅ่กจ
HTML
/?arch={arch}
GET
ๆๅฎๆถๆ็ๅ
ๅ่กจ
HTML
/package/{name}
GET
ๅ
่ฏฆๆ
้กต
HTML
/upload
GET
ไธไผ ้กต้ข
HTML
/ (Synology ๅฎขๆท็ซฏ)
GET
Package Center API
JSON
/api/upload
POST
ไธไผ SPK ๅ
JSON
/api/delete/{name}
DELETE
ๅ ้คๅ
JSON
/api/icon
GET
่ทๅๅ
ๅพๆ
PNG
/packages/{name}.spk
GET
SPK ๆไปถไธ่ฝฝ
binary
/_assets/**
GET
ๅ
ฌๅ
ฑ้ๆ่ตๆบ
static
/_themes/{theme}/**
GET
ไธป้ข้ๆ่ตๆบ
static
/themes/{theme}/**
GET
ไธป้ข่ตๆบ(ๅซๅ)
static
ๅๆฐๅ
็ฑปๅ
่ฏดๆ
็คบไพ
unique
string
่ฎพๅคๅฏไธๆ ่ฏ
synology_avoton_415+
arch
string
CPU ๆถๆ
avoton
major
number
ไธป็ๆฌๅท
6
minor
number
ๆฌก็ๆฌๅท
2
build
number
ๆๅปบๅท
6455
package_update_channel
string
ๆดๆฐ้้
stable/beta
language
string
่ฏญ่จไปฃ็
enu
{
"packages" : [
{
"package" : " PackageName" ,
"version" : " 1.0.0" ,
"dname" : " Display Name" ,
"desc" : " Description" ,
"link" : " https://..." ,
"size" : 1234567 ,
"md5" : " abc123..." ,
"thumbnail" : [" url1" , " url2" ],
"qinst" : true ,
"qupgrade" : true ,
"qstart" : true ,
"beta" : false
}
],
"keyrings" : [" -----BEGIN PGP PUBLIC KEY BLOCK-----..." ]
}
POST /api/upload
Content-Type: multipart/form-data
X-API-Key: <your-api-key>
ๅๆฐ
็ฑปๅ
่ฏดๆ
spk
File
SPK ๆไปถ (ๅฟ
ๅกซ)
overwrite
boolean
ๆฏๅฆ่ฆ็ๅทฒๅญๅจ็ๅ
(้ป่ฎค: false)
{
"success" : true ,
"filename" : " PackageName-x86-avoton.spk" ,
"package" : " PackageName" ,
"version" : " 1.0.0" ,
"arch" : [" avoton" , " noarch" ],
"message" : " Package uploaded and indexed"
}
{
"error" : {
"code" : " UNAUTHORIZED" ,
"message" : " Invalid or missing API key"
}
}
้่ฏฏ็
HTTP ็ถๆ
่ฏดๆ
UNAUTHORIZED
401
API Key ๆ ๆๆ็ผบๅคฑ
INVALID_FILE
400
ๆไปถๆ ๆๆ้ SPK ๆ ผๅผ
FILE_TOO_LARGE
400
ๆไปถ่ถ
่ฟ 500MB ้ๅถ
PACKAGE_EXISTS
409
ๅ
ๅทฒๅญๅจไธ overwrite=false
PARSE_ERROR
500
SPK ๅ
ๆฐๆฎ่งฃๆๅคฑ่ดฅ
STORAGE_ERROR
500
R2 ๅญๅจ้่ฏฏ
DELETE /api/delete/{package_name}
X-API-Key: <your-api-key>
{
"success" : true ,
"package" : " PackageName" ,
"message" : " Package deleted"
}
้่ฏฏ็
HTTP ็ถๆ
่ฏดๆ
UNAUTHORIZED
401
API Key ๆ ๆๆ็ผบๅคฑ
PACKAGE_NOT_FOUND
404
ๅ
ไธๅญๅจ
DELETE_ERROR
500
ๅ ้คๅคฑ่ดฅ
GET /api/icon?package={name}&size={size}
ๅๆฐ
็ฑปๅ
่ฏดๆ
้ป่ฎคๅผ
package
string
ๅ
ๅ
-
size
number
ๅพๆ ๅฐบๅฏธ
72
่ฟๅ PNG ๆ ผๅผ็ๅพๆ ๅพ็๏ผๆ้ๅฎๅๅฐไธป้ขๅพๆ ใ
| INVALID_FILE | 400 | ๆไปถๆ ๆๆ้ SPK ๆ ผๅผ |
| FILE_TOO_LARGE | 400 | ๆไปถ่ถ
่ฟ 500MB ้ๅถ |
| PACKAGE_EXISTS | 409 | ๅ
ๅทฒๅญๅจไธ overwrite=false |
| PARSE_ERROR | 500 | SPK ๅ
ๆฐๆฎ่งฃๆๅคฑ่ดฅ |
| STORAGE_ERROR | 500 | R2 ๅญๅจ้่ฏฏ |
site :
name : " Simple SPK Server"
theme : " material" # material | classic
redirectindex : " " # ๅฏ้้ๅฎๅURL
packages :
file_mask : " *.spk"
maintainer : " "
maintainer_url : " "
distributor : " "
distributor_url : " "
support_url : " "
paths :
cache : " cache/"
models : " conf/synology_models.yaml"
packages : " packages/"
themes : " themes/"
excludedSynoServices :
- apache-sys
- apache-web
- mdns
- samba
- db
- applenetwork
- cron
- nfs
- firewall
ๅ้ๅ
่ฏดๆ
้ป่ฎคๅผ
SSPKS_SITE_NAME
็ฝ็ซๅ็งฐ
-
SSPKS_SITE_THEME
ไธป้ข
material
SSPKS_PACKAGES_FILE_MASK
ๆไปถๆฉ็
*.spk
SSPKS_PACKAGES_MAINTAINER
็ปดๆค่
-
SSPKS_R2_BUCKET
R2 ๅญๅจๆกถๅ
spks
SSPKS_KV_NAMESPACE
KV ๅฝๅ็ฉบ้ด
spks-cache
SSPKS_API_KEY
ไธไผ API ๅฏ้ฅ
-
SSPKS_STORAGE_BACKEND
ๅญๅจๅ็ซฏ
hybrid
SSPKS_DB
D1 ๆฐๆฎๅบๅ็งฐ
-
SSPKS_D1_API_TOKEN
D1 API Token
-
SSPKS_CACHE_API_TOKEN
KV API Token
-
ๅ็ซฏ
่ฏดๆ
้็จๅบๆฏ
d1
D1 SQLite ๆฐๆฎๅบ
ๆไน
ๅๅญๅจ๏ผๅคๆๆฅ่ฏข
kv
Workers KV ็ผๅญ
้ซ้็ผๅญ๏ผ็ฎๅ็้ฎๅผ
hybrid (ๆจ่)
D1 + KV ๆททๅ
D1 ๆไน
ๅ + KV ่ฏป็ผๅญ
D1 Database: spks
packages ่กจ:
โโโ id (TEXT PRIMARY KEY) -- ๅ
ๅ
โโโ r2_key (TEXT) -- R2 ่ทฏๅพ
โโโ version (TEXT) -- ็ๆฌ
โโโ displayname (TEXT) -- ๆพ็คบๅ็งฐ
โโโ description (TEXT) -- ๆ่ฟฐ
โโโ maintainer (TEXT) -- ็ปดๆค่
โโโ maintainer_url (TEXT) -- ็ปดๆค่
้พๆฅ
โโโ arch (TEXT) -- ๆถๆ JSON
โโโ firmware (TEXT) -- ๅบไปถ่ฆๆฑ
โโโ beta (INTEGER) -- ๆต่ฏ็
โโโ thumbnail_url (TEXT) -- ็ผฉ็ฅๅพ
โโโ size (INTEGER) -- ๆไปถๅคงๅฐ
โโโ created_at (INTEGER) -- ๅๅปบๆถ้ด
โโโ updated_at (INTEGER) -- ๆดๆฐๆถ้ด
package_arch ่กจ:
โโโ id (INTEGER PRIMARY KEY) -- ่ชๅข ID
โโโ package_id (TEXT) -- ๅ
ๅ (ๅค้ฎ)
โโโ arch (TEXT) -- ๆถๆ
โโโ UNIQUE(package_id, arch)
r2://spks-bucket/
โโโ packages/
โ โโโ Package1-x86-avoton.spk
โ โโโ Package1-noarch.spk
โ โโโ Package2-x86-cedarview.spk
โโโ icons/
โ โโโ Package1.thumb.72.png
โ โโโ Package1.thumb.120.png
โโโ INFO/
โโโ Package1.nfo
โโโ Package2.nfo
KV Namespace: spks-cache
โโโ packages:index # ๅ
็ดขๅผ JSON
โโโ packages:arch:{arch} # ๆถๆ็ดขๅผ JSON
โโโ config:yaml # ้
็ฝฎ็ผๅญ
โโโ device:config # ่ฎพๅค้
็ฝฎ็ผๅญ
โโโ icon:{package}:{size} # ๅพๆ ็ผๅญ
โโโ template:{name} # HTML ๆจกๆฟ็ผๅญ
่ตๆบ็ฑปๅ
TTL
็ญ็ฅ
ๅ
็ดขๅผ
10 ๅ้
LRU ๆทๆฑฐ
ๆถๆ็ดขๅผ
10 ๅ้
LRU ๆทๆฑฐ
่ฎพๅค้
็ฝฎ
1 ๅฐๆถ
ๅฎๆถๅทๆฐ
ๅพๆ
24 ๅฐๆถ
ๆฐธไน
็ผๅญ
ๆจกๆฟ
1 ๅฐๆถ
ๅฎๆถๅทๆฐ
็ผๅญ็ณป็ปๅ
ๅซไปฅไธ็ๆง่ฝๅ๏ผ
CacheMonitor : ๅฎๆถ็ๆง็ผๅญๅฝไธญ็
CacheWarmer : ๅฎๆถ้ข็ญ็ผๅญ
CacheFallback : ้็บง็ญ็ฅๅค็
QuotaManager : D1/KV ้
้ข็ฎก็
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅญๅจๅฑๆถๆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ R2 Bucket โ โ D1 (SQL) โ โ KV (Key-Val) โ โ
โ โ SPKS_BUCKET โ โ SPKS_DB โ โ SPKS_CACHE โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ โ โ โ
โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โ StorageManager โ โ
โ โ (็ปไธๆฅๅฃๅฑ) โ โ
โ โโโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โ Handlers โ โ
โ โโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
D1 (ๆจ่) ๏ผSQLite ๆฐๆฎๅบ๏ผ5M ่ก่ฏปๅ/ๅคฉ้
้ข๏ผๆฏๆๅคๆๆฅ่ฏข
KV (ๅ้) ๏ผKey-Value ๅญๅจ๏ผ100K ๆฌก่ฏปๅ/ๅคฉ๏ผ็ฎๅๅฟซ้
// src/config/Config.ts
interface SiteConfig {
name : string ;
theme : 'material' | 'classic' ;
redirectindex ?: string ;
}
interface PackagesConfig {
file_mask : string ;
maintainer : string ;
maintainer_url : string ;
distributor : string ;
distributor_url : string ;
support_url : string ;
}
interface PathsConfig {
cache : string ;
models : string ;
packages : string ;
themes : string ;
}
class Config {
site : SiteConfig ;
packages : PackagesConfig ;
paths : PathsConfig ;
excludedSynoServices : string [ ] ;
constructor ( env : Bindings ) ;
get ( key : string ) : any ;
}
// src/package/Package.ts
interface PackageMetadata {
package : string ;
version : string ;
displayname : string ;
description : string ;
maintainer : string ;
maintainer_url ?: string ;
distributor ?: string ;
distributor_url ?: string ;
support_url ?: string ;
arch : string [ ] ;
thumbnail : string [ ] ;
thumbnail_url : string [ ] ;
snapshot : string [ ] ;
snapshot_url : string [ ] ;
beta : boolean ;
firmware : string ;
install_dep_services ?: string ;
silent_install : boolean ;
silent_uninstall : boolean ;
silent_upgrade : boolean ;
qinst : boolean ;
qupgrade : boolean ;
qstart : boolean ;
spk : string ;
spk_url : string ;
}
class Package {
metadata : PackageMetadata ;
constructor ( r2 : R2Bucket , filename : string ) ;
static async fromR2 ( r2 : R2Bucket , key : string ) : Promise < Package > ;
getMetadata ( ) : PackageMetadata ;
isCompatibleToArch ( arch : string ) : boolean ;
isCompatibleToFirmware ( version : string ) : boolean ;
isBeta ( ) : boolean ;
}
// src/package/PackageFilter.ts
class PackageFilter {
constructor ( packages : Package [ ] ) ;
setArchitectureFilter ( arch : string ) : void ;
setFirmwareVersionFilter ( version : string | false ) : void ;
setChannelFilter ( channel : 'stable' | 'beta' | false ) : void ;
setOldVersionFilter ( status : boolean ) : void ;
isMatchingArchitecture ( pkg : Package ) : boolean ;
isMatchingFirmwareVersion ( pkg : Package ) : boolean ;
isMatchingChannel ( pkg : Package ) : boolean ;
getFilteredPackageList ( ) : Package [ ] ;
}
// src/handlers/Handler.ts
interface Handler {
handle ( request : Request , env : Bindings , ctx : ExecutionContext ) : Promise < Response > ;
canHandle ( request : Request ) : boolean ;
}
็ถๆ็
่ฏดๆ
200
ๆๅ
400
่ฏทๆฑๅๆฐ้่ฏฏ
404
่ตๆบไธๅญๅจ
500
ๆๅกๅจๅ
้จ้่ฏฏ
502
ไธๆธธๆๅก้่ฏฏ
503
ๆๅกไธๅฏ็จ
{
"error" : {
"code" : " PACKAGE_NOT_FOUND" ,
"message" : " Package 'xxx' not found"
}
}
็ซฏ็น
็ฎๆ ๅๅบๆถ้ด
/ (Synology API)
< 50ms (็ผๅญๅฝไธญ)
/
< 100ms
SPK ไธ่ฝฝ
< 200ms
่ตๆบ็ฑปๅ
็ผๅญๆถ้ด
API ๅๅบ
5 ๅ้
ๅ
็ดขๅผ
10 ๅ้
SPK ๆไปถ
1 ๅฐๆถ
้ๆ่ตๆบ
1 ๅคฉ
ๅ
่ฎธ Synology ๅๅ็่ทจๅ่ฏทๆฑ
้ๆ่ตๆบๅ
่ฎธๅ
ฌๅ
ฑ่ฎฟ้ฎ
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
ๆจกๅ
่ฆ็็็ฎๆ
Config
90%
DeviceList
90%
Package
85%
PackageFilter
90%
Storage (D1/KV/Hybrid)
85%
Handlers
80%
ๅๅ
ๆต่ฏ : ๆจกๅ็ฌ็ซๅ่ฝๆต่ฏ
้ๆๆต่ฏ : ๆจกๅ้ดๅไฝๆต่ฏ
็ซฏๅฐ็ซฏๆต่ฏ : ๅฎๆด่ฏทๆฑๆต็จๆต่ฏ
็ฏๅข
่ฏดๆ
development
ๆฌๅฐ Wrangler
staging
Cloudflare ้ขๅๅธ
production
Cloudflare ๆญฃๅผ
ๆฌๅฐๆต่ฏ้่ฟ (npm test)
ไปฃ็ ๅฎกๆฅ (Pull Request)
่ชๅจ้จ็ฝฒๅฐ staging
ๆๅจ็กฎ่ฎคๅๅธๅฐ production
็ๆฌ
ๆฅๆ
ๅๆด่ฏดๆ
1.0.0
TBD
ๅๅง็ๆฌ