@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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
//! Background jobs: hash a NAS file the caller may read.
|
|
2
|
+
//!
|
|
3
|
+
//! The SDK's worker runs each job as the package account: it hashes the job's
|
|
4
|
+
//! input (SHA-256) and keeps the result for the job's owner, the caller. The
|
|
5
|
+
//! runtime serves files.read with the caller's DSM session and bg.* for the
|
|
6
|
+
//! caller's own jobs only.
|
|
7
|
+
|
|
8
|
+
use crate::routes::{Config, internal};
|
|
9
|
+
use serde::Deserialize;
|
|
10
|
+
use serde_json::{Value, json};
|
|
11
|
+
use std::os::unix::fs::DirBuilderExt;
|
|
12
|
+
use std::path::{Path, PathBuf};
|
|
13
|
+
use syno_pkg_land_app::{App, Context, Error, Params};
|
|
14
|
+
|
|
15
|
+
/// A job's input is at most 512 KiB, copied into the job when it is submitted.
|
|
16
|
+
const MAX_INPUT: u64 = 512 * 1024;
|
|
17
|
+
|
|
18
|
+
#[derive(Deserialize)]
|
|
19
|
+
#[serde(deny_unknown_fields)]
|
|
20
|
+
struct Submit {
|
|
21
|
+
key: String,
|
|
22
|
+
path: String,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[derive(Deserialize)]
|
|
26
|
+
#[serde(deny_unknown_fields)]
|
|
27
|
+
struct JobId {
|
|
28
|
+
id: String,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[derive(Deserialize)]
|
|
32
|
+
struct Read {
|
|
33
|
+
path: PathBuf,
|
|
34
|
+
size: u64,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn register(app: &mut App, cfg: &Config) {
|
|
38
|
+
let cfg = cfg.clone();
|
|
39
|
+
app.method("hash.submit", move |ctx, params| submit(&cfg, ctx, params));
|
|
40
|
+
app.method("hash.get", |ctx, params| job(ctx, "bg.get", params));
|
|
41
|
+
app.method("hash.cancel", |ctx, params| job(ctx, "bg.cancel", params));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn job(ctx: &Context, op: &str, params: &Params) -> Result<Value, Error> {
|
|
45
|
+
let JobId { id } = params.decode()?;
|
|
46
|
+
ctx.runtime(op, &json!({"id": id}))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fn submit(cfg: &Config, ctx: &Context, params: &Params) -> Result<Value, Error> {
|
|
50
|
+
let p: Submit = params.decode()?;
|
|
51
|
+
let read: Read = ctx.runtime("files.read", &json!({"path": p.path}))?;
|
|
52
|
+
if read.size > MAX_INPUT {
|
|
53
|
+
return Err(Error::new(
|
|
54
|
+
"limit_exceeded",
|
|
55
|
+
"The file is larger than allowed.",
|
|
56
|
+
));
|
|
57
|
+
}
|
|
58
|
+
// bg.submit takes its input from the data directory; the call's spool is elsewhere.
|
|
59
|
+
let inputs = cfg.data_dir.join("hash-inputs");
|
|
60
|
+
std::fs::DirBuilder::new()
|
|
61
|
+
.recursive(true)
|
|
62
|
+
.mode(0o700)
|
|
63
|
+
.create(&inputs)
|
|
64
|
+
.map_err(internal)?;
|
|
65
|
+
let copy = inputs.join(&ctx.request_id);
|
|
66
|
+
std::fs::copy(&read.path, ©).map_err(internal)?;
|
|
67
|
+
let name = Path::new(&p.path)
|
|
68
|
+
.file_name()
|
|
69
|
+
.map_or(String::new(), |n| n.to_string_lossy().into_owned());
|
|
70
|
+
let submitted = ctx.runtime(
|
|
71
|
+
"bg.submit",
|
|
72
|
+
&json!({"key": p.key, "name": name, "input": copy}),
|
|
73
|
+
);
|
|
74
|
+
let _ = std::fs::remove_file(©);
|
|
75
|
+
submitted
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[cfg(test)]
|
|
79
|
+
mod tests {
|
|
80
|
+
use super::*;
|
|
81
|
+
use crate::test_support::{Services, TempDir, call};
|
|
82
|
+
use serde_json::{Value, json};
|
|
83
|
+
use std::sync::{Arc, Mutex};
|
|
84
|
+
|
|
85
|
+
/// What bg.submit was asked: its params and the input it read.
|
|
86
|
+
type Submitted = Arc<Mutex<Vec<(Value, Vec<u8>)>>>;
|
|
87
|
+
|
|
88
|
+
fn setup() -> (TempDir, Config) {
|
|
89
|
+
let dir = TempDir::new();
|
|
90
|
+
let cfg = Config {
|
|
91
|
+
package: "ExampleApp".into(),
|
|
92
|
+
data_dir: dir.path().join("var"),
|
|
93
|
+
packages_root: dir.path().into(),
|
|
94
|
+
};
|
|
95
|
+
std::fs::create_dir(&cfg.data_dir).unwrap();
|
|
96
|
+
(dir, cfg)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fn job() -> Value {
|
|
100
|
+
json!({"job": {"id": "j1", "state": "queued"}, "duplicate": false})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// files.read answers a spool file with content; bg.submit records its params and the input it read.
|
|
104
|
+
fn services(dir: &TempDir, content: Vec<u8>, submitted: &Submitted) -> Services {
|
|
105
|
+
let spool = dir.path().join("spool");
|
|
106
|
+
std::fs::write(&spool, &content).unwrap();
|
|
107
|
+
let read = json!({"path": spool, "size": content.len()});
|
|
108
|
+
let submitted = submitted.clone();
|
|
109
|
+
[
|
|
110
|
+
(
|
|
111
|
+
"files.read",
|
|
112
|
+
Box::new(move |_: &Value| Ok(read.clone())) as _,
|
|
113
|
+
),
|
|
114
|
+
(
|
|
115
|
+
"bg.submit",
|
|
116
|
+
Box::new(move |p: &Value| {
|
|
117
|
+
// The runtime reads the input file while it answers bg.submit.
|
|
118
|
+
let input = std::fs::read(p["input"].as_str().unwrap()).unwrap();
|
|
119
|
+
submitted.lock().unwrap().push((p.clone(), input));
|
|
120
|
+
Ok(job())
|
|
121
|
+
}) as _,
|
|
122
|
+
),
|
|
123
|
+
]
|
|
124
|
+
.into()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#[test]
|
|
128
|
+
fn submit_hashes_a_copy_of_the_callers_file() {
|
|
129
|
+
let (dir, cfg) = setup();
|
|
130
|
+
let submitted = Arc::new(Mutex::new(Vec::new()));
|
|
131
|
+
let services = services(&dir, b"hello".to_vec(), &submitted);
|
|
132
|
+
let reply = call(
|
|
133
|
+
crate::routes::build(cfg.clone()),
|
|
134
|
+
"hash.submit",
|
|
135
|
+
json!({"key": "k1", "path": "/home/report.txt"}),
|
|
136
|
+
"alice",
|
|
137
|
+
&services,
|
|
138
|
+
);
|
|
139
|
+
assert_eq!(reply.data, job());
|
|
140
|
+
let ops: Vec<_> = reply.services.iter().map(|(op, _)| op.as_str()).collect();
|
|
141
|
+
assert_eq!(ops, ["files.read", "bg.submit"]);
|
|
142
|
+
assert_eq!(reply.services[0].1, json!({"path": "/home/report.txt"}));
|
|
143
|
+
let submitted = submitted.lock().unwrap();
|
|
144
|
+
let (params, content) = &submitted[0];
|
|
145
|
+
assert_eq!(
|
|
146
|
+
(¶ms["key"], ¶ms["name"], content.as_slice()),
|
|
147
|
+
(&json!("k1"), &json!("report.txt"), b"hello".as_slice())
|
|
148
|
+
);
|
|
149
|
+
let input = params["input"].as_str().unwrap();
|
|
150
|
+
assert!(input.starts_with(cfg.data_dir.to_str().unwrap()), "{input}");
|
|
151
|
+
assert!(
|
|
152
|
+
!std::path::Path::new(input).exists(),
|
|
153
|
+
"the copy is removed once submitted"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[test]
|
|
158
|
+
fn submit_refuses_a_file_over_the_job_input_limit() {
|
|
159
|
+
let (dir, cfg) = setup();
|
|
160
|
+
let submitted = Arc::new(Mutex::new(Vec::new()));
|
|
161
|
+
let services = services(&dir, vec![0; MAX_INPUT as usize + 1], &submitted);
|
|
162
|
+
let reply = call(
|
|
163
|
+
crate::routes::build(cfg),
|
|
164
|
+
"hash.submit",
|
|
165
|
+
json!({"key": "k1", "path": "/home/big.bin"}),
|
|
166
|
+
"alice",
|
|
167
|
+
&services,
|
|
168
|
+
);
|
|
169
|
+
assert_eq!(reply.code(), "limit_exceeded");
|
|
170
|
+
assert!(submitted.lock().unwrap().is_empty());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#[test]
|
|
174
|
+
fn submit_passes_on_the_runtimes_refusal() {
|
|
175
|
+
let (_dir, cfg) = setup();
|
|
176
|
+
let services: Services = [(
|
|
177
|
+
"files.read",
|
|
178
|
+
Box::new(|_: &Value| Err(Error::new("read_failed", "Unable to read the file."))) as _,
|
|
179
|
+
)]
|
|
180
|
+
.into();
|
|
181
|
+
let reply = call(
|
|
182
|
+
crate::routes::build(cfg),
|
|
183
|
+
"hash.submit",
|
|
184
|
+
json!({"key": "k1", "path": "/home/a"}),
|
|
185
|
+
"alice",
|
|
186
|
+
&services,
|
|
187
|
+
);
|
|
188
|
+
assert_eq!(reply.code(), "read_failed");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[test]
|
|
192
|
+
fn submit_refuses_invalid_params_before_any_service() {
|
|
193
|
+
let (_dir, cfg) = setup();
|
|
194
|
+
for params in [
|
|
195
|
+
Value::Null,
|
|
196
|
+
json!({"key": "k1"}),
|
|
197
|
+
json!({"key": 1, "path": "/home/a"}),
|
|
198
|
+
json!({"key": "k1", "path": "/home/a", "x": 1}),
|
|
199
|
+
] {
|
|
200
|
+
let reply = call(
|
|
201
|
+
crate::routes::build(cfg.clone()),
|
|
202
|
+
"hash.submit",
|
|
203
|
+
params.clone(),
|
|
204
|
+
"alice",
|
|
205
|
+
&Services::new(),
|
|
206
|
+
);
|
|
207
|
+
assert_eq!(
|
|
208
|
+
(reply.code(), reply.services.len()),
|
|
209
|
+
("bad_request", 0),
|
|
210
|
+
"{params}"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
#[test]
|
|
216
|
+
fn get_and_cancel_act_on_the_callers_job() {
|
|
217
|
+
let (_dir, cfg) = setup();
|
|
218
|
+
for (method, op) in [("hash.get", "bg.get"), ("hash.cancel", "bg.cancel")] {
|
|
219
|
+
let services: Services =
|
|
220
|
+
[(op, Box::new(|_: &Value| Ok(job()["job"].clone())) as _)].into();
|
|
221
|
+
let reply = call(
|
|
222
|
+
crate::routes::build(cfg.clone()),
|
|
223
|
+
method,
|
|
224
|
+
json!({"id": "j1"}),
|
|
225
|
+
"alice",
|
|
226
|
+
&services,
|
|
227
|
+
);
|
|
228
|
+
assert_eq!(reply.data, job()["job"]);
|
|
229
|
+
assert_eq!(reply.services, vec![(op.to_string(), json!({"id": "j1"}))]);
|
|
230
|
+
let reply = call(
|
|
231
|
+
crate::routes::build(cfg.clone()),
|
|
232
|
+
method,
|
|
233
|
+
json!({}),
|
|
234
|
+
"alice",
|
|
235
|
+
&Services::new(),
|
|
236
|
+
);
|
|
237
|
+
assert_eq!(reply.code(), "bad_request");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//! Root operations: run `whoami`, a program the package ships in root/ and
|
|
2
|
+
//! dsm-package.yaml approves, as root through the executor. The runtime
|
|
3
|
+
//! serves root.run only when the executor has the program's grant.
|
|
4
|
+
|
|
5
|
+
use crate::routes::Config;
|
|
6
|
+
use serde::Deserialize;
|
|
7
|
+
use serde_json::Value;
|
|
8
|
+
use syno_pkg_land_app::App;
|
|
9
|
+
|
|
10
|
+
#[derive(Deserialize)]
|
|
11
|
+
#[serde(deny_unknown_fields)]
|
|
12
|
+
struct NoParams {}
|
|
13
|
+
|
|
14
|
+
pub fn register(app: &mut App, _cfg: &Config) {
|
|
15
|
+
app.method("whoami.run", |ctx, params| {
|
|
16
|
+
params.decode::<Option<NoParams>>()?;
|
|
17
|
+
ctx.runtime::<Value>("root.run", &serde_json::json!({"name": "whoami"}))
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[cfg(test)]
|
|
22
|
+
mod tests {
|
|
23
|
+
use crate::routes::{Config, build};
|
|
24
|
+
use crate::test_support::{Services, call};
|
|
25
|
+
use serde_json::{Value, json};
|
|
26
|
+
use syno_pkg_land_app::Error;
|
|
27
|
+
|
|
28
|
+
fn cfg() -> Config {
|
|
29
|
+
Config {
|
|
30
|
+
package: "ExampleApp".into(),
|
|
31
|
+
data_dir: "/nonexistent".into(),
|
|
32
|
+
packages_root: "/nonexistent".into(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[test]
|
|
37
|
+
fn whoami_runs_the_approved_root_program() {
|
|
38
|
+
let output = json!({"exit_code": 0, "stdout": "0\n", "stderr": ""});
|
|
39
|
+
for params in [Value::Null, json!({})] {
|
|
40
|
+
let answer = output.clone();
|
|
41
|
+
let services: Services = [(
|
|
42
|
+
"root.run",
|
|
43
|
+
Box::new(move |_: &Value| Ok(answer.clone())) as _,
|
|
44
|
+
)]
|
|
45
|
+
.into();
|
|
46
|
+
let reply = call(build(cfg()), "whoami.run", params, "admin", &services);
|
|
47
|
+
assert_eq!(reply.data, output);
|
|
48
|
+
assert_eq!(
|
|
49
|
+
reply.services,
|
|
50
|
+
vec![("root.run".to_string(), json!({"name": "whoami"}))]
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[test]
|
|
56
|
+
fn whoami_passes_on_the_executors_refusal() {
|
|
57
|
+
let services: Services = [(
|
|
58
|
+
"root.run",
|
|
59
|
+
Box::new(|_: &Value| Err(Error::new("EXECUTOR_PAUSED", "The executor is paused.")))
|
|
60
|
+
as _,
|
|
61
|
+
)]
|
|
62
|
+
.into();
|
|
63
|
+
assert_eq!(
|
|
64
|
+
call(build(cfg()), "whoami.run", json!({}), "admin", &services).code(),
|
|
65
|
+
"EXECUTOR_PAUSED"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#[test]
|
|
70
|
+
fn whoami_takes_no_params() {
|
|
71
|
+
assert_eq!(
|
|
72
|
+
call(
|
|
73
|
+
build(cfg()),
|
|
74
|
+
"whoami.run",
|
|
75
|
+
json!({"name": "other"}),
|
|
76
|
+
"admin",
|
|
77
|
+
&Services::new()
|
|
78
|
+
)
|
|
79
|
+
.code(),
|
|
80
|
+
"bad_request"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Native Rust with Vue
|
|
2
2
|
|
|
3
|
-
Edit `frontend/src/App.vue`
|
|
4
|
-
|
|
5
|
-
files, portal, jobs and
|
|
6
|
-
|
|
7
|
-
`syno-pkg-land-
|
|
3
|
+
The Vue shell is the same as the Go template's. Edit `frontend/src/App.vue`
|
|
4
|
+
and `main.ts` to build a DSM-window app with the public `@synopackageland/ui`
|
|
5
|
+
components and embedded theme. The shell, sign-in, files, portal, jobs and
|
|
6
|
+
delegate components come from `@synopackageland/sdk/vue` (types from
|
|
7
|
+
`@synopackageland/sdk`); its version must equal the `syno-pkg-land-app` tag in backend/Cargo.toml. See
|
|
8
8
|
<https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
@@ -15,41 +15,42 @@ npm run build
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
The base app reads package status and edits the package greeting through
|
|
18
|
-
`ctx.call`.
|
|
19
|
-
|
|
18
|
+
`ctx.call`. Errors show the app's own `frontend/src/i18n` text, or the SDK's
|
|
19
|
+
`bridgeErrorCopy` for the codes the bridge runtime adds (`app_unavailable`
|
|
20
|
+
and the services' refusals). Each window has its own Vue mount, request state
|
|
21
|
+
and host subscriptions; unmount disposes subscriptions and ignores late responses.
|
|
20
22
|
|
|
21
|
-
`--with root-ops`
|
|
22
|
-
|
|
23
|
-
and
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
the SDK's `PortalSettings`: the DSM Application Portal link, the alias form
|
|
27
|
-
for administrators, and the Control Panel shortcut.
|
|
28
|
-
|
|
29
|
-
Use public library controls, the embedded theme, per-app modal scope and unique
|
|
30
|
-
Vue ID prefixes. Backend route permissions and DSM checks remain authoritative.
|
|
31
|
-
Generated build and unit tests do not establish NAS runtime acceptance.
|
|
32
|
-
|
|
33
|
-
The manifest is dsm-package.yaml schema 2: backend.kind: rust 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.
|
|
23
|
+
The `--with jobs`, `root-ops` and `delegate` examples add their methods to
|
|
24
|
+
the Rust App; the shell's panels call them with `ctx.call`. The SDK's
|
|
25
|
+
background-jobs page and executor settings call the runtime's own browser
|
|
26
|
+
methods (`bg.*`, `executor.*`); check the SDK release notes for the ones a
|
|
27
|
+
bridge App's runtime serves.
|
|
34
28
|
|
|
35
29
|
Use the installed `@synopackageland/ui`; package.json pins its version.
|
|
36
|
-
`
|
|
37
|
-
`
|
|
38
|
-
|
|
39
|
-
`wide-layout="
|
|
40
|
-
|
|
30
|
+
`App.vue` lays the shell out with the SDK's `AppShell` (`@synopackageland/sdk/vue`):
|
|
31
|
+
`pages` from `shellPages`, the header buttons in `#actions` and the pages in its
|
|
32
|
+
default slot. Given `ctx`, it follows the launch. In a DSM desktop window it
|
|
33
|
+
keeps `wide-layout="window"`, even maximized; the DSM host sets the 700px
|
|
34
|
+
minimum window width. A Portal or standalone launch owns the whole page
|
|
35
|
+
(`ctx.fullPage`, from the generated window): `AppShell` takes
|
|
36
|
+
`wide-layout="web"` (the SaaS layout at 1200px or wider, mobile on a phone) and
|
|
37
|
+
a device-width viewport, undone at unmount, since DSM's page has no
|
|
38
|
+
device-width viewport. Its full-page sidebar names the App with
|
|
39
|
+
`ctx.displayName` and `ctx.appIcon` (without them the page title and icon) and
|
|
40
|
+
shows the signed-in DSM account and its role from `ctx.user`. See the ui's
|
|
41
|
+
`docs/adaptive-layout.md`.
|
|
42
|
+
`AppShell`'s `app-root` wraps it in an app-local `AppRoot` (app modal scope, on
|
|
43
|
+
a full page as well, bounded at `height: 100%`) with the UI locale from
|
|
44
|
+
`locale`; do not add another `AppRoot`.
|
|
45
|
+
In a DSM window (not a full page) that `AppRoot` takes
|
|
41
46
|
`dialog-bounds="viewport"`, so Dialogs can be dragged out of the window, and
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
pressing one calls `ctx.host.activateWindow()` to bring the window to the
|
|
48
|
+
front. Elsewhere Dialogs stay inside the App. A confirmed sign-in need sets
|
|
49
|
+
`denied`: the `#denied` slot's `LoginPage` takes the place of the App's pages.
|
|
44
50
|
Import `themes/dsm7-embedded.css` and assign each mount a unique
|
|
45
51
|
`app.config.idPrefix` before mounting.
|
|
46
52
|
|
|
47
53
|
Start with AGENTS.md, `synopkgland skill list` and `synopkgland skill path`.
|
|
48
|
-
Read `synopkgland skill show ui-vue` for direct Vue component work
|
|
49
|
-
|
|
50
|
-
validate/render workflow. The existing host/backend lifecycle remains in place.
|
|
51
|
-
|
|
52
|
-
For UI discovery, run `npx --no syno-pkg-land-ui catalog --json` and
|
|
54
|
+
Read `synopkgland skill show ui-vue` for direct Vue component work. For UI
|
|
55
|
+
discovery, run `npx --no syno-pkg-land-ui catalog --json` and
|
|
53
56
|
`npx --no syno-pkg-land-ui describe Button --json` from the installed frontend.
|
|
54
|
-
Read its prompt and exported Vue docs/types before choosing component APIs.
|
|
55
|
-
Use `syno-pkg-land-ui` directly in npm scripts; keep AI guidance version-independent.
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
Native Rust package build dependencies (crates linked into the backend binaries)
|
|
2
|
-
|
|
3
|
-
syno-pkg-land-sdk v0.9.9 (MIT)
|
|
4
|
-
|
|
5
|
-
MIT License
|
|
6
|
-
|
|
7
|
-
Copyright (c) 2026 synopkgland
|
|
8
|
-
|
|
9
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
-
in the Software without restriction, including without limitation the rights
|
|
12
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
-
furnished to do so, subject to the following conditions:
|
|
15
|
-
|
|
16
|
-
The above copyright notice and this permission notice shall be included in all
|
|
17
|
-
copies or substantial portions of the Software.
|
|
18
|
-
|
|
19
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
-
SOFTWARE.
|
|
26
|
-
|
|
27
|
-
itoa v1.0.18 (MIT OR Apache-2.0; used under MIT)
|
|
28
|
-
|
|
29
|
-
Permission is hereby granted, free of charge, to any
|
|
30
|
-
person obtaining a copy of this software and associated
|
|
31
|
-
documentation files (the "Software"), to deal in the
|
|
32
|
-
Software without restriction, including without
|
|
33
|
-
limitation the rights to use, copy, modify, merge,
|
|
34
|
-
publish, distribute, sublicense, and/or sell copies of
|
|
35
|
-
the Software, and to permit persons to whom the Software
|
|
36
|
-
is furnished to do so, subject to the following
|
|
37
|
-
conditions:
|
|
38
|
-
|
|
39
|
-
The above copyright notice and this permission notice
|
|
40
|
-
shall be included in all copies or substantial portions
|
|
41
|
-
of the Software.
|
|
42
|
-
|
|
43
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
44
|
-
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
45
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
46
|
-
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
47
|
-
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
48
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
49
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
50
|
-
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
51
|
-
DEALINGS IN THE SOFTWARE.
|
|
52
|
-
|
|
53
|
-
libc v0.2.190 (MIT OR Apache-2.0; used under MIT)
|
|
54
|
-
|
|
55
|
-
Copyright (c) The Rust Project Developers
|
|
56
|
-
|
|
57
|
-
Permission is hereby granted, free of charge, to any
|
|
58
|
-
person obtaining a copy of this software and associated
|
|
59
|
-
documentation files (the "Software"), to deal in the
|
|
60
|
-
Software without restriction, including without
|
|
61
|
-
limitation the rights to use, copy, modify, merge,
|
|
62
|
-
publish, distribute, sublicense, and/or sell copies of
|
|
63
|
-
the Software, and to permit persons to whom the Software
|
|
64
|
-
is furnished to do so, subject to the following
|
|
65
|
-
conditions:
|
|
66
|
-
|
|
67
|
-
The above copyright notice and this permission notice
|
|
68
|
-
shall be included in all copies or substantial portions
|
|
69
|
-
of the Software.
|
|
70
|
-
|
|
71
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
72
|
-
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
73
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
74
|
-
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
75
|
-
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
76
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
77
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
78
|
-
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
79
|
-
DEALINGS IN THE SOFTWARE.
|
|
80
|
-
|
|
81
|
-
memchr v2.8.3 (Unlicense OR MIT; used under MIT)
|
|
82
|
-
|
|
83
|
-
The MIT License (MIT)
|
|
84
|
-
|
|
85
|
-
Copyright (c) 2015 Andrew Gallant
|
|
86
|
-
|
|
87
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
88
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
89
|
-
in the Software without restriction, including without limitation the rights
|
|
90
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
91
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
92
|
-
furnished to do so, subject to the following conditions:
|
|
93
|
-
|
|
94
|
-
The above copyright notice and this permission notice shall be included in
|
|
95
|
-
all copies or substantial portions of the Software.
|
|
96
|
-
|
|
97
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
98
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
99
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
100
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
101
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
102
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
103
|
-
THE SOFTWARE.
|
|
104
|
-
|
|
105
|
-
serde v1.0.229 (MIT OR Apache-2.0; used under MIT)
|
|
106
|
-
|
|
107
|
-
Permission is hereby granted, free of charge, to any
|
|
108
|
-
person obtaining a copy of this software and associated
|
|
109
|
-
documentation files (the "Software"), to deal in the
|
|
110
|
-
Software without restriction, including without
|
|
111
|
-
limitation the rights to use, copy, modify, merge,
|
|
112
|
-
publish, distribute, sublicense, and/or sell copies of
|
|
113
|
-
the Software, and to permit persons to whom the Software
|
|
114
|
-
is furnished to do so, subject to the following
|
|
115
|
-
conditions:
|
|
116
|
-
|
|
117
|
-
The above copyright notice and this permission notice
|
|
118
|
-
shall be included in all copies or substantial portions
|
|
119
|
-
of the Software.
|
|
120
|
-
|
|
121
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
122
|
-
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
123
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
124
|
-
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
125
|
-
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
126
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
127
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
128
|
-
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
129
|
-
DEALINGS IN THE SOFTWARE.
|
|
130
|
-
|
|
131
|
-
serde_core v1.0.229 (MIT OR Apache-2.0; used under MIT)
|
|
132
|
-
|
|
133
|
-
Permission is hereby granted, free of charge, to any
|
|
134
|
-
person obtaining a copy of this software and associated
|
|
135
|
-
documentation files (the "Software"), to deal in the
|
|
136
|
-
Software without restriction, including without
|
|
137
|
-
limitation the rights to use, copy, modify, merge,
|
|
138
|
-
publish, distribute, sublicense, and/or sell copies of
|
|
139
|
-
the Software, and to permit persons to whom the Software
|
|
140
|
-
is furnished to do so, subject to the following
|
|
141
|
-
conditions:
|
|
142
|
-
|
|
143
|
-
The above copyright notice and this permission notice
|
|
144
|
-
shall be included in all copies or substantial portions
|
|
145
|
-
of the Software.
|
|
146
|
-
|
|
147
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
148
|
-
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
149
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
150
|
-
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
151
|
-
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
152
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
153
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
154
|
-
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
155
|
-
DEALINGS IN THE SOFTWARE.
|
|
156
|
-
|
|
157
|
-
serde_json v1.0.154 (MIT OR Apache-2.0; used under MIT)
|
|
158
|
-
|
|
159
|
-
Permission is hereby granted, free of charge, to any
|
|
160
|
-
person obtaining a copy of this software and associated
|
|
161
|
-
documentation files (the "Software"), to deal in the
|
|
162
|
-
Software without restriction, including without
|
|
163
|
-
limitation the rights to use, copy, modify, merge,
|
|
164
|
-
publish, distribute, sublicense, and/or sell copies of
|
|
165
|
-
the Software, and to permit persons to whom the Software
|
|
166
|
-
is furnished to do so, subject to the following
|
|
167
|
-
conditions:
|
|
168
|
-
|
|
169
|
-
The above copyright notice and this permission notice
|
|
170
|
-
shall be included in all copies or substantial portions
|
|
171
|
-
of the Software.
|
|
172
|
-
|
|
173
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
174
|
-
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
175
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
176
|
-
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
177
|
-
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
178
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
179
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
180
|
-
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
181
|
-
DEALINGS IN THE SOFTWARE.
|
|
182
|
-
|
|
183
|
-
zmij v1.0.23 (MIT)
|
|
184
|
-
|
|
185
|
-
Permission is hereby granted, free of charge, to any
|
|
186
|
-
person obtaining a copy of this software and associated
|
|
187
|
-
documentation files (the "Software"), to deal in the
|
|
188
|
-
Software without restriction, including without
|
|
189
|
-
limitation the rights to use, copy, modify, merge,
|
|
190
|
-
publish, distribute, sublicense, and/or sell copies of
|
|
191
|
-
the Software, and to permit persons to whom the Software
|
|
192
|
-
is furnished to do so, subject to the following
|
|
193
|
-
conditions:
|
|
194
|
-
|
|
195
|
-
The above copyright notice and this permission notice
|
|
196
|
-
shall be included in all copies or substantial portions
|
|
197
|
-
of the Software.
|
|
198
|
-
|
|
199
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
200
|
-
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
201
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
202
|
-
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
203
|
-
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
204
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
205
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
206
|
-
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
207
|
-
DEALINGS IN THE SOFTWARE.
|