Skip to content

Latest commit

ย 

History

History
798 lines (657 loc) ยท 21.7 KB

File metadata and controls

798 lines (657 loc) ยท 21.7 KB

SPK Workers - ้กน็›ฎ่ง„่Œƒ

1. ้กน็›ฎๆฆ‚่ฟฐ

1.1 ้กน็›ฎๅ็งฐไธŽๅฎšไฝ

  • ้กน็›ฎๅ็งฐ: SPK Workers
  • ้กน็›ฎๅฎšไฝ: ๅฐ† SSPKS (Simple SPK Server) ไปŽ PHP ้‡ๅ†™ไธบ Cloudflare Workers ็‰ˆๆœฌ
  • ๆ ธๅฟƒๅŠŸ่ƒฝ: ๅ‘ Synology NAS ่ฎพๅค‡ๆไพ› SPK ๅฎ‰่ฃ…ๅŒ…ๆœๅŠก็š„่พน็ผ˜่ฎก็ฎ—่งฃๅ†ณๆ–นๆกˆ

1.2 ๆŠ€ๆœฏๆ ˆ

็ฑปๅˆซ ๆŠ€ๆœฏ้€‰ๅž‹ ็‰ˆๆœฌ่ฆๆฑ‚
่ฏญ่จ€ 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

1.3 ๅŠŸ่ƒฝ่Œƒๅ›ด

ๆ ธๅฟƒๅŠŸ่ƒฝ

  • Synology Package Center API ๅ…ผๅฎนๆŽฅๅฃ
  • SPK ๅŒ…ๅ…ƒๆ•ฐๆฎ่งฃๆžไธŽ็ฎก็†
  • ๆŒ‰ๆžถๆž„/ๅ›บไปถ็‰ˆๆœฌ/้€š้“่ฟ‡ๆปคๅŒ…
  • ๆต่งˆๅ™จ็ซฏๅŒ…ๆต่งˆ็•Œ้ข
  • SPK ๅŒ…ๆ–‡ไปถไธ‹่ฝฝๆœๅŠก

่พน็ผ˜่ฎก็ฎ—็‰นๆ€ง

  • ๅ…จ็ƒ่พน็ผ˜็ฝ‘็ปœๅˆ†ๅ‘
  • R2 Storage ๅฏน่ฑกๅญ˜ๅ‚จ
  • D1 ๆ•ฐๆฎๅบ“ๅ…ƒๆ•ฐๆฎๅญ˜ๅ‚จ
  • Workers KV ็ดขๅผ•็ผ“ๅญ˜๏ผˆๅ›ž้€€๏ผ‰
  • ่‡ชๅŠจ HTTPS ้…็ฝฎ

2. ็ณป็ปŸๆžถๆž„

2.1 ๆ•ดไฝ“ๆžถๆž„

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

2.2 ๆ•ฐๆฎๆตๆžถๆž„

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

2.2 ็›ฎๅฝ•็ป“ๆž„

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

3. ๅŠŸ่ƒฝ่ง„่Œƒ

3.1 API ็ซฏ็‚น

็ซฏ็‚น ๆ–นๆณ• ่ฏดๆ˜Ž ๅ“ๅบ”ๆ ผๅผ
/ 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

3.2 Synology API ่ง„่Œƒ

่ฏทๆฑ‚ๅ‚ๆ•ฐ

ๅ‚ๆ•ฐๅ ็ฑปๅž‹ ่ฏดๆ˜Ž ็คบไพ‹
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-----..."]
}

3.3 ไธŠไผ  API ่ง„่Œƒ

่ฏทๆฑ‚

POST /api/upload
Content-Type: multipart/form-data
X-API-Key: <your-api-key>
ๅ‚ๆ•ฐ ็ฑปๅž‹ ่ฏดๆ˜Ž
spk File SPK ๆ–‡ไปถ (ๅฟ…ๅกซ)
overwrite boolean ๆ˜ฏๅฆ่ฆ†็›–ๅทฒๅญ˜ๅœจ็š„ๅŒ… (้ป˜่ฎค: false)

ๅ“ๅบ”ๆˆๅŠŸ (200)

{
  "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 ๅญ˜ๅ‚จ้”™่ฏฏ

3.4 ๅˆ ้™ค API ่ง„่Œƒ

่ฏทๆฑ‚

DELETE /api/delete/{package_name}
X-API-Key: <your-api-key>

ๅ“ๅบ”ๆˆๅŠŸ (200)

{
  "success": true,
  "package": "PackageName",
  "message": "Package deleted"
}

ๅ“ๅบ”ๅคฑ่ดฅ

้”™่ฏฏ็  HTTP ็Šถๆ€ ่ฏดๆ˜Ž
UNAUTHORIZED 401 API Key ๆ— ๆ•ˆๆˆ–็ผบๅคฑ
PACKAGE_NOT_FOUND 404 ๅŒ…ไธๅญ˜ๅœจ
DELETE_ERROR 500 ๅˆ ้™คๅคฑ่ดฅ

3.5 ๅ›พๆ ‡ API ่ง„่Œƒ

่ฏทๆฑ‚

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 ๅญ˜ๅ‚จ้”™่ฏฏ |

3.4 ้…็ฝฎ่ง„่Œƒ

sspks.yaml

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 -

3.4 ๅญ˜ๅ‚จๅŽ็ซฏ้…็ฝฎ

ๅŽ็ซฏ็ฑปๅž‹

ๅŽ็ซฏ ่ฏดๆ˜Ž ้€‚็”จๅœบๆ™ฏ
d1 D1 SQLite ๆ•ฐๆฎๅบ“ ๆŒไน…ๅŒ–ๅญ˜ๅ‚จ๏ผŒๅคๆ‚ๆŸฅ่ฏข
kv Workers KV ็ผ“ๅญ˜ ้ซ˜้€Ÿ็ผ“ๅญ˜๏ผŒ็ฎ€ๅ•็š„้”ฎๅ€ผ
hybrid (ๆŽจ่) D1 + KV ๆททๅˆ D1 ๆŒไน…ๅŒ– + KV ่ฏป็ผ“ๅญ˜

D1 ๆ•ฐๆฎๅบ“็ป“ๆž„

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)

3.5 R2 ๅญ˜ๅ‚จ็ป“ๆž„

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

3.6 Workers KV ็ป“ๆž„

KV Namespace: spks-cache
โ”œโ”€โ”€ packages:index          # ๅŒ…็ดขๅผ• JSON
โ”œโ”€โ”€ packages:arch:{arch}   # ๆžถๆž„็ดขๅผ• JSON
โ”œโ”€โ”€ config:yaml           # ้…็ฝฎ็ผ“ๅญ˜
โ”œโ”€โ”€ device:config         # ่ฎพๅค‡้…็ฝฎ็ผ“ๅญ˜
โ”œโ”€โ”€ icon:{package}:{size}  # ๅ›พๆ ‡็ผ“ๅญ˜
โ””โ”€โ”€ template:{name}       # HTML ๆจกๆฟ็ผ“ๅญ˜

3.7 ็ผ“ๅญ˜็ญ–็•ฅ

่ต„ๆบ็ฑปๅž‹ TTL ็ญ–็•ฅ
ๅŒ…็ดขๅผ• 10 ๅˆ†้’Ÿ LRU ๆท˜ๆฑฐ
ๆžถๆž„็ดขๅผ• 10 ๅˆ†้’Ÿ LRU ๆท˜ๆฑฐ
่ฎพๅค‡้…็ฝฎ 1 ๅฐๆ—ถ ๅฎšๆ—ถๅˆทๆ–ฐ
ๅ›พๆ ‡ 24 ๅฐๆ—ถ ๆฐธไน…็ผ“ๅญ˜
ๆจกๆฟ 1 ๅฐๆ—ถ ๅฎšๆ—ถๅˆทๆ–ฐ

3.8 ็ผ“ๅญ˜็›‘ๆŽง

็ผ“ๅญ˜็ณป็ปŸๅŒ…ๅซไปฅไธ‹็›‘ๆŽง่ƒฝๅŠ›๏ผš

  • 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 ๆฌก่ฏปๅ–/ๅคฉ๏ผŒ็ฎ€ๅ•ๅฟซ้€Ÿ

4. ๆจกๅ—่ง„่Œƒ

4.1 Config ๆจกๅ—

// 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;
}

4.2 Package ๆจกๅ—

// 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;
}

4.3 PackageFilter ๆจกๅ—

// 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[];
}

4.4 Handler ๆŽฅๅฃ

// src/handlers/Handler.ts
interface Handler {
  handle(request: Request, env: Bindings, ctx: ExecutionContext): Promise<Response>;
  canHandle(request: Request): boolean;
}

5. ้”™่ฏฏๅค„็†

5.1 HTTP ็Šถๆ€็ 

็Šถๆ€็  ่ฏดๆ˜Ž
200 ๆˆๅŠŸ
400 ่ฏทๆฑ‚ๅ‚ๆ•ฐ้”™่ฏฏ
404 ่ต„ๆบไธๅญ˜ๅœจ
500 ๆœๅŠกๅ™จๅ†…้ƒจ้”™่ฏฏ
502 ไธŠๆธธๆœๅŠก้”™่ฏฏ
503 ๆœๅŠกไธๅฏ็”จ

5.2 ้”™่ฏฏๅ“ๅบ”ๆ ผๅผ

{
  "error": {
    "code": "PACKAGE_NOT_FOUND",
    "message": "Package 'xxx' not found"
  }
}

6. ๆ€ง่ƒฝ่ฆๆฑ‚

6.1 ๅ“ๅบ”ๆ—ถ้—ด

็ซฏ็‚น ็›ฎๆ ‡ๅ“ๅบ”ๆ—ถ้—ด
/ (Synology API) < 50ms (็ผ“ๅญ˜ๅ‘ฝไธญ)
/ < 100ms
SPK ไธ‹่ฝฝ < 200ms

6.2 ็ผ“ๅญ˜็ญ–็•ฅ

่ต„ๆบ็ฑปๅž‹ ็ผ“ๅญ˜ๆ—ถ้—ด
API ๅ“ๅบ” 5 ๅˆ†้’Ÿ
ๅŒ…็ดขๅผ• 10 ๅˆ†้’Ÿ
SPK ๆ–‡ไปถ 1 ๅฐๆ—ถ
้™ๆ€่ต„ๆบ 1 ๅคฉ

7. ๅฎ‰ๅ…จ่ง„่Œƒ

7.1 CORS ้…็ฝฎ

  • ๅ…่ฎธ Synology ๅŸŸๅ็š„่ทจๅŸŸ่ฏทๆฑ‚
  • ้™ๆ€่ต„ๆบๅ…่ฎธๅ…ฌๅ…ฑ่ฎฟ้—ฎ

7.2 CSP ็ญ–็•ฅ

default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';

8. ๆต‹่ฏ•่ง„่Œƒ

8.1 ๆต‹่ฏ•่ฆ†็›–็›ฎๆ ‡

ๆจกๅ— ่ฆ†็›–็އ็›ฎๆ ‡
Config 90%
DeviceList 90%
Package 85%
PackageFilter 90%
Storage (D1/KV/Hybrid) 85%
Handlers 80%

8.2 ๆต‹่ฏ•็ฑปๅž‹

  • ๅ•ๅ…ƒๆต‹่ฏ•: ๆจกๅ—็‹ฌ็ซ‹ๅŠŸ่ƒฝๆต‹่ฏ•
  • ้›†ๆˆๆต‹่ฏ•: ๆจกๅ—้—ดๅไฝœๆต‹่ฏ•
  • ็ซฏๅˆฐ็ซฏๆต‹่ฏ•: ๅฎŒๆ•ด่ฏทๆฑ‚ๆต็จ‹ๆต‹่ฏ•

9. ้ƒจ็ฝฒ่ง„่Œƒ

9.1 ็Žฏๅขƒ

็Žฏๅขƒ ่ฏดๆ˜Ž
development ๆœฌๅœฐ Wrangler
staging Cloudflare ้ข„ๅ‘ๅธƒ
production Cloudflare ๆญฃๅผ

9.2 ้ƒจ็ฝฒๆต็จ‹

  1. ๆœฌๅœฐๆต‹่ฏ•้€š่ฟ‡ (npm test)
  2. ไปฃ็ ๅฎกๆŸฅ (Pull Request)
  3. ่‡ชๅŠจ้ƒจ็ฝฒๅˆฐ staging
  4. ๆ‰‹ๅŠจ็กฎ่ฎคๅ‘ๅธƒๅˆฐ production

10. ๅ˜ๆ›ด่ฎฐๅฝ•

็‰ˆๆœฌ ๆ—ฅๆœŸ ๅ˜ๆ›ด่ฏดๆ˜Ž
1.0.0 TBD ๅˆๅง‹็‰ˆๆœฌ