diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 11bb84e..c67a636 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -25,11 +25,12 @@ jobs: - name: Enable Corepack run: | corepack enable - corepack prepare yarn@4.12.0 --activate + corepack prepare yarn@4.18.0 --activate yarn --version - - uses: calcit-lang/setup-calcit@704b3f5cd6f9ce3bd1e0c926dbdd1d181adb8180 # v1.4.0 + - uses: calcit-lang/setup-calcit@ca701be5a471759e442aa053cd100720bbe1f09f # v1.5.0 with: + deps-file: deps.cirru tools: calcit,caps - name: Resolve immutable dependencies @@ -45,8 +46,9 @@ jobs: calcit calcit.cirru edit format git diff --exit-code -- calcit.cirru calcit calcit.cirru --check-only - calcit calcit.cirru analyze dynamic-methods - calcit calcit.cirru analyze deprecated --summary-only + calcit calcit.cirru analyze weak-types --only dynamic-method --summary-only --format json | jq -e '.data.summary.findings == 0' + calcit calcit.cirru --check-only --ns app.main --ns app.http + calcit calcit.cirru analyze weak-types --only deprecated-call --summary-only calcit calcit.cirru analyze quality --baseline config/calcit-quality.cirru - name: Build server bundle @@ -55,7 +57,7 @@ jobs: calcit calcit.cirru js node --check js-out/app.main.mjs mkdir -p dist-server - cp -r package.json main.mjs js-out yarn.lock dist-server/ + cp -r package.json main.mjs js-out yarn.lock .yarnrc.yml dist-server/ - name: Upload server script if: github.event_name == 'push' && github.ref == 'refs/heads/main' @@ -67,7 +69,7 @@ jobs: flags: '-avzr --progress' options: '' ssh_options: '' - src: 'dist-server/*' + src: 'dist-server/' dest: 'rsync-user@tiye.me:/servers/cos-up/' - name: Display deployment status diff --git a/.gitignore b/.gitignore index c75c14c..1ef2ac2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .compact-inc.cirru .calcit-error.cirru +.calcit/ js-out/ node_modules @@ -9,6 +10,7 @@ example/ secrets.cirru dist +dist-server/ dl-out .pnp.cjs .pnp.loader.mjs diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..2453ab1 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,4 @@ +nodeLinker: node-modules + +npmPreapprovedPackages: + - "@calcit/procs@0.29.3" diff --git a/README.md b/README.md index c7db0f1..90f554f 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,48 @@ COS upload > dirty script providing a server. +### 使用者影响与开发 + +使用正式 Calcit/procs 0.29.3、Skir 0.0.29、Node 24 和 Yarn 4.18.0。 +应用版本保持 0.0.9,不额外发模块 release。移除未被源码使用的 Lilac 依赖和 entry, +不混用旧 alpha、main/hash 依赖。应用继续接收 Skir 的真实 Request。 + +配置和 `/token` 请求体要求 Tag → String 的 Cirru EDN Map,包括额外字段; +非字符串字段、非 Map 或无效 Cirru 请求返回 400,坏配置仍由服务端报错。 +已有缺失字段的空字符串默认值、认证比较、403/404/OPTIONS、PUT 签名、 +区域和 `cos-up/` key 前缀保留;这不是认证策略重设计。 +请求体复用共享 UTF-8 接口和 Result decoder;错误 SDK URL 形状返回 500, +不让新的类型检查异常逃出异步回调。日志仅保留方法/路径,不再打印请求对象、 +密码请求体或含临时凭据的签名 URL。 + +```bash +caps --strict --ci +yarn install --immutable +caps verify --toolchain +calcit calcit.cirru --check-only +calcit calcit.cirru analyze weak-types --only dynamic-method --summary-only --format json | jq -e '.data.summary.findings == 0' +calcit calcit.cirru --check-only --ns app.main --ns app.http +calcit calcit.cirru analyze weak-types --only deprecated-call --summary-only +calcit calcit.cirru analyze quality --baseline config/calcit-quality.cirru +``` + +Skir 的旧 after-start 类型阻塞已通过正式 0.0.29 解决,使用当前配套正式依赖图。 +原质量预算保持不变;仍有两个 partial 定义和一个 SDK 构造器宿主强转, +不宣称零类型债务。无新增测试/验证脚本或编译器改写规则,生成的 JS 不提交。 + +这是后端签名服务,不添加前端 COS/CDN 上传。原有服务器部署路径 +`/servers/cos-up/` 保持不变。原 Actions 只 rsync 文件,不会远程安装依赖; +部署前须确认该目录使用 Node 24,并安装本次 lock 中的配套 runtime +(`corepack yarn install --immutable`)。构建成功不代表生产进程依赖已更新。 + +本地以明确假配置实际验证路由、拒绝认证、非法请求 Result、仅本地 URL 签名和 +异常 SDK URL,禁止 SDK 出站请求、未上传文件、未打印签名值;临时配置/服务器 +已清理。未读取生产凭据,也未验收生产进程或全量旧版行为等价。 + Format for `secrets.cirru`: ```cirru -{} - :bucket |TODO - :app-id |TODO - :secret |TODO - :user |TODO - :pass-md5 |TODO +{} (:bucket |TODO) (:app-id |TODO) (:secret |TODO) (:user |TODO) (:pass-md5 |TODO) ``` Dev request: diff --git a/calcit.cirru b/calcit.cirru index 348b036..2322c6a 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -3,38 +3,31 @@ :about "|Machine-generated snapshot. Do not edit directly — changes will be overwritten. Use `calcit query` to inspect and `calcit edit`/`calcit tree` to modify. Run `calcit docs agents --contract` before mutations; use `--full` for first orientation or changed contract digest. Manual edits must follow format and schema conventions, then run `calcit edit format`." :package |app :entries $ {} $ :default - {} (:description |) (:init-fn 'app.main/main!) (:mode :js) (:reload-fn 'app.main/reload!) + {} (:description |) (:init-fn 'app.main/main!) (:mode :js) (:reload-fn 'app.main/reload!) (:target :node) :feature-policy $ {} - :modules $ [] |skir/ |lilac/ + :modules $ [] |skir/ :type-slots $ {} :files $ {} 'app.http $ %{} 'FileEntry - :defs $ {} - 'IncomingRequestHost $ %{} 'CodeEntry (:doc |) - :code $ quote $ deftrait IncomingRequestHost - .on! $ :: 'Fn $ {} - :args $ [] 'app.http/IncomingRequestHost 'String 'Dynamic - :return 'app.http/IncomingRequestHost - :examples $ [] - :ffi $ {} (:backend :js) (:kind :external-object) (:target :node) - :names $ {} $ :on! |on - :schema $ :: 'Trait - 'extract-body $ %{} 'CodeEntry (:doc |) + :defs $ {} $ 'extract-body + %{} 'CodeEntry (:doc |) :code $ quote $ defn extract-body (req cb) - let - *buf $ atom | - req .on! |data $ fn (d) (swap! *buf str d) - req .on! |end $ fn () $ cb - parse-cirru-edn $ deref *buf + collect-body-str req $ Option :some $ fn (text) + hint-fn $ {} + :args $ [] 'String + :return 'Unit + cb $ try-parse-cirru-edn-as text $ :: 'Map 'Tag 'String , &unit + , &unit :examples $ [] :schema $ :: 'Fn $ {} (:return 'Unit) - :args $ [] 'app.http/IncomingRequestHost $ :: 'Fn + :args $ [] 'js-ffi.node/NodeRequestHost $ :: 'Fn {} (:return 'Unit) - :args $ [] 'Dynamic + :args $ [] $ :: 'Result (:: 'Map 'Tag 'String) 'String :features $ #{} :js-ffi :ns $ %{} 'NsEntry (:doc |) :code $ quote $ ns app.http + :require $ skir.util :refer $ collect-body-str 'app.main $ %{} 'FileEntry :defs $ {} 'CosClientHost $ %{} 'CodeEntry (:doc |) @@ -49,32 +42,35 @@ :ffi $ {} (:backend :js) (:kind :external-object) (:target :node) :names $ {} $ :get-object-url! |getObjectUrl :schema $ :: 'Trait - 'CosDataHost $ %{} 'CodeEntry (:doc |) - :code $ quote $ deftrait CosDataHost (:url 'String) - :examples $ [] - :ffi $ {} (:backend :js) (:kind :external-object) (:target :node) - :names $ {} $ :url |Url - :schema $ :: 'Trait 'handle-token $ %{} 'CodeEntry (:doc |) :code $ quote $ defn handle-token (bucket app-id secret file-key cb) let cos-obj $ unsafe-coerce new COS $ js-object (:SecretId app-id) (:SecretKey secret) , app.main/CosClientHost - w-js-log $ cos-obj .get-object-url! + cos-obj .get-object-url! js-object (:Bucket bucket) (:Region |ap-shanghai) :Key $ str |cos-up/ $ or file-key |demo0 :Method |PUT :Sign true - fn (err data) (js/console.log |COS_DATA: err data) - if (some? err) + fn (err data) + if (non-nil? err) cb $ {} (:code 400) :body $ {} $ :message (str err) let - cos-data $ unsafe-coerce data app.main/CosDataHost - cb $ {} (:code 200) - :headers $ {} $ |Access-Control-Allow-Origin |* - :body $ {} $ :url (.-url cos-data) + checked-url $ try + Result :ok $ contract/expect-string |COS.Url $ contract/object-field |COS.result data |Url + fn (_error) (Result :err "|Invalid COS response") + match checked-url + (:ok url) + cb $ {} (:code 200) + :headers $ {} $ |Access-Control-Allow-Origin |* + :body $ {} $ :url url + (:err _error) + cb $ {} (:code 500) + :body $ {} $ :message "|Invalid COS response" + , &unit + , &unit :examples $ [] :schema $ :: 'Fn $ {} (:return 'Unit) :args $ [] 'String 'String 'String 'String $ :: 'Fn @@ -83,7 +79,7 @@ :features $ #{} :js-ffi 'main! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn main! () - skir/create-server! on-request! $ %some $ {} + skir/create-server! on-request! $ Option :some $ {} :port $ js/parseInt $ option:unwrap-or (get-env |PORT) |4000 , &unit :examples $ [] @@ -93,39 +89,53 @@ 'on-request! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn on-request! (req-data res) let - request $ unsafe-coerce req-data Map - method $ option:unwrap-or (get request :method) :get - request-path $ option:unwrap-or (get request :path) | - println $ format-cirru-edn $ -> request (dissoc :original-request) (dissoc :headers) + request req-data + method $ :method request + request-path $ :path request + println method request-path if and (= :post method) (= |/token request-path) let - secrets $ unsafe-coerce - parse-cirru-edn $ fs/readFileSync + secrets $ parse-cirru-edn-as + contract/expect-string |secrets-file $ fs/readFileSync path/join (fileURLToPath js/import.meta.url) |../../secrets.cirru , |utf8 - , Map - original-request $ option:unwrap-or (get request :original-request) nil + :: 'Map 'Tag 'String + original-request $ .unwrap $ :original-request request fn (cb) - extract-body original-request $ fn (body) - let - payload $ unsafe-coerce body Map - user $ option:unwrap-or (get payload :user) | - pass $ option:unwrap-or (get payload :pass) | - expected-user $ option:unwrap-or (get secrets :user) | - expected-pass $ option:unwrap-or (get secrets :pass-md5) | - println |BODY payload - if - and (= user expected-user) (= pass expected-pass) - handle-token - option:unwrap-or (get secrets :bucket) | - option:unwrap-or (get secrets :app-id) | - option:unwrap-or (get secrets :secret) | - option:unwrap-or (get payload :file-key) | - , cb - cb $ {} (:code 403) (:message "|Not user") - :headers $ {} - :body $ {} $ :message "|not open for all users..." + hint-fn $ {} + :args $ [] $ :: 'Fn + {} + :args $ [] 'Dynamic + :return 'Unit + :return 'Unit + extract-body original-request $ fn (parsed) + hint-fn $ {} + :args $ [] $ :: 'Result (:: 'Map 'Tag 'String) 'String + :return 'Unit + match parsed + (:ok payload) + let + user $ .unwrap-or (get payload :user) | + pass $ .unwrap-or (get payload :pass) | + expected-user $ .unwrap-or (get secrets :user) | + expected-pass $ .unwrap-or (get secrets :pass-md5) | + if + and (= user expected-user) (= pass expected-pass) + handle-token + .unwrap-or (get secrets :bucket) | + .unwrap-or (get secrets :app-id) | + .unwrap-or (get secrets :secret) | + .unwrap-or (get payload :file-key) | + , cb + cb $ {} (:code 403) (:message "|Not user") + :headers $ {} + :body $ {} $ :message "|not open for all users..." + (:err _error) + cb $ {} (:code 400) + :body $ {} $ :message "|Invalid token request" + , &unit + , &unit if (= :options method) {} (:code 200) (:message |OK) :headers $ {} $ |Access-Control-Allow-Origin |* @@ -134,10 +144,10 @@ :body $ {} $ :message "|only token is impelemented" :examples $ [] :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'skir.schema/Request 'Dynamic + :args $ [] 'skir.schema/Request 'js-ffi.node/NodeServerResponseHost :features $ #{} :js-ffi 'reload! $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn reload! () (skir/reset-req-handler! on-request!) (println |Reloaded.) + :code $ quote $ defn reload! () (skir/reset-req-handler! on-request!) (println |Reloaded.) &unit :examples $ [] :schema $ :: 'Fn $ {} (:return 'Unit) :args $ [] @@ -150,3 +160,4 @@ |node:url :refer $ fileURLToPath |node:fs :as fs app.http :refer $ extract-body + js-ffi.contract :as contract diff --git a/deps.cirru b/deps.cirru index 8ae4e9c..7375f59 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,5 +1,4 @@ -{} (:calcit-version |0.22.0-alpha.3) (:version |0.0.9) +{} (:calcit-version |0.29.3) (:version |0.0.9) :dependencies $ {} - |calcit-lang/skir |0.0.28 - |calcit-lang/lilac |0.5.9 + |calcit-lang/skir |0.0.29 diff --git a/package.json b/package.json index 4e26875..51ecde8 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "packageManager": "yarn@4.12.0", + "packageManager": "yarn@4.18.0", "version": "0.0.9", "dependencies": { - "@calcit/procs": "0.22.0-alpha.3", + "@calcit/procs": "0.29.3", "cos-nodejs-sdk-v5": "^2.12.4" } } diff --git a/yarn.lock b/yarn.lock index 4977581..18669dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,17 +2,25 @@ # Manual changes might be lost - proceed with caution! __metadata: - version: 8 + version: 10 cacheKey: 10c0 -"@calcit/procs@npm:0.22.0-alpha.3": - version: 0.22.0-alpha.3 - resolution: "@calcit/procs@npm:0.22.0-alpha.3" +"@calcit/finger-vec@npm:0.1.1": + version: 0.1.1 + resolution: "@calcit/finger-vec@npm:0.1.1" + checksum: 10c0/c75e7f58924a7bb226ca4a47f6b0702021796b096c04d7fb7e9a92cb49b11ca64d654ea10d78bde29a8ac2d2d05ef9e7865efd2b358af2ca989d950b04d529a8 + languageName: node + linkType: hard + +"@calcit/procs@npm:0.29.3": + version: 0.29.3 + resolution: "@calcit/procs@npm:0.29.3" dependencies: + "@calcit/finger-vec": "npm:0.1.1" "@calcit/ternary-tree": "npm:0.0.26" - "@cirru/parser.ts": "npm:^0.0.9" + "@cirru/parser.ts": "npm:0.0.10" "@cirru/writer.ts": "npm:^0.1.9" - checksum: 10c0/b6aa28ae40918bef2e5363adbd4f5b36ec2ee26eda1a45b5e6c628b12c0d2746554d1106c5b0c86e2ff1be0fc726bed7173df1984e1dcb9a07d846b849db2a4e + checksum: 10c0/88713901f7e522a42252302ae43c44aa7d68551bb4b15add8521a9f3ef553a923c2ef8d13ee1d9413d0bec934efd7b63d4e77a2cdc777a550607f3e778d086a3 languageName: node linkType: hard @@ -23,10 +31,10 @@ __metadata: languageName: node linkType: hard -"@cirru/parser.ts@npm:^0.0.9": - version: 0.0.9 - resolution: "@cirru/parser.ts@npm:0.0.9" - checksum: 10c0/3b13623b8f627ac81adae0cb6a4e3664f0da09115a997ca9b78b8d5d0b0f9b9b18c1ad847e068831cb1052c7da2a00329d2af04421b1024a004eef0ddca5fd7a +"@cirru/parser.ts@npm:0.0.10": + version: 0.0.10 + resolution: "@cirru/parser.ts@npm:0.0.10" + checksum: 10c0/d66a16bcf49c60865fb8e733a5c219289a7c755c6400dea7e7a8d3166ee7841a623030f35f8ee96e232bc40f974f1b79a0ccdf089947fc5779699485102b5995 languageName: node linkType: hard @@ -600,7 +608,7 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@calcit/procs": "npm:0.22.0-alpha.3" + "@calcit/procs": "npm:0.29.3" cos-nodejs-sdk-v5: "npm:^2.12.4" languageName: unknown linkType: soft