@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
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
use std::sync::Arc;
|
|
2
|
-
|
|
3
|
-
use syno_pkg_land_sdk::api::Route;
|
|
4
|
-
|
|
5
|
-
use super::Config;
|
|
6
|
-
#[cfg(test)]
|
|
7
|
-
use super::test_support::Sample;
|
|
8
|
-
|
|
9
|
-
impl Config {
|
|
10
|
-
/// Empty: this package did not opt in to background jobs.
|
|
11
|
-
/// `synopkgland init --with jobs` overlays this file with one that serves
|
|
12
|
-
/// [`super::BackgroundApi`] for the package-user worker unit.
|
|
13
|
-
pub(super) fn background_routes(self: &Arc<Self>) -> Vec<Route> {
|
|
14
|
-
Vec::new()
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/// The background routes' samples: none.
|
|
19
|
-
#[cfg(test)]
|
|
20
|
-
pub(super) fn samples() -> Vec<Sample> {
|
|
21
|
-
Vec::new()
|
|
22
|
-
}
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
use std::collections::BTreeSet;
|
|
2
|
-
use std::fmt;
|
|
3
|
-
|
|
4
|
-
use super::Config;
|
|
5
|
-
|
|
6
|
-
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
7
|
-
pub struct DelegateSettings {
|
|
8
|
-
pub scope: String,
|
|
9
|
-
pub operations: Vec<String>,
|
|
10
|
-
pub destinations: Vec<String>,
|
|
11
|
-
pub lifetime: String,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
#[derive(Debug, PartialEq, Eq)]
|
|
15
|
-
pub struct InvalidDelegateConfig;
|
|
16
|
-
|
|
17
|
-
impl fmt::Display for InvalidDelegateConfig {
|
|
18
|
-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
19
|
-
f.write_str("invalid delegate build configuration")
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
impl std::error::Error for InvalidDelegateConfig {}
|
|
24
|
-
|
|
25
|
-
/// The file operations a time-limited delegation may grant.
|
|
26
|
-
const TIME_LIMITED_OPERATIONS: [&str; 10] = [
|
|
27
|
-
"files.list",
|
|
28
|
-
"files.info",
|
|
29
|
-
"files.createFolder",
|
|
30
|
-
"files.checkPermission",
|
|
31
|
-
"files.write",
|
|
32
|
-
"files.read",
|
|
33
|
-
"files.rename",
|
|
34
|
-
"files.copy",
|
|
35
|
-
"files.move",
|
|
36
|
-
"files.delete",
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
const PERMANENT_OPERATION: &str = "files.moveFromStaging";
|
|
40
|
-
const PERMANENT_LIFETIME: &str = "until-revoked";
|
|
41
|
-
const MAX_DESTINATIONS: usize = 64;
|
|
42
|
-
|
|
43
|
-
/// A JSON array of strings; empty text and `null` are an empty list.
|
|
44
|
-
fn string_list(json: &str) -> Result<Vec<String>, InvalidDelegateConfig> {
|
|
45
|
-
if json.is_empty() {
|
|
46
|
-
return Ok(Vec::new());
|
|
47
|
-
}
|
|
48
|
-
let list: Option<Vec<String>> =
|
|
49
|
-
serde_json::from_str(json).map_err(|_| InvalidDelegateConfig)?;
|
|
50
|
-
Ok(list.unwrap_or_default())
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/// Go's `^[a-z][a-z0-9_-]{0,63}$`.
|
|
54
|
-
fn destination_key(key: &str) -> bool {
|
|
55
|
-
(1..=64).contains(&key.len())
|
|
56
|
-
&& key.starts_with(|c: char| c.is_ascii_lowercase())
|
|
57
|
-
&& key
|
|
58
|
-
.bytes()
|
|
59
|
-
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == b'_' || c == b'-')
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fn all_unique(list: &[String]) -> bool {
|
|
63
|
-
list.iter().collect::<BTreeSet<_>>().len() == list.len()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/// Consumes only build/startup settings. Legacy declarations retain their
|
|
67
|
-
/// TTL/job branch; absent extension fields never imply permanence.
|
|
68
|
-
pub fn parse_delegate_config(cfg: &Config) -> Result<DelegateSettings, InvalidDelegateConfig> {
|
|
69
|
-
let out = DelegateSettings {
|
|
70
|
-
scope: cfg.delegate_scope.clone(),
|
|
71
|
-
operations: string_list(&cfg.delegate_operations_json)?,
|
|
72
|
-
destinations: string_list(&cfg.delegate_destinations_json)?,
|
|
73
|
-
lifetime: cfg.delegate_lifetime.clone(),
|
|
74
|
-
};
|
|
75
|
-
let valid = if out.lifetime.is_empty() {
|
|
76
|
-
out.destinations.is_empty()
|
|
77
|
-
&& out
|
|
78
|
-
.operations
|
|
79
|
-
.iter()
|
|
80
|
-
.all(|op| TIME_LIMITED_OPERATIONS.contains(&op.as_str()))
|
|
81
|
-
&& all_unique(&out.operations)
|
|
82
|
-
} else {
|
|
83
|
-
out.lifetime == PERMANENT_LIFETIME
|
|
84
|
-
&& !out.scope.is_empty()
|
|
85
|
-
&& out.operations == [PERMANENT_OPERATION]
|
|
86
|
-
&& (1..=MAX_DESTINATIONS).contains(&out.destinations.len())
|
|
87
|
-
&& out.destinations.iter().all(|key| destination_key(key))
|
|
88
|
-
&& all_unique(&out.destinations)
|
|
89
|
-
};
|
|
90
|
-
if valid {
|
|
91
|
-
Ok(out)
|
|
92
|
-
} else {
|
|
93
|
-
Err(InvalidDelegateConfig)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
#[cfg(test)]
|
|
98
|
-
mod tests {
|
|
99
|
-
use super::*;
|
|
100
|
-
|
|
101
|
-
fn permanent() -> Config {
|
|
102
|
-
Config {
|
|
103
|
-
delegate_scope: "home".into(),
|
|
104
|
-
delegate_operations_json: r#"["files.moveFromStaging"]"#.into(),
|
|
105
|
-
delegate_destinations_json: r#"["save"]"#.into(),
|
|
106
|
-
delegate_lifetime: "until-revoked".into(),
|
|
107
|
-
..Config::default()
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
#[test]
|
|
112
|
-
fn delegate_build_config_strictly_separates_legacy_and_permanent() {
|
|
113
|
-
let settings = parse_delegate_config(&permanent()).unwrap();
|
|
114
|
-
assert_eq!(settings.lifetime, "until-revoked");
|
|
115
|
-
assert_eq!(settings.destinations, ["save"]);
|
|
116
|
-
let legacy = [
|
|
117
|
-
Config::default(),
|
|
118
|
-
Config {
|
|
119
|
-
delegate_scope: "home".into(),
|
|
120
|
-
..Config::default()
|
|
121
|
-
},
|
|
122
|
-
Config {
|
|
123
|
-
delegate_scope: "home".into(),
|
|
124
|
-
delegate_operations_json: r#"["files.write"]"#.into(),
|
|
125
|
-
..Config::default()
|
|
126
|
-
},
|
|
127
|
-
];
|
|
128
|
-
for cfg in legacy {
|
|
129
|
-
let settings = parse_delegate_config(&cfg).unwrap();
|
|
130
|
-
assert!(
|
|
131
|
-
settings.lifetime.is_empty() && settings.destinations.is_empty(),
|
|
132
|
-
"legacy changed"
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
let mutations: [fn(&mut Config); 8] = [
|
|
136
|
-
|c| c.delegate_lifetime = String::new(),
|
|
137
|
-
|c| c.delegate_scope = String::new(),
|
|
138
|
-
|c| c.delegate_operations_json = r#"["files.write"]"#.into(),
|
|
139
|
-
|c| c.delegate_destinations_json = r#"["save","save"]"#.into(),
|
|
140
|
-
|c| c.delegate_destinations_json = r#"["Save"]"#.into(),
|
|
141
|
-
|c| c.delegate_destinations_json = "[]".into(),
|
|
142
|
-
|c| c.delegate_destinations_json = "null".into(),
|
|
143
|
-
|c| c.delegate_destinations_json = r#"["save"] {}"#.into(),
|
|
144
|
-
];
|
|
145
|
-
for mutate in mutations {
|
|
146
|
-
let mut cfg = permanent();
|
|
147
|
-
mutate(&mut cfg);
|
|
148
|
-
assert_eq!(
|
|
149
|
-
parse_delegate_config(&cfg),
|
|
150
|
-
Err(InvalidDelegateConfig),
|
|
151
|
-
"invalid trusted config accepted: {}",
|
|
152
|
-
cfg.delegate_destinations_json
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
#[test]
|
|
158
|
-
fn delegate_build_config_accepts_every_time_limited_file_operation() {
|
|
159
|
-
let ops = r#"["files.checkPermission","files.copy","files.createFolder","files.delete","files.info","files.list","files.move","files.read","files.rename","files.write"]"#;
|
|
160
|
-
let settings = parse_delegate_config(&Config {
|
|
161
|
-
delegate_scope: "shared:media".into(),
|
|
162
|
-
delegate_operations_json: ops.into(),
|
|
163
|
-
delegate_destinations_json: "[]".into(),
|
|
164
|
-
..Config::default()
|
|
165
|
-
})
|
|
166
|
-
.unwrap();
|
|
167
|
-
assert_eq!(settings.operations.len(), 10);
|
|
168
|
-
assert!(settings.lifetime.is_empty());
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
#[test]
|
|
172
|
-
fn destination_keys_follow_the_documented_pattern() {
|
|
173
|
-
for ok in ["a", "save", "a-b_c9", &format!("a{}", "b".repeat(63))] {
|
|
174
|
-
assert!(destination_key(ok), "{ok}");
|
|
175
|
-
}
|
|
176
|
-
for bad in [
|
|
177
|
-
"",
|
|
178
|
-
"1a",
|
|
179
|
-
"-a",
|
|
180
|
-
"A",
|
|
181
|
-
"a b",
|
|
182
|
-
"é",
|
|
183
|
-
&format!("a{}", "b".repeat(64)),
|
|
184
|
-
] {
|
|
185
|
-
assert!(!destination_key(bad), "{bad}");
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
use std::sync::Arc;
|
|
2
|
-
|
|
3
|
-
use syno_pkg_land_sdk::api::Route;
|
|
4
|
-
|
|
5
|
-
use super::Config;
|
|
6
|
-
#[cfg(test)]
|
|
7
|
-
use super::test_support::Sample;
|
|
8
|
-
|
|
9
|
-
impl Config {
|
|
10
|
-
/// Empty: this package did not opt in to delegated operations.
|
|
11
|
-
/// `synopkgland init --with delegate` overlays this file with one that
|
|
12
|
-
/// serves the SDK's delegate routes.
|
|
13
|
-
pub(super) fn delegate_routes(self: &Arc<Self>) -> Vec<Route> {
|
|
14
|
-
Vec::new()
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/// The delegate routes' samples: none.
|
|
19
|
-
#[cfg(test)]
|
|
20
|
-
pub(super) fn samples() -> Vec<Sample> {
|
|
21
|
-
Vec::new()
|
|
22
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
use syno_pkg_land_sdk::BoxError;
|
|
2
|
-
use syno_pkg_land_sdk::identity::{Config as IdentityConfig, Service};
|
|
3
|
-
|
|
4
|
-
use super::Config;
|
|
5
|
-
|
|
6
|
-
/// Builds the SDK's standalone identity service, which registers
|
|
7
|
-
/// identity.status/session/login/logout through `Service::wrap`. Business
|
|
8
|
-
/// routes keep their permissions.json policies. Call only for
|
|
9
|
-
/// capabilities.identity=standalone; the path comes from the CGI environment.
|
|
10
|
-
pub fn standalone_identity(cfg: &Config, script_name: &str) -> Result<Service, BoxError> {
|
|
11
|
-
if !absolute_cgi_path(script_name) {
|
|
12
|
-
return Err("identity requires an absolute CGI script path".into());
|
|
13
|
-
}
|
|
14
|
-
// script_name is .../cgi/api.cgi; the cookie belongs to the folder above cgi.
|
|
15
|
-
let app_dir = &script_name[..script_name.len() - "/cgi/api.cgi".len()];
|
|
16
|
-
Service::new(IdentityConfig {
|
|
17
|
-
data_dir: cfg.data_dir.to_string_lossy().into_owned(),
|
|
18
|
-
cookie_path: format!("{app_dir}/"),
|
|
19
|
-
..IdentityConfig::default()
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/// Whether path is clean, absolute and ends in /cgi/api.cgi.
|
|
24
|
-
fn absolute_cgi_path(path: &str) -> bool {
|
|
25
|
-
path.starts_with('/')
|
|
26
|
-
&& !path.starts_with("//")
|
|
27
|
-
&& path.ends_with("/cgi/api.cgi")
|
|
28
|
-
&& path
|
|
29
|
-
.split('/')
|
|
30
|
-
.skip(1)
|
|
31
|
-
.all(|s| !matches!(s, "" | "." | ".."))
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
#[cfg(test)]
|
|
35
|
-
mod tests {
|
|
36
|
-
use super::super::test_support::Fixture;
|
|
37
|
-
use super::*;
|
|
38
|
-
|
|
39
|
-
#[test]
|
|
40
|
-
fn identity_application_path() {
|
|
41
|
-
let fixture = Fixture::new();
|
|
42
|
-
for (script, valid) in [
|
|
43
|
-
("/webman/3rdparty/test/cgi/api.cgi", true),
|
|
44
|
-
("/cgi/api.cgi", true),
|
|
45
|
-
("", false),
|
|
46
|
-
("cgi/api.cgi", false),
|
|
47
|
-
("//evil/cgi/api.cgi", false),
|
|
48
|
-
("/a/../cgi/api.cgi", false),
|
|
49
|
-
("/a//cgi/api.cgi", false),
|
|
50
|
-
("/a/cgi/api.cgi/", false),
|
|
51
|
-
] {
|
|
52
|
-
let got = standalone_identity(&fixture.cfg, script);
|
|
53
|
-
assert_eq!(got.is_ok(), valid, "script {script:?}");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
use syno_pkg_land_sdk::BoxError;
|
|
2
|
-
use syno_pkg_land_sdk::datamigrate::{self, Info, Migration};
|
|
3
|
-
|
|
4
|
-
use super::Config;
|
|
5
|
-
|
|
6
|
-
/// This app's schema migrations. The template ships one empty example;
|
|
7
|
-
/// replace it with your own steps. Versions start at 1 and never change
|
|
8
|
-
/// meaning. Every step must be idempotent: a crash between the step and the
|
|
9
|
-
/// recorded version, or two CGI requests racing on the first run after an
|
|
10
|
-
/// upgrade, can run one step twice. A failed step stops the run with the
|
|
11
|
-
/// previous version still recorded, and the next run resumes from there; the
|
|
12
|
-
/// utility itself never deletes or rewrites data.
|
|
13
|
-
pub fn migrations() -> Vec<Migration> {
|
|
14
|
-
vec![Migration::new(1, "example", |_ctx| {
|
|
15
|
-
// Nothing to do yet. Read ctx.data_dir, rewrite files with a
|
|
16
|
-
// temporary file + rename, and keep the step idempotent.
|
|
17
|
-
Ok(())
|
|
18
|
-
})]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/// Applies [`migrations`] to the package's persistent var directory. The CGI
|
|
22
|
-
/// calls it once per request; the recorded data-version makes that a cheap
|
|
23
|
-
/// no-op. SYNOPKG_OLD_PKGVER is set when DSM starts the app from a lifecycle
|
|
24
|
-
/// script; in a plain CGI request the version of the last successful run is
|
|
25
|
-
/// used instead.
|
|
26
|
-
pub fn migrate(cfg: &Config) -> Result<(), BoxError> {
|
|
27
|
-
migrate_from(
|
|
28
|
-
cfg,
|
|
29
|
-
&std::env::var("SYNOPKG_OLD_PKGVER").unwrap_or_default(),
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
fn migrate_from(cfg: &Config, old_pkg_ver: &str) -> Result<(), BoxError> {
|
|
34
|
-
let info = Info {
|
|
35
|
-
old_pkg_ver: old_pkg_ver.to_string(),
|
|
36
|
-
new_pkg_ver: cfg.version.clone(),
|
|
37
|
-
log: None,
|
|
38
|
-
};
|
|
39
|
-
datamigrate::run(&cfg.data_dir, &info, &migrations())?;
|
|
40
|
-
Ok(())
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
#[cfg(test)]
|
|
44
|
-
mod tests {
|
|
45
|
-
use super::super::test_support::Fixture;
|
|
46
|
-
use super::*;
|
|
47
|
-
|
|
48
|
-
#[test]
|
|
49
|
-
fn migrations_are_ordered_unique_and_valid() {
|
|
50
|
-
let migrations = migrations();
|
|
51
|
-
assert!(
|
|
52
|
-
!migrations.is_empty(),
|
|
53
|
-
"the template must ship one example migration"
|
|
54
|
-
);
|
|
55
|
-
for (i, migration) in migrations.iter().enumerate() {
|
|
56
|
-
assert!(
|
|
57
|
-
migration.version >= 1 && !migration.name.is_empty(),
|
|
58
|
-
"migration {i} is invalid"
|
|
59
|
-
);
|
|
60
|
-
if i > 0 {
|
|
61
|
-
assert!(
|
|
62
|
-
migrations[i - 1].version < migration.version,
|
|
63
|
-
"migrations are not strictly version-ascending"
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
#[test]
|
|
70
|
-
fn migrate_records_the_schema_and_package_version() {
|
|
71
|
-
let mut fixture = Fixture::new();
|
|
72
|
-
fixture.cfg.version = "0.2.0-0002".into();
|
|
73
|
-
migrate_from(&fixture.cfg, "0.1.0-0001").unwrap();
|
|
74
|
-
let state = datamigrate::load(&fixture.cfg.data_dir).unwrap();
|
|
75
|
-
assert_eq!(state.schema, 1);
|
|
76
|
-
assert_eq!(state.package_version, "0.2.0-0002");
|
|
77
|
-
migrate_from(&fixture.cfg, "0.1.0-0001").expect("a second migrate must be a no-op");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
//! This package's business operations. Add a route in `routes.rs` and register
|
|
2
|
-
//! its handler there. App permissions live in `permissions.json`; SDK
|
|
3
|
-
//! capability routes use the immutable SDK policy. Unlisted methods require
|
|
4
|
-
//! membership in DSM's administrators group.
|
|
5
|
-
|
|
6
|
-
mod background;
|
|
7
|
-
mod background_routes;
|
|
8
|
-
mod delegate_config;
|
|
9
|
-
mod delegate_routes;
|
|
10
|
-
mod identity;
|
|
11
|
-
mod migrations;
|
|
12
|
-
mod portal;
|
|
13
|
-
mod root_routes;
|
|
14
|
-
mod routes;
|
|
15
|
-
mod session_files;
|
|
16
|
-
mod shared_folder;
|
|
17
|
-
#[cfg(test)]
|
|
18
|
-
mod test_support;
|
|
19
|
-
mod uninstall;
|
|
20
|
-
|
|
21
|
-
pub use background::{BackgroundApi, BackgroundFiles, hash_work, system_uid_lookup};
|
|
22
|
-
pub use delegate_config::{DelegateSettings, InvalidDelegateConfig, parse_delegate_config};
|
|
23
|
-
pub use identity::standalone_identity;
|
|
24
|
-
pub use migrations::{migrate, migrations};
|
|
25
|
-
pub use routes::{Config, UidLookup, routes};
|
|
26
|
-
pub use session_files::{FILE_STATION_SESSION, session_files_enabled};
|
|
27
|
-
pub use shared_folder::{
|
|
28
|
-
SharedFolder, SharedFolderError, read_shared_folders, resolve_shared_folder,
|
|
29
|
-
};
|
|
30
|
-
pub use uninstall::uninstall;
|