@synopackageland/templates 0.9.9 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -27
- package/package.json +2 -2
- package/templates/_shared/AGENTS.md +53 -15
- package/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +1 -1
- package/templates/native-go/backend/cmd/app/main.go +15 -1
- package/templates/native-go/backend/cmd/app/main_test.go +33 -0
- package/templates/native-go/backend/go.mod +1 -1
- package/templates/native-go/backend/go.sum +2 -2
- package/templates/native-go/i18n/messages/de.json +4 -0
- package/templates/native-go/i18n/messages/en.json +4 -0
- package/templates/native-go/i18n/messages/es.json +4 -0
- package/templates/native-go/i18n/messages/fr.json +4 -0
- package/templates/native-go/i18n/messages/it.json +4 -0
- package/templates/native-go/i18n/messages/ja.json +4 -0
- package/templates/native-go/i18n/messages/ko.json +4 -0
- package/templates/native-go/i18n/messages/zh-CN.json +4 -0
- package/templates/native-go/i18n/messages/zh-TW.json +4 -0
- package/templates/native-go/ui/app.js +2 -1
- package/templates/native-go-delegate/frontend/test/capability-panels.test.ts +3 -2
- package/templates/native-go-jobs/frontend/test/background-page.test.ts +4 -4
- package/templates/native-go-vue/README.md +19 -7
- package/templates/native-go-vue/frontend/package-lock.json +9 -9
- package/templates/native-go-vue/frontend/package.json +2 -2
- package/templates/native-go-vue/frontend/src/App.vue +31 -36
- package/templates/native-go-vue/frontend/test/apps.test.ts +65 -12
- package/templates/native-go-vue/frontend/test/context.ts +8 -0
- package/templates/native-go-vue/frontend/test/native-controls.test.ts +3 -4
- package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +2 -2
- package/templates/native-node/README.md +153 -0
- package/templates/native-node/backend/app/package.json +4 -0
- package/templates/native-node/backend/app/permissions.json +5 -0
- package/templates/native-node/backend/package-lock.json +59 -0
- package/templates/native-node/backend/package.json +17 -0
- package/templates/native-node/backend/src/delegate.ts +5 -0
- package/templates/native-node/backend/src/jobs.ts +5 -0
- package/templates/native-node/backend/src/main.ts +4 -0
- package/templates/native-node/backend/src/params.ts +11 -0
- package/templates/native-node/backend/src/root-ops.ts +5 -0
- package/templates/native-node/backend/src/routes.ts +97 -0
- package/templates/native-node/backend/src/service.ts +22 -0
- package/templates/native-node/backend/test/routes.test.ts +75 -0
- package/templates/native-node/backend/test/support.ts +67 -0
- package/templates/native-node/backend/tsconfig.build.json +8 -0
- package/templates/native-node/backend/tsconfig.json +18 -0
- package/templates/native-node/dsm-package.yaml +80 -0
- package/templates/native-node/gitignore +4 -0
- package/templates/native-node-delegate/backend/src/delegate.ts +35 -0
- package/templates/native-node-delegate/backend/test/delegate.test.ts +51 -0
- package/templates/native-node-jobs/backend/src/jobs.ts +38 -0
- package/templates/native-node-jobs/backend/test/jobs.test.ts +73 -0
- package/templates/native-node-root-ops/backend/src/root-ops.ts +16 -0
- package/templates/native-node-root-ops/backend/test/root-ops.test.ts +23 -0
- package/templates/native-node-vue/README.md +56 -0
- package/templates/native-node-vue/gitignore +4 -0
- package/templates/native-python/README.md +150 -0
- package/templates/native-python/backend/app/delegate.py +5 -0
- package/templates/native-python/backend/app/jobs.py +5 -0
- package/templates/native-python/backend/app/main.py +5 -0
- package/templates/native-python/backend/app/params.py +10 -0
- package/templates/native-python/backend/app/permissions.json +5 -0
- package/templates/native-python/backend/app/root_ops.py +5 -0
- package/templates/native-python/backend/app/routes.py +107 -0
- package/templates/native-python/backend/tests/support.py +72 -0
- package/templates/native-python/backend/tests/test_routes.py +73 -0
- package/templates/native-python/dsm-package.yaml +77 -0
- package/templates/native-python/gitignore +2 -0
- package/templates/native-python-delegate/backend/app/delegate.py +42 -0
- package/templates/native-python-delegate/backend/tests/test_delegate.py +58 -0
- package/templates/native-python-jobs/backend/app/jobs.py +40 -0
- package/templates/native-python-jobs/backend/tests/test_jobs.py +72 -0
- package/templates/native-python-root-ops/backend/app/root_ops.py +18 -0
- package/templates/native-python-root-ops/backend/tests/test_root_ops.py +34 -0
- package/templates/native-python-vue/README.md +56 -0
- package/templates/native-python-vue/gitignore +3 -0
- package/templates/native-rust/README.md +74 -296
- package/templates/native-rust/backend/Cargo.lock +6 -6
- package/templates/native-rust/backend/Cargo.toml +2 -10
- package/templates/native-rust/backend/src/delegate.rs +6 -0
- package/templates/native-rust/backend/src/jobs.rs +6 -0
- package/templates/native-rust/backend/src/main.rs +12 -0
- package/templates/native-rust/backend/src/root_ops.rs +6 -0
- package/templates/native-rust/backend/src/routes.rs +352 -0
- package/templates/native-rust/backend/src/test_support.rs +91 -0
- package/templates/native-rust/dsm-package.yaml +10 -21
- package/templates/native-rust-delegate/backend/src/delegate.rs +171 -0
- package/templates/native-rust-jobs/backend/src/jobs.rs +240 -0
- package/templates/native-rust-root-ops/backend/src/root_ops.rs +83 -0
- package/templates/native-rust-vue/README.md +35 -34
- package/templates/native-rust/backend/THIRD_PARTY_NOTICES.txt +0 -207
- package/templates/native-rust/backend/src/app/background/mod.rs +0 -424
- package/templates/native-rust/backend/src/app/background/owner.rs +0 -77
- package/templates/native-rust/backend/src/app/background/owner_tests.rs +0 -174
- package/templates/native-rust/backend/src/app/background/schedule.rs +0 -126
- package/templates/native-rust/backend/src/app/background/schedule_tests.rs +0 -108
- package/templates/native-rust/backend/src/app/background/tests.rs +0 -505
- package/templates/native-rust/backend/src/app/background/work.rs +0 -78
- package/templates/native-rust/backend/src/app/background/work_tests.rs +0 -123
- package/templates/native-rust/backend/src/app/background_routes.rs +0 -22
- package/templates/native-rust/backend/src/app/delegate_config.rs +0 -188
- package/templates/native-rust/backend/src/app/delegate_routes.rs +0 -22
- package/templates/native-rust/backend/src/app/identity.rs +0 -56
- package/templates/native-rust/backend/src/app/migrations.rs +0 -79
- package/templates/native-rust/backend/src/app/mod.rs +0 -30
- package/templates/native-rust/backend/src/app/portal.rs +0 -317
- package/templates/native-rust/backend/src/app/root_routes.rs +0 -23
- package/templates/native-rust/backend/src/app/routes.rs +0 -315
- package/templates/native-rust/backend/src/app/session_files.rs +0 -19
- package/templates/native-rust/backend/src/app/shared_folder.rs +0 -383
- package/templates/native-rust/backend/src/app/test_support.rs +0 -202
- package/templates/native-rust/backend/src/app/uninstall.rs +0 -38
- package/templates/native-rust/backend/src/bin/api.rs +0 -72
- package/templates/native-rust/backend/src/bin/background-worker.rs +0 -37
- package/templates/native-rust/backend/src/build_env.rs +0 -155
- package/templates/native-rust/backend/src/lib.rs +0 -6
- package/templates/native-rust/backend/src/permissions.rs +0 -54
- package/templates/native-rust/backend/src/process.rs +0 -91
- package/templates/native-rust/i18n/glossary.json +0 -665
- package/templates/native-rust/i18n/messages/de.json +0 -32
- package/templates/native-rust/i18n/messages/en.json +0 -32
- package/templates/native-rust/i18n/messages/es.json +0 -32
- package/templates/native-rust/i18n/messages/fr.json +0 -32
- package/templates/native-rust/i18n/messages/it.json +0 -32
- package/templates/native-rust/i18n/messages/ja.json +0 -32
- package/templates/native-rust/i18n/messages/ko.json +0 -32
- package/templates/native-rust/i18n/messages/zh-CN.json +0 -32
- package/templates/native-rust/i18n/messages/zh-TW.json +0 -32
- package/templates/native-rust/ui/app.css +0 -45
- package/templates/native-rust/ui/app.js +0 -111
- package/templates/native-rust-delegate/backend/src/app/delegate_routes.rs +0 -92
- package/templates/native-rust-delegate/backend/src/bin/background-worker.rs +0 -52
- package/templates/native-rust-jobs/backend/src/app/background_routes.rs +0 -109
- package/templates/native-rust-root-ops/backend/src/app/root_routes.rs +0 -475
package/README.md
CHANGED
|
@@ -11,11 +11,11 @@ the files an App owns; the SDK (`github.com/synopackageland/syno-pkg-land-sdk`,
|
|
|
11
11
|
| `native-go-jobs` | Background job routes in `internal/app` and their page test |
|
|
12
12
|
| `native-go-delegate` | Delegate routes, the delegate background worker, the delegate `CapabilityPanels.vue` |
|
|
13
13
|
| `native-go-root-ops` | Executor routes in `internal/app` and an example `root/whoami.sh` |
|
|
14
|
-
| `native-
|
|
15
|
-
| `native-
|
|
16
|
-
| `native-rust
|
|
17
|
-
| `native-rust-delegate` |
|
|
18
|
-
| `native-rust-
|
|
14
|
+
| `native-python` | Base: `dsm-package.yaml` (`backend.runtime: python`), the App in `backend/app` (`routes.py`, stub capability modules, `permissions.json`), unittest tests |
|
|
15
|
+
| `native-node` | Base: `dsm-package.yaml` (`backend.runtime: node`), TypeScript in `backend/src` compiled into `backend/app`, node:test tests, `package-lock.json` |
|
|
16
|
+
| `native-rust` | Base: `dsm-package.yaml` (`backend.runtime: rust`, `methods`), the Cargo project in `backend` on the `syno-pkg-land-app` crate, `Cargo.lock`, `rust-toolchain.toml` |
|
|
17
|
+
| `native-{python,node,rust}-{jobs,root-ops,delegate}` | The capability's example module, replacing the base's stub, with its tests |
|
|
18
|
+
| `native-{python,node,rust}-vue` | The README and `.gitignore` of the Vue shell |
|
|
19
19
|
| `binary` | A prebuilt static CGI (`example/main.go`) with a plain UI |
|
|
20
20
|
| `compose` | Container Manager project (`compose.yaml`, `synology-app.yaml`) |
|
|
21
21
|
| `_shared` | Agent guides, template license, Go license texts, third-party notice writer |
|
|
@@ -23,25 +23,22 @@ the files an App owns; the SDK (`github.com/synopackageland/syno-pkg-land-sdk`,
|
|
|
23
23
|
Overlays are copied over `native-go` in the order root-ops, vue, jobs, delegate.
|
|
24
24
|
An overlay may replace `internal/app/*_none.go` (the CLI deletes the matching
|
|
25
25
|
`_none.go` file); nothing outside `internal/app` and the Vue shell differs.
|
|
26
|
+
The Python, Node.js and Rust templates borrow the Go template's UI: `ui/` and
|
|
27
|
+
`i18n/` from `native-go`, `frontend/` from `native-go-vue` (and the jobs and
|
|
28
|
+
delegate overlays' frontend files), `root/whoami.sh` from `native-go-root-ops`.
|
|
29
|
+
Their overlays are copied in the order vue, root-ops, jobs, delegate; delegate
|
|
30
|
+
also makes `app.status` and `report.save` `authenticated`, and the Rust
|
|
31
|
+
manifest gets the overlays' methods. `init` copies the App SDK into
|
|
32
|
+
`backend/vendor` (Python `synopkgland_app`; Node `@synopackageland/app`, its
|
|
33
|
+
package.json without devDependencies and scripts, and `dist/`).
|
|
26
34
|
The placeholders `__PACKAGE__` (also the Go module path), `__DISPLAY_NAME__` and
|
|
27
35
|
`__BACKGROUND_ENABLED__` are substituted by `init`.
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
base files (`background_routes.rs`, `delegate_routes.rs`, `root_routes.rs`,
|
|
31
|
-
`bin/background-worker.rs`) and nothing is removed. They carry only the
|
|
32
|
-
backend. A Rust project borrows the native-go overlays' non-backend files: the
|
|
33
|
-
Vue shell from `native-go-vue` (then `native-rust-vue`), `frontend/` of
|
|
34
|
-
`native-go-jobs` and `native-go-delegate`, and `root/whoami.sh` of
|
|
35
|
-
`native-go-root-ops`. A delegate project without root-ops also takes
|
|
36
|
-
`native-rust-root-ops`'s `root_routes.rs`, the executor shell Go borrows as
|
|
37
|
-
`executor.go`. `scripts/assemble.sh --language rust` does exactly this.
|
|
38
|
-
|
|
39
|
-
`backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.9.9`; that line is
|
|
37
|
+
`backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.10.0`; that line is
|
|
40
38
|
the single source of the SDK version and `frontend/package.json` pins
|
|
41
|
-
`@synopackageland/sdk` to the same version.
|
|
42
|
-
`
|
|
43
|
-
`Cargo.lock`
|
|
44
|
-
`SYNOPKGLAND_*` environment variables.
|
|
39
|
+
`@synopackageland/sdk` to the same version. Python and Node.js name it in
|
|
40
|
+
`backend.sdk`, Rust in the `syno-pkg-land-app` tag of `backend/Cargo.toml`
|
|
41
|
+
(and `Cargo.lock`); all of them pin 0.10.0.
|
|
45
42
|
|
|
46
43
|
## Notice writer
|
|
47
44
|
|
|
@@ -62,9 +59,10 @@ and `UI-THIRD_PARTY.md` in the output directory; other files there stay.
|
|
|
62
59
|
```sh
|
|
63
60
|
export GOPRIVATE='github.com/synopackageland/*' # the SDK repository is private
|
|
64
61
|
export SYNOPKGLAND_SDK_DIR=~/Public/synopackageland/sdk # optional sdk checkout: Go module at the root, ts/
|
|
62
|
+
export SYNOPKGLAND_DOCKER_SSH=my-docker-host # optional: the Docker engine for the Linux tests
|
|
65
63
|
scripts/assemble.sh /tmp/app vue delegate # one combination, as init lays it out
|
|
66
|
-
scripts/assemble.sh --language rust /tmp/app
|
|
67
|
-
scripts/gate.sh # layout, every combination
|
|
64
|
+
scripts/assemble.sh --language rust /tmp/app-rs plain jobs # python, node or rust
|
|
65
|
+
scripts/gate.sh # layout, every combination, binary, npm pack
|
|
68
66
|
```
|
|
69
67
|
|
|
70
68
|
Without `SYNOPKGLAND_SDK_DIR` the combinations use the released SDK that
|
|
@@ -72,11 +70,13 @@ Without `SYNOPKGLAND_SDK_DIR` the combinations use the released SDK that
|
|
|
72
70
|
`replace github.com/synopackageland/syno-pkg-land-sdk => $SYNOPKGLAND_SDK_DIR`
|
|
73
71
|
and installs `$SYNOPKGLAND_SDK_DIR/ts` as an `npm pack` tarball (not a link, so Vue
|
|
74
72
|
resolves once). With `SYNOPKGLAND_UI_TARBALL` set it installs that tarball as
|
|
75
|
-
`@synopackageland/ui`, for a UI version not yet on npm.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
`@synopackageland/ui`, for a UI version not yet on npm.
|
|
74
|
+
The Python and Node.js combinations need `SYNOPKGLAND_SDK_DIR` (its `python/`,
|
|
75
|
+
and `node/` built with `npm ci && npm run build`) for their App SDK, and the
|
|
76
|
+
Rust ones fetch the SDK crate from it (`scripts/rust-env.sh`), so it must hold
|
|
77
|
+
the tag `Cargo.lock` pins. The gate runs each language's tests on Linux in
|
|
78
|
+
Docker (`scripts/bridge-linux-test.sh`: Python 3.8, Node.js 18, Rust).
|
|
79
|
+
CI is not available; `scripts/gate.sh` is the gate.
|
|
80
80
|
|
|
81
81
|
SDK usage, protocol and API documentation: <https://github.com/synopackageland/syno-pkg-land-sdk>.
|
|
82
82
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/templates",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Project templates for synopkgland init: native-go and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Project templates for synopkgland init: native-go, native-python, native-node and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {"type": "git", "url": "git+https://github.com/synopackageland/syno-pkg-land-templates.git"},
|
|
7
7
|
"exports": {
|
|
@@ -213,20 +213,58 @@ sweep forward the cancel instead of admitting it.
|
|
|
213
213
|
`Service.OperationStatus`, `Service.ExportExample` (the SDK's
|
|
214
214
|
`delegate/example.go`).
|
|
215
215
|
|
|
216
|
+
## Python, Node.js and Rust backends
|
|
217
|
+
|
|
218
|
+
The SDK runtime is the CGI: it checks the DSM session, the entry gate and
|
|
219
|
+
permissions.json, then runs the App once per call, as the package account, and
|
|
220
|
+
answers the browser itself for every refusal. The App only registers methods
|
|
221
|
+
and asks the runtime for services with `ctx.runtime(op, params)`: `files.read`
|
|
222
|
+
(the caller's file, `files: session`), `bg.submit`/`bg.get`/`bg.cancel` (the
|
|
223
|
+
caller's jobs, `jobs: true`, whose worker hashes each job's input), `root.run`
|
|
224
|
+
(`rootOps`), `delegate.move_from_staging` (`delegate`) and `portal.status`. A
|
|
225
|
+
service refusal is a code the browser may see; pass it on. The admin guard
|
|
226
|
+
inside a shared method is `ctx.admin`. State lives in
|
|
227
|
+
`SYNOPKGLAND_DATA_DIR`. `synopkgland add` does not change these backends: choose
|
|
228
|
+
their capabilities with `synopkgland init --with`.
|
|
229
|
+
|
|
230
|
+
## Python backend
|
|
231
|
+
|
|
232
|
+
- Methods: `backend/app/routes.py` (`build` registers them, the capability
|
|
233
|
+
modules `jobs.py`, `root_ops.py`, `delegate.py` add theirs); permissions:
|
|
234
|
+
`backend/app/permissions.json`. DSM runs it with its own python3 (3.8):
|
|
235
|
+
standard library only, nothing newer than 3.8.
|
|
236
|
+
- Params: `fields(params, name=type)` (`params.py`); errors:
|
|
237
|
+
`Error(code, message)`, `Error.bad_request(message)`; anything else is
|
|
238
|
+
`internal`. `ctx.runtime` raises the runtime's refusal as an `Error`.
|
|
239
|
+
- Test with `(cd backend && python3 -m unittest discover -s tests)`;
|
|
240
|
+
`tests/support.py`'s `call` plays the runtime, with the services a test answers.
|
|
241
|
+
|
|
242
|
+
## Node.js backend
|
|
243
|
+
|
|
244
|
+
- Methods: `backend/src/routes.ts` (`build` registers them, the capability
|
|
245
|
+
modules `jobs.ts`, `root-ops.ts`, `delegate.ts` add theirs); permissions:
|
|
246
|
+
`backend/app/permissions.json`. TypeScript with `"module": "NodeNext"`;
|
|
247
|
+
`npm run build` compiles it into `backend/app`, which the SPK ships, so run it
|
|
248
|
+
before `synopkgland check` or `build`.
|
|
249
|
+
- Params: `strings(params, ...names)` (`params.ts`); errors:
|
|
250
|
+
`new AppError(code, message)`, `AppError.badRequest(message)`. Call services
|
|
251
|
+
through `service(ctx, op, params)` (`service.ts`), which passes the runtime's
|
|
252
|
+
refusals on; a bare `ctx.runtime` refusal would reach the browser as `internal`.
|
|
253
|
+
- Test with `(cd backend && npm ci && npm test && npm run typecheck)`; `test/support.ts`'s
|
|
254
|
+
`call` plays the runtime.
|
|
255
|
+
|
|
216
256
|
## Rust backend
|
|
217
257
|
|
|
218
|
-
-
|
|
219
|
-
`
|
|
220
|
-
|
|
221
|
-
`
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
|
|
225
|
-
`
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
`
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
- Delegate: `Service::destination`, `move_from_staging`, `cancel_operation`,
|
|
232
|
-
`operation_status`, `export_example` (the SDK's `delegate/example.rs`).
|
|
258
|
+
- Methods: `backend/src/routes.rs` (`build` registers them, the capability
|
|
259
|
+
modules `jobs.rs`, `root_ops.rs`, `delegate.rs` add theirs); permissions:
|
|
260
|
+
`backend/permissions.json`. The build cross-compiles the binary, which cannot
|
|
261
|
+
run on the build machine: list every method in `backend.methods` of
|
|
262
|
+
dsm-package.yaml too.
|
|
263
|
+
- Params: a `#[serde(deny_unknown_fields)]` struct and `params.decode()?`;
|
|
264
|
+
errors: `Error::new(code, message)`, `Error::bad_request(message)`,
|
|
265
|
+
`routes::internal(e)` for the unexpected. `ctx.runtime::<T>(op, ¶ms)?`
|
|
266
|
+
passes the runtime's refusal on.
|
|
267
|
+
- Test with `(cd backend && cargo test --locked)`, then
|
|
268
|
+
`cargo clippy --locked --all-targets -- -D warnings` and `cargo fmt --check`;
|
|
269
|
+
`test_support::call` plays the runtime. `rust-toolchain.toml` pins the
|
|
270
|
+
toolchain and the DSM targets; keep `Cargo.lock` committed.
|
|
@@ -11,6 +11,8 @@ import (
|
|
|
11
11
|
"net/http"
|
|
12
12
|
"net/http/cgi"
|
|
13
13
|
"os"
|
|
14
|
+
"strconv"
|
|
15
|
+
"time"
|
|
14
16
|
|
|
15
17
|
"github.com/synopackageland/syno-pkg-land-sdk/api"
|
|
16
18
|
"github.com/synopackageland/syno-pkg-land-sdk/dsm"
|
|
@@ -40,8 +42,20 @@ var (
|
|
|
40
42
|
// "on" when dsm-package.yaml declares portal: serves portal.status;
|
|
41
43
|
// "executor" below DSM 7.2, where the executor registers the entry.
|
|
42
44
|
portal = ""
|
|
45
|
+
// backend.timeout_seconds, 60 unless set.
|
|
46
|
+
requestTimeout = ""
|
|
43
47
|
)
|
|
44
48
|
|
|
49
|
+
// deadline is the CGI request deadline: requestTimeout seconds, or 60 when it
|
|
50
|
+
// is unset or not a positive number.
|
|
51
|
+
func deadline() api.DeadlineOption {
|
|
52
|
+
seconds, err := strconv.Atoi(requestTimeout)
|
|
53
|
+
if err != nil || seconds <= 0 {
|
|
54
|
+
seconds = 60
|
|
55
|
+
}
|
|
56
|
+
return api.Timeout(time.Duration(seconds) * time.Second)
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
func main() {
|
|
46
60
|
log.SetFlags(0)
|
|
47
61
|
log.SetPrefix(packageID + ": ")
|
|
@@ -104,7 +118,7 @@ func main() {
|
|
|
104
118
|
if standalone != nil {
|
|
105
119
|
handler = standalone.Wrap(handler)
|
|
106
120
|
}
|
|
107
|
-
if err := cgi.Serve(api.WithRequestDeadline(handler)); err != nil {
|
|
121
|
+
if err := cgi.Serve(api.WithRequestDeadline(handler, deadline())); err != nil {
|
|
108
122
|
log.Fatal(err)
|
|
109
123
|
}
|
|
110
124
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"net/http"
|
|
5
|
+
"net/http/httptest"
|
|
6
|
+
"testing"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"github.com/synopackageland/syno-pkg-land-sdk/api"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func TestRequestDeadlineFollowsTheBuildSetting(t *testing.T) {
|
|
13
|
+
saved := requestTimeout
|
|
14
|
+
t.Cleanup(func() { requestTimeout = saved })
|
|
15
|
+
for setting, want := range map[string]time.Duration{
|
|
16
|
+
"": 60 * time.Second,
|
|
17
|
+
"120": 120 * time.Second,
|
|
18
|
+
"1": time.Second,
|
|
19
|
+
"0": 60 * time.Second,
|
|
20
|
+
"-5": 60 * time.Second,
|
|
21
|
+
"x": 60 * time.Second,
|
|
22
|
+
} {
|
|
23
|
+
requestTimeout = setting
|
|
24
|
+
var left time.Duration
|
|
25
|
+
api.WithRequestDeadline(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
|
26
|
+
d, _ := r.Context().Deadline()
|
|
27
|
+
left = time.Until(d)
|
|
28
|
+
}), deadline()).ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("POST", "/", nil))
|
|
29
|
+
if left > want || left < want-time.Second {
|
|
30
|
+
t.Errorf("requestTimeout %q: deadline in %v, want %v", setting, left, want)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
github.com/synopackageland/syno-pkg-land-sdk v0.
|
|
2
|
-
github.com/synopackageland/syno-pkg-land-sdk v0.
|
|
1
|
+
github.com/synopackageland/syno-pkg-land-sdk v0.10.0 h1:YcKdeKZDRGqUuNuuGS1WIehCA9UgfB3cpHyP8eHYdds=
|
|
2
|
+
github.com/synopackageland/syno-pkg-land-sdk v0.10.0/go.mod h1:hlsvaVuH2wsAoR8jzI1Rg/NeejQSFpAaN6zpIWsVbjI=
|
|
3
3
|
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
|
4
4
|
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
19
19
|
"error.session_required": "Verbindung abgelaufen. Melden Sie sich erneut an.",
|
|
20
20
|
"error.unknown_method": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
21
|
+
"error.app_unavailable": "Die App ist derzeit nicht verfügbar. Versuchen Sie es später erneut.",
|
|
22
|
+
"error.limit_exceeded": "Die Datei ist größer als zulässig.",
|
|
23
|
+
"error.read_failed": "Die Datei kann nicht gelesen werden. Bitte versuchen Sie es später erneut.",
|
|
24
|
+
"error.identity_unavailable": "Dieses Benutzerkonto kann nicht ermittelt werden. Bitte versuchen Sie es später erneut.",
|
|
21
25
|
"greeting.adminOnly": "Nur Mitglieder der Gruppe administrators können den Gruß ändern.",
|
|
22
26
|
"greeting.label": "Neuer Gruß",
|
|
23
27
|
"greeting.save": "Speichern",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "Unable to perform this operation. Please try again later.",
|
|
19
19
|
"error.session_required": "Connection expired. Please sign in again.",
|
|
20
20
|
"error.unknown_method": "Unable to perform this operation. Please try again later.",
|
|
21
|
+
"error.app_unavailable": "The App is not available right now. Try again later.",
|
|
22
|
+
"error.limit_exceeded": "The file is larger than allowed.",
|
|
23
|
+
"error.read_failed": "Unable to read the file. Please try again later.",
|
|
24
|
+
"error.identity_unavailable": "Unable to identify this user account. Please try again later.",
|
|
21
25
|
"greeting.adminOnly": "Only members of the administrators group can change the greeting.",
|
|
22
26
|
"greeting.label": "New greeting",
|
|
23
27
|
"greeting.save": "Save",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
19
19
|
"error.session_required": "Conexión caducada. Vuelva a conectarse.",
|
|
20
20
|
"error.unknown_method": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
21
|
+
"error.app_unavailable": "La App no está disponible en este momento. Vuelva a intentarlo más tarde.",
|
|
22
|
+
"error.limit_exceeded": "El archivo supera el tamaño permitido.",
|
|
23
|
+
"error.read_failed": "No se puede leer el archivo. Vuelva a intentarlo más tarde.",
|
|
24
|
+
"error.identity_unavailable": "No se puede identificar esta cuenta de usuario. Vuelva a intentarlo más tarde.",
|
|
21
25
|
"greeting.adminOnly": "Solo los miembros del grupo administrators pueden cambiar el saludo.",
|
|
22
26
|
"greeting.label": "Nuevo saludo",
|
|
23
27
|
"greeting.save": "Guardar",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
19
19
|
"error.session_required": "La connexion a expiré. Reconnectez-vous.",
|
|
20
20
|
"error.unknown_method": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
21
|
+
"error.app_unavailable": "L’App n’est pas disponible pour le moment. Réessayez plus tard.",
|
|
22
|
+
"error.limit_exceeded": "Le fichier dépasse la taille autorisée.",
|
|
23
|
+
"error.read_failed": "Impossible de lire le fichier. Veuillez réessayer ultérieurement.",
|
|
24
|
+
"error.identity_unavailable": "Impossible d'identifier ce compte utilisateur. Veuillez réessayer ultérieurement.",
|
|
21
25
|
"greeting.adminOnly": "Seuls les membres du groupe administrators peuvent modifier le message d'accueil.",
|
|
22
26
|
"greeting.label": "Nouveau message d'accueil",
|
|
23
27
|
"greeting.save": "Sauvegarder",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
19
19
|
"error.session_required": "Connessione scaduta. Accedere nuovamente.",
|
|
20
20
|
"error.unknown_method": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
21
|
+
"error.app_unavailable": "L’App non è al momento disponibile. Riprovare più tardi.",
|
|
22
|
+
"error.limit_exceeded": "Il file supera le dimensioni consentite.",
|
|
23
|
+
"error.read_failed": "Impossibile leggere il file. Riprovare più tardi.",
|
|
24
|
+
"error.identity_unavailable": "Impossibile identificare questo account utente. Riprovare più tardi.",
|
|
21
25
|
"greeting.adminOnly": "Solo i membri del gruppo administrators possono modificare il saluto.",
|
|
22
26
|
"greeting.label": "Nuovo saluto",
|
|
23
27
|
"greeting.save": "Salva",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "この操作は実行できません。後でもう一度お試しください。",
|
|
19
19
|
"error.session_required": "接続の期限が切れています。もう 1 度サイン インしてください。",
|
|
20
20
|
"error.unknown_method": "この操作は実行できません。後でもう一度お試しください。",
|
|
21
|
+
"error.app_unavailable": "App は現在使用できません。しばらくしてから再試行してください。",
|
|
22
|
+
"error.limit_exceeded": "ファイルが許可されたサイズを超えています。",
|
|
23
|
+
"error.read_failed": "ファイルを読み取れません。後でもう一度お試しください。",
|
|
24
|
+
"error.identity_unavailable": "このユーザー アカウントを識別できません。後でもう一度お試しください。",
|
|
21
25
|
"greeting.adminOnly": "あいさつを変更できるのは administrators グループのメンバーのみです。",
|
|
22
26
|
"greeting.label": "新しいあいさつ",
|
|
23
27
|
"greeting.save": "保存",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
19
19
|
"error.session_required": "연결 만료됨. 다시 로그인 해 주십시오.",
|
|
20
20
|
"error.unknown_method": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
21
|
+
"error.app_unavailable": "현재 App을 사용할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
22
|
+
"error.limit_exceeded": "파일이 허용된 크기를 초과합니다.",
|
|
23
|
+
"error.read_failed": "파일을 읽을 수 없습니다. 나중에 다시 시도하십시오.",
|
|
24
|
+
"error.identity_unavailable": "이 사용자 계정을 확인할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
21
25
|
"greeting.adminOnly": "administrators 그룹의 구성원만 인사말을 변경할 수 있습니다.",
|
|
22
26
|
"greeting.label": "새 인사말",
|
|
23
27
|
"greeting.save": "저장",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "无法执行此操作。请稍后再试。",
|
|
19
19
|
"error.session_required": "联机超时,请重新登录。",
|
|
20
20
|
"error.unknown_method": "无法执行此操作。请稍后再试。",
|
|
21
|
+
"error.app_unavailable": "App 当前不可用,请稍后重试。",
|
|
22
|
+
"error.limit_exceeded": "文件超过允许的大小。",
|
|
23
|
+
"error.read_failed": "无法读取文件。请稍后再试。",
|
|
24
|
+
"error.identity_unavailable": "无法识别此用户帐号。请稍后再试。",
|
|
21
25
|
"greeting.adminOnly": "只有 administrators 群组的成员可以更改问候语。",
|
|
22
26
|
"greeting.label": "新的问候语",
|
|
23
27
|
"greeting.save": "保存",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"error.request_canceled": "無法執行此操作。請稍後再試。",
|
|
19
19
|
"error.session_required": "連線逾時。請重新登入。",
|
|
20
20
|
"error.unknown_method": "無法執行此操作。請稍後再試。",
|
|
21
|
+
"error.app_unavailable": "App 目前無法使用,請稍後再試。",
|
|
22
|
+
"error.limit_exceeded": "檔案超過允許的大小。",
|
|
23
|
+
"error.read_failed": "無法讀取檔案。請稍後再試。",
|
|
24
|
+
"error.identity_unavailable": "無法識別此使用者帳號。請稍後再試。",
|
|
21
25
|
"greeting.adminOnly": "只有 administrators 群組的成員可以變更問候語。",
|
|
22
26
|
"greeting.label": "新的問候語",
|
|
23
27
|
"greeting.save": "儲存",
|
|
@@ -71,7 +71,8 @@
|
|
|
71
71
|
[[t("status.user"), data.admin ? t("status.userAdmin", { user: data.user }) : data.user],
|
|
72
72
|
[t("status.package"), data.package],
|
|
73
73
|
[t("status.version"), data.version],
|
|
74
|
-
|
|
74
|
+
// A Go backend reports go_version; a Python, Node.js or Rust App reports language.
|
|
75
|
+
[t("status.backend"), (data.language || data.go_version) + " · " + data.platform],
|
|
75
76
|
[t("status.uid"), String(data.process.uid)]].forEach(function (row) {
|
|
76
77
|
status.appendChild(el("dt", { text: row[0] }));
|
|
77
78
|
status.appendChild(el("dd", { text: row[1] }));
|
|
@@ -5,6 +5,7 @@ import {AppRoot} from '@synopackageland/ui'
|
|
|
5
5
|
import type {BackgroundSaveFacade} from '@synopackageland/sdk'
|
|
6
6
|
import CapabilityPanels from '../src/CapabilityPanels.vue'
|
|
7
7
|
import {mount} from '../src/main'
|
|
8
|
+
import {dsmWindow} from './context'
|
|
8
9
|
const cleanup:Array<()=>void>=[]
|
|
9
10
|
afterEach(()=>cleanup.splice(0).forEach(fn=>fn()))
|
|
10
11
|
async function settle(){for(let i=0;i<30;i++){await Promise.resolve();await nextTick()}}
|
|
@@ -66,7 +67,7 @@ test('the app mount forwards ctx.executor to administrator setup only',async()=>
|
|
|
66
67
|
throw {code:'unknown_method'}
|
|
67
68
|
})
|
|
68
69
|
const root=document.createElement('div');document.body.append(root)
|
|
69
|
-
const app=mount(root,{
|
|
70
|
+
const app=mount(root,dsmWindow({call,executor:{status,authorize,control:vi.fn(async()=>({state:'ready' as const}))} as never}))
|
|
70
71
|
cleanup.push(()=>{app.unmount();root.remove()});await settle()
|
|
71
72
|
if(admin){
|
|
72
73
|
expect(status).toHaveBeenCalled()
|
|
@@ -90,7 +91,7 @@ test('the header Refresh reloads the save destinations',async()=>{
|
|
|
90
91
|
throw {code:'unknown_method'}
|
|
91
92
|
})
|
|
92
93
|
const root=document.createElement('div');document.body.append(root)
|
|
93
|
-
const app=mount(root,{
|
|
94
|
+
const app=mount(root,dsmWindow({call}))
|
|
94
95
|
cleanup.push(()=>{app.unmount();root.remove()});await settle()
|
|
95
96
|
const reads=()=>call.mock.calls.filter(([method])=>method==='delegate.destination').length
|
|
96
97
|
const before=reads();expect(before).toBeGreaterThan(0)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {expect,test,vi} from 'vitest'
|
|
3
3
|
import {nextTick} from 'vue'
|
|
4
4
|
import {mount} from '../src/main'
|
|
5
|
-
import
|
|
5
|
+
import {dsmWindow} from './context'
|
|
6
6
|
async function settle(){for(let i=0;i<20;i++){await Promise.resolve();await nextTick()}}
|
|
7
7
|
// The CLI turns the background page on for jobs (and delegate) projects only.
|
|
8
8
|
test('a jobs project mounts the background panel for the signed-in user',async()=>{
|
|
@@ -11,7 +11,7 @@ test('a jobs project mounts the background panel for the signed-in user',async()
|
|
|
11
11
|
throw {code:'unknown_method'}
|
|
12
12
|
})
|
|
13
13
|
const root=document.createElement('div');document.body.append(root)
|
|
14
|
-
const app=mount(root,{
|
|
14
|
+
const app=mount(root,dsmWindow({call}))
|
|
15
15
|
try{
|
|
16
16
|
await settle()
|
|
17
17
|
expect(call.mock.calls.some(([method])=>method.startsWith('bg.'))).toBe(true)
|
|
@@ -29,7 +29,7 @@ test('the background panel pauses a running job and resumes a paused one',async(
|
|
|
29
29
|
return {}
|
|
30
30
|
})
|
|
31
31
|
const root=document.createElement('div');document.body.append(root)
|
|
32
|
-
const app=mount(root,{
|
|
32
|
+
const app=mount(root,dsmWindow({call}))
|
|
33
33
|
const button=(id:string,label:string)=>Array.from(root.querySelectorAll(`[data-job-id="${id}"] button`)).find(b=>b.textContent===label) as HTMLButtonElement|undefined
|
|
34
34
|
try{
|
|
35
35
|
await settle()
|
|
@@ -48,7 +48,7 @@ test('the header Refresh reloads the background panel',async()=>{
|
|
|
48
48
|
return {}
|
|
49
49
|
})
|
|
50
50
|
const root=document.createElement('div');document.body.append(root)
|
|
51
|
-
const app=mount(root,{
|
|
51
|
+
const app=mount(root,dsmWindow({call}))
|
|
52
52
|
const reads=()=>call.mock.calls.filter(([method])=>method==='bg.jobs.list').length
|
|
53
53
|
try{
|
|
54
54
|
await settle();const before=reads()
|
|
@@ -33,14 +33,26 @@ Generated build and unit tests do not establish NAS runtime acceptance.
|
|
|
33
33
|
The manifest is dsm-package.yaml schema 2: backend.kind: go and capabilities.files: session, jobs: true, identity: standalone, rootOps: true. Use --with login to declare standalone identity. Existing projects use synopkgland add <cap>...; app entrypoints stay app-owned, SDK conflicts are refused. check, inspect, build, deploy and status accept --json.
|
|
34
34
|
|
|
35
35
|
Use the installed `@synopackageland/ui`; package.json pins its version.
|
|
36
|
-
`
|
|
37
|
-
`
|
|
38
|
-
|
|
39
|
-
`wide-layout="
|
|
40
|
-
|
|
36
|
+
`App.vue` lays the shell out with the SDK's `AppShell` (`@synopackageland/sdk/vue`):
|
|
37
|
+
`pages` from `shellPages`, the header buttons in `#actions` and the pages in its
|
|
38
|
+
default slot. Given `ctx`, it follows the launch. In a DSM desktop window it
|
|
39
|
+
keeps `wide-layout="window"`, even maximized; the DSM host sets the 700px
|
|
40
|
+
minimum window width. A Portal or standalone launch owns the whole page
|
|
41
|
+
(`ctx.fullPage`, from the generated window): `AppShell` takes
|
|
42
|
+
`wide-layout="web"` (the SaaS layout at 1200px or wider, mobile on a phone) and
|
|
43
|
+
a device-width viewport, undone at unmount, since DSM's page has no
|
|
44
|
+
device-width viewport. Its full-page sidebar names the App with
|
|
45
|
+
`ctx.displayName` and `ctx.appIcon` (without them the page title and icon) and
|
|
46
|
+
shows the signed-in DSM account and its role from `ctx.user`. See the ui's
|
|
47
|
+
`docs/adaptive-layout.md`.
|
|
48
|
+
`AppShell`'s `app-root` wraps it in an app-local `AppRoot` (app modal scope, on
|
|
49
|
+
a full page as well, bounded at `height: 100%`) with the UI locale from
|
|
50
|
+
`locale`; do not add another `AppRoot`.
|
|
51
|
+
In a DSM window (not a full page) that `AppRoot` takes
|
|
41
52
|
`dialog-bounds="viewport"`, so Dialogs can be dragged out of the window, and
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
pressing one calls `ctx.host.activateWindow()` to bring the window to the
|
|
54
|
+
front. Elsewhere Dialogs stay inside the App. A confirmed sign-in need sets
|
|
55
|
+
`denied`: the `#denied` slot's `LoginPage` takes the place of the App's pages.
|
|
44
56
|
Import `themes/dsm7-embedded.css` and assign each mount a unique
|
|
45
57
|
`app.config.idPrefix` before mounting.
|
|
46
58
|
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"name": "native-dsm-vue-app",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@lucide/vue": "1.48.0",
|
|
10
|
-
"@synopackageland/sdk": "0.
|
|
11
|
-
"@synopackageland/ui": "0.9.
|
|
10
|
+
"@synopackageland/sdk": "0.10.0",
|
|
11
|
+
"@synopackageland/ui": "0.9.4",
|
|
12
12
|
"vue": "3.5.43"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
@@ -1148,13 +1148,13 @@
|
|
|
1148
1148
|
}
|
|
1149
1149
|
},
|
|
1150
1150
|
"node_modules/@synopackageland/sdk": {
|
|
1151
|
-
"version": "0.
|
|
1152
|
-
"resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.
|
|
1153
|
-
"integrity": "sha512-
|
|
1151
|
+
"version": "0.10.0",
|
|
1152
|
+
"resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.10.0.tgz",
|
|
1153
|
+
"integrity": "sha512-oRwr3I4WNWCqknTPvDnlR7wjM/6pBX+UJ/GRc2U4l9+VUGrdgq0UH9gCjmGIZS9beE5y33tcxmj2ASweLUbJIg==",
|
|
1154
1154
|
"license": "MIT",
|
|
1155
1155
|
"peerDependencies": {
|
|
1156
1156
|
"@lucide/vue": "^1.48.0",
|
|
1157
|
-
"@synopackageland/ui": "^0.9.
|
|
1157
|
+
"@synopackageland/ui": "^0.9.4",
|
|
1158
1158
|
"vue": "^3.5.0"
|
|
1159
1159
|
},
|
|
1160
1160
|
"peerDependenciesMeta": {
|
|
@@ -1167,9 +1167,9 @@
|
|
|
1167
1167
|
}
|
|
1168
1168
|
},
|
|
1169
1169
|
"node_modules/@synopackageland/ui": {
|
|
1170
|
-
"version": "0.9.
|
|
1171
|
-
"resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.9.
|
|
1172
|
-
"integrity": "sha512-
|
|
1170
|
+
"version": "0.9.4",
|
|
1171
|
+
"resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.9.4.tgz",
|
|
1172
|
+
"integrity": "sha512-yu0RrYtT/QcAn/lNwqZBoqqjLTXrpqqnSWB+0swGou8IWLrhfwXrHVBf+oJc+NjLj+G4kB7AyP9kbvw9FHxNOA==",
|
|
1173
1173
|
"license": "MIT",
|
|
1174
1174
|
"dependencies": {
|
|
1175
1175
|
"@internationalized/date": "3.12.4",
|