@synopackageland/templates 0.9.7 → 0.9.9
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 +1 -1
- package/package.json +1 -1
- package/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +1 -1
- package/templates/native-go/backend/go.mod +1 -1
- package/templates/native-go/backend/go.sum +2 -2
- package/templates/native-go/backend/internal/app/background_test.go +1 -0
- package/templates/native-go/backend/internal/app/routes_test.go +2 -0
- package/templates/native-go/i18n/glossary.json +3 -1
- package/templates/native-go/i18n/messages/de.json +3 -0
- package/templates/native-go/i18n/messages/en.json +3 -0
- package/templates/native-go/i18n/messages/es.json +3 -0
- package/templates/native-go/i18n/messages/fr.json +3 -0
- package/templates/native-go/i18n/messages/it.json +3 -0
- package/templates/native-go/i18n/messages/ja.json +3 -0
- package/templates/native-go/i18n/messages/ko.json +3 -0
- package/templates/native-go/i18n/messages/zh-CN.json +3 -0
- package/templates/native-go/i18n/messages/zh-TW.json +3 -0
- package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +8 -6
- package/templates/native-go-delegate/frontend/test/capability-panels.test.ts +28 -2
- package/templates/native-go-jobs/backend/internal/app/background_routes_test.go +1 -0
- package/templates/native-go-jobs/frontend/test/background-page.test.ts +18 -0
- package/templates/native-go-vue/frontend/package-lock.json +12 -12
- package/templates/native-go-vue/frontend/package.json +2 -2
- package/templates/native-go-vue/frontend/src/App.vue +14 -7
- package/templates/native-go-vue/frontend/src/CapabilityPanels.vue +4 -1
- package/templates/native-go-vue/frontend/src/i18n/de.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/en.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/es.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/fr.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/it.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/ja.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/ko.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +0 -1
- package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +0 -1
- package/templates/native-go-vue/frontend/test/apps.test.ts +37 -0
- package/templates/native-go-vue/frontend/test/native-controls.test.ts +4 -4
- package/templates/native-rust/backend/Cargo.lock +2 -2
- package/templates/native-rust/backend/Cargo.toml +1 -1
- package/templates/native-rust/backend/THIRD_PARTY_NOTICES.txt +1 -1
- package/templates/native-rust/backend/src/app/test_support.rs +2 -0
- package/templates/native-rust/i18n/glossary.json +3 -1
- package/templates/native-rust/i18n/messages/de.json +3 -0
- package/templates/native-rust/i18n/messages/en.json +3 -0
- package/templates/native-rust/i18n/messages/es.json +3 -0
- package/templates/native-rust/i18n/messages/fr.json +3 -0
- package/templates/native-rust/i18n/messages/it.json +3 -0
- package/templates/native-rust/i18n/messages/ja.json +3 -0
- package/templates/native-rust/i18n/messages/ko.json +3 -0
- package/templates/native-rust/i18n/messages/zh-CN.json +3 -0
- package/templates/native-rust/i18n/messages/zh-TW.json +3 -0
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ Vue shell from `native-go-vue` (then `native-rust-vue`), `frontend/` of
|
|
|
36
36
|
`native-rust-root-ops`'s `root_routes.rs`, the executor shell Go borrows as
|
|
37
37
|
`executor.go`. `scripts/assemble.sh --language rust` does exactly this.
|
|
38
38
|
|
|
39
|
-
`backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.9.
|
|
39
|
+
`backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.9.9`; that line is
|
|
40
40
|
the single source of the SDK version and `frontend/package.json` pins
|
|
41
41
|
`@synopackageland/sdk` to the same version. A Rust project takes it from the
|
|
42
42
|
`tag` of the `syno-pkg-land-sdk` git dependency in `backend/Cargo.toml`, which
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/templates",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
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.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {"type": "git", "url": "git+https://github.com/synopackageland/syno-pkg-land-templates.git"},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
github.com/synopackageland/syno-pkg-land-sdk v0.9.
|
|
2
|
-
github.com/synopackageland/syno-pkg-land-sdk v0.9.
|
|
1
|
+
github.com/synopackageland/syno-pkg-land-sdk v0.9.9 h1:TUsblZtIwlBO0DpBTUOj6gd8+xTi60Y0CwkKNb+OR3M=
|
|
2
|
+
github.com/synopackageland/syno-pkg-land-sdk v0.9.9/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=
|
|
@@ -63,6 +63,7 @@ func (e *bgEnv) call(auth api.Authenticator, method, params string) (string, map
|
|
|
63
63
|
}
|
|
64
64
|
req := httptest.NewRequest(http.MethodPost, "/api.cgi", strings.NewReader(`{"method":"`+method+`","params":`+params+`}`))
|
|
65
65
|
req.Header.Set("Content-Type", "application/json")
|
|
66
|
+
req.Header.Set("Sec-Fetch-Site", "same-origin")
|
|
66
67
|
rec := httptest.NewRecorder()
|
|
67
68
|
srv.ServeHTTP(rec, req)
|
|
68
69
|
var out map[string]any
|
|
@@ -67,6 +67,8 @@ func apiRequest(method, params string) *http.Request {
|
|
|
67
67
|
body := `{"method":"` + method + `","params":` + params + `}`
|
|
68
68
|
req := httptest.NewRequest(http.MethodPost, "/api.cgi", strings.NewReader(body))
|
|
69
69
|
req.Header.Set("Content-Type", "application/json")
|
|
70
|
+
// A browser says which page sent a POST; the SDK refuses one that does not.
|
|
71
|
+
req.Header.Set("Sec-Fetch-Site", "same-origin")
|
|
70
72
|
return req
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Überprüfen Sie den eingegebenen Wert und versuchen Sie es erneut.",
|
|
9
9
|
"error.bad_response": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
10
10
|
"error.cross_origin": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
11
|
+
"error.origin_required": "Die Anfrage wurde abgelehnt: Der Browser hat nicht angegeben, dass sie von der Seite dieser App stammt. Öffnen Sie die App in einem aktuellen Browser erneut.",
|
|
11
12
|
"error.internal": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
12
13
|
"error.json_required": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
13
14
|
"error.method_not_allowed": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
14
15
|
"error.network": "Verbindung fehlgeschlagen. Bitte überprüfen Sie Ihre Netzwerkeinstellungen.",
|
|
15
16
|
"error.request_too_large": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
17
|
+
"error.request_timeout": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
18
|
+
"error.request_canceled": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
16
19
|
"error.session_required": "Verbindung abgelaufen. Melden Sie sich erneut an.",
|
|
17
20
|
"error.unknown_method": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
18
21
|
"greeting.adminOnly": "Nur Mitglieder der Gruppe administrators können den Gruß ändern.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Check the entered value and try again.",
|
|
9
9
|
"error.bad_response": "Unable to perform this operation. Please try again later.",
|
|
10
10
|
"error.cross_origin": "Unable to perform this operation. Please try again later.",
|
|
11
|
+
"error.origin_required": "The request was refused: the browser did not show that it came from this App's page. Open the App again in a current browser.",
|
|
11
12
|
"error.internal": "Unable to perform this operation. Please try again later.",
|
|
12
13
|
"error.json_required": "Unable to perform this operation. Please try again later.",
|
|
13
14
|
"error.method_not_allowed": "Unable to perform this operation. Please try again later.",
|
|
14
15
|
"error.network": "Connection failed. Please check your network settings.",
|
|
15
16
|
"error.request_too_large": "Unable to perform this operation. Please try again later.",
|
|
17
|
+
"error.request_timeout": "Unable to perform this operation. Please try again later.",
|
|
18
|
+
"error.request_canceled": "Unable to perform this operation. Please try again later.",
|
|
16
19
|
"error.session_required": "Connection expired. Please sign in again.",
|
|
17
20
|
"error.unknown_method": "Unable to perform this operation. Please try again later.",
|
|
18
21
|
"greeting.adminOnly": "Only members of the administrators group can change the greeting.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Compruebe el valor introducido y vuelva a intentarlo.",
|
|
9
9
|
"error.bad_response": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
10
10
|
"error.cross_origin": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
11
|
+
"error.origin_required": "Se rechazó la solicitud: el navegador no indicó que procediera de la página de esta App. Vuelva a abrir la App en un navegador actual.",
|
|
11
12
|
"error.internal": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
12
13
|
"error.json_required": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
13
14
|
"error.method_not_allowed": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
14
15
|
"error.network": "Error de la conexión. Compruebe su conexión de red.",
|
|
15
16
|
"error.request_too_large": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
17
|
+
"error.request_timeout": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
18
|
+
"error.request_canceled": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
16
19
|
"error.session_required": "Conexión caducada. Vuelva a conectarse.",
|
|
17
20
|
"error.unknown_method": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
18
21
|
"greeting.adminOnly": "Solo los miembros del grupo administrators pueden cambiar el saludo.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Vérifiez la valeur saisie et réessayez.",
|
|
9
9
|
"error.bad_response": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
10
10
|
"error.cross_origin": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
11
|
+
"error.origin_required": "La requête a été refusée : le navigateur n’a pas indiqué qu’elle provenait de la page de cette App. Rouvrez l’App dans un navigateur récent.",
|
|
11
12
|
"error.internal": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
12
13
|
"error.json_required": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
13
14
|
"error.method_not_allowed": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
14
15
|
"error.network": "La connexion a échoué. Vérifiez vos paramètres réseau.",
|
|
15
16
|
"error.request_too_large": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
17
|
+
"error.request_timeout": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
18
|
+
"error.request_canceled": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
16
19
|
"error.session_required": "La connexion a expiré. Reconnectez-vous.",
|
|
17
20
|
"error.unknown_method": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
18
21
|
"greeting.adminOnly": "Seuls les membres du groupe administrators peuvent modifier le message d'accueil.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Controllare il valore inserito e riprovare.",
|
|
9
9
|
"error.bad_response": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
10
10
|
"error.cross_origin": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
11
|
+
"error.origin_required": "La richiesta è stata rifiutata: il browser non ha indicato che proviene dalla pagina di questa App. Riaprire l’App in un browser aggiornato.",
|
|
11
12
|
"error.internal": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
12
13
|
"error.json_required": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
13
14
|
"error.method_not_allowed": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
14
15
|
"error.network": "Connessione non riuscita. Controllare le impostazioni di rete.",
|
|
15
16
|
"error.request_too_large": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
17
|
+
"error.request_timeout": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
18
|
+
"error.request_canceled": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
16
19
|
"error.session_required": "Connessione scaduta. Accedere nuovamente.",
|
|
17
20
|
"error.unknown_method": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
18
21
|
"greeting.adminOnly": "Solo i membri del gruppo administrators possono modificare il saluto.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "入力した値を確認して、もう一度お試しください。",
|
|
9
9
|
"error.bad_response": "この操作は実行できません。後でもう一度お試しください。",
|
|
10
10
|
"error.cross_origin": "この操作は実行できません。後でもう一度お試しください。",
|
|
11
|
+
"error.origin_required": "リクエストは拒否されました。ブラウザーがこの App のページからのリクエストであることを示しませんでした。最新のブラウザーで App を開き直してください。",
|
|
11
12
|
"error.internal": "この操作は実行できません。後でもう一度お試しください。",
|
|
12
13
|
"error.json_required": "この操作は実行できません。後でもう一度お試しください。",
|
|
13
14
|
"error.method_not_allowed": "この操作は実行できません。後でもう一度お試しください。",
|
|
14
15
|
"error.network": "接続に失敗しました。ネットワーク設定を確認してください。",
|
|
15
16
|
"error.request_too_large": "この操作は実行できません。後でもう一度お試しください。",
|
|
17
|
+
"error.request_timeout": "この操作は実行できません。後でもう一度お試しください。",
|
|
18
|
+
"error.request_canceled": "この操作は実行できません。後でもう一度お試しください。",
|
|
16
19
|
"error.session_required": "接続の期限が切れています。もう 1 度サイン インしてください。",
|
|
17
20
|
"error.unknown_method": "この操作は実行できません。後でもう一度お試しください。",
|
|
18
21
|
"greeting.adminOnly": "あいさつを変更できるのは administrators グループのメンバーのみです。",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "입력한 값을 확인한 후 다시 시도하십시오.",
|
|
9
9
|
"error.bad_response": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
10
10
|
"error.cross_origin": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
11
|
+
"error.origin_required": "요청이 거부되었습니다. 브라우저가 이 App 페이지에서 보낸 요청임을 알리지 않았습니다. 최신 브라우저에서 App을 다시 여십시오.",
|
|
11
12
|
"error.internal": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
12
13
|
"error.json_required": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
13
14
|
"error.method_not_allowed": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
14
15
|
"error.network": "연결 실패. 네트워크 설정을 확인해 주십시오.",
|
|
15
16
|
"error.request_too_large": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
17
|
+
"error.request_timeout": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
18
|
+
"error.request_canceled": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
16
19
|
"error.session_required": "연결 만료됨. 다시 로그인 해 주십시오.",
|
|
17
20
|
"error.unknown_method": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
18
21
|
"greeting.adminOnly": "administrators 그룹의 구성원만 인사말을 변경할 수 있습니다.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "请检查输入的值后重试。",
|
|
9
9
|
"error.bad_response": "无法执行此操作。请稍后再试。",
|
|
10
10
|
"error.cross_origin": "无法执行此操作。请稍后再试。",
|
|
11
|
+
"error.origin_required": "请求已被拒绝:浏览器没有表明它来自此 App 的页面。请使用新版浏览器重新打开 App。",
|
|
11
12
|
"error.internal": "无法执行此操作。请稍后再试。",
|
|
12
13
|
"error.json_required": "无法执行此操作。请稍后再试。",
|
|
13
14
|
"error.method_not_allowed": "无法执行此操作。请稍后再试。",
|
|
14
15
|
"error.network": "无法联机,请检查你的网络设置。",
|
|
15
16
|
"error.request_too_large": "无法执行此操作。请稍后再试。",
|
|
17
|
+
"error.request_timeout": "无法执行此操作。请稍后再试。",
|
|
18
|
+
"error.request_canceled": "无法执行此操作。请稍后再试。",
|
|
16
19
|
"error.session_required": "联机超时,请重新登录。",
|
|
17
20
|
"error.unknown_method": "无法执行此操作。请稍后再试。",
|
|
18
21
|
"greeting.adminOnly": "只有 administrators 群组的成员可以更改问候语。",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "請檢查輸入的值後再試一次。",
|
|
9
9
|
"error.bad_response": "無法執行此操作。請稍後再試。",
|
|
10
10
|
"error.cross_origin": "無法執行此操作。請稍後再試。",
|
|
11
|
+
"error.origin_required": "要求已被拒絕:瀏覽器沒有表明它來自這個 App 的頁面。請使用新版瀏覽器重新開啟 App。",
|
|
11
12
|
"error.internal": "無法執行此操作。請稍後再試。",
|
|
12
13
|
"error.json_required": "無法執行此操作。請稍後再試。",
|
|
13
14
|
"error.method_not_allowed": "無法執行此操作。請稍後再試。",
|
|
14
15
|
"error.network": "無法連線,請檢查你的網路設定。",
|
|
15
16
|
"error.request_too_large": "無法執行此操作。請稍後再試。",
|
|
17
|
+
"error.request_timeout": "無法執行此操作。請稍後再試。",
|
|
18
|
+
"error.request_canceled": "無法執行此操作。請稍後再試。",
|
|
16
19
|
"error.session_required": "連線逾時。請重新登入。",
|
|
17
20
|
"error.unknown_method": "無法執行此操作。請稍後再試。",
|
|
18
21
|
"greeting.adminOnly": "只有 administrators 群組的成員可以變更問候語。",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import {computed,onBeforeUnmount,onMounted,ref} from 'vue'
|
|
3
|
-
import {Button,Callout} from '@synopackageland/ui'
|
|
3
|
+
import {Button,Callout,SectionSkeleton} from '@synopackageland/ui'
|
|
4
4
|
import type {Context} from '@synopackageland/sdk'
|
|
5
5
|
import {BackgroundSavePanel, DelegatePanel, ExecutorSettings, createBackgroundPickerProvider, createBackgroundSaveFacade, saveCopy} from '@synopackageland/sdk/vue'
|
|
6
6
|
const props=defineProps<{ctx:Context;admin:boolean}>()
|
|
@@ -20,13 +20,15 @@ async function detect(){
|
|
|
20
20
|
}else mode.value='legacy'
|
|
21
21
|
}catch{if(!disposed)mode.value='unavailable'}
|
|
22
22
|
}
|
|
23
|
+
const executor=ref<{refresh():unknown}>(),panel=ref<{refresh():unknown}>()
|
|
24
|
+
defineExpose({refresh(){void executor.value?.refresh();void panel.value?.refresh()}})
|
|
23
25
|
onMounted(()=>{if(!props.ctx.delegate)void detect()})
|
|
24
26
|
onBeforeUnmount(()=>{disposed=true;provider.dispose()})
|
|
25
27
|
</script>
|
|
26
28
|
<template>
|
|
27
|
-
<ExecutorSettings :admin="admin" :locale="ctx.locale" :ctx="ctx" @confirmed="revision++;detect()" />
|
|
28
|
-
<BackgroundSavePanel v-if="mode==='long'" :key="revision" :sdk="sdk" :admin="admin" :locale="ctx.locale" :picker="{provider,keys}" />
|
|
29
|
-
<DelegatePanel v-else-if="mode==='legacy'" :ctx="ctx" />
|
|
30
|
-
<
|
|
31
|
-
<Callout v-else tone="danger" role="alert">{{ copy.error }} <Button variant="secondary" @click="detect">{{ copy.
|
|
29
|
+
<ExecutorSettings ref="executor" :admin="admin" :locale="ctx.locale" :ctx="ctx" @confirmed="revision++;detect()" />
|
|
30
|
+
<BackgroundSavePanel v-if="mode==='long'" ref="panel" :key="revision" :sdk="sdk" :admin="admin" :locale="ctx.locale" :picker="{provider,keys}" sample-save />
|
|
31
|
+
<DelegatePanel v-else-if="mode==='legacy'" ref="panel" :ctx="ctx" />
|
|
32
|
+
<SectionSkeleton v-else-if="mode==='loading'" />
|
|
33
|
+
<Callout v-else tone="danger" role="alert">{{ copy.error }} <Button variant="secondary" @click="detect">{{ copy.retry }}</Button></Callout>
|
|
32
34
|
</template>
|
|
@@ -33,21 +33,25 @@ test('the capability surface uses an explicit long-running facade without legacy
|
|
|
33
33
|
test('the default surface binds the real routes and the session-only picker',async()=>{
|
|
34
34
|
const grant={id:'g1',key:'save',path:'/home',username:'normal1',lifetime:'until-revoked'}
|
|
35
35
|
let selected=false
|
|
36
|
-
const call=vi.fn(async(method:string)=>{
|
|
36
|
+
const call=vi.fn(async(method:string,params?:unknown)=>{
|
|
37
37
|
if(method==='delegate.status')return {state:'ready',disabled:false,lifetime:'until-revoked',destinations:['save']}
|
|
38
38
|
if(method==='delegate.destination')return {key:'save',selection:{scope:'home',root:'/home',generation:0},destination:selected?grant:null,status:selected?'active':'missing'}
|
|
39
39
|
if(method==='delegate.list')return {delegations:selected?[{delegation_id:'g1',destination:'save',path:'/home',subject:{username:'normal1'},lifetime:'until-revoked',revoked:false}]:[]}
|
|
40
40
|
if(method==='delegate.operations.list')return {operations:[]}
|
|
41
41
|
if(method==='bg.browse')return {shares:[{name:'home',path:'/home',isdir:true}],offset:0,total:1}
|
|
42
42
|
if(method==='delegate.select_destination'){selected=true;return grant}
|
|
43
|
+
if(method==='delegate.example.export')return {delegation_id:'g1',job_id:(params as {job_id:string}).job_id}
|
|
43
44
|
throw {code:'unexpected'}
|
|
44
45
|
})
|
|
45
46
|
const root=render({call,locale:'en'});await settle()
|
|
46
|
-
button(root,'Choose save destination
|
|
47
|
+
button(root,'Choose save destination').click();await settle()
|
|
47
48
|
// The SDK picker selects a folder with a row click.
|
|
48
49
|
const row=root.querySelector<HTMLElement>('tr[data-row-key="/home"]')!;expect(row).toBeTruthy();row.dispatchEvent(new MouseEvent('click',{bubbles:true}));await settle()
|
|
49
50
|
button(root,'Confirm selection').click();await settle()
|
|
50
51
|
expect(call).toHaveBeenCalledWith('delegate.select_destination',{key:'save',path:'/home'})
|
|
52
|
+
// The template demonstrates the save flow; SDK 0.9.9 shows it only on request.
|
|
53
|
+
button(root,'Save example file').click();await settle()
|
|
54
|
+
expect(call).toHaveBeenCalledWith('delegate.example.export',expect.objectContaining({key:'save'}))
|
|
51
55
|
})
|
|
52
56
|
test('the app mount forwards ctx.executor to administrator setup only',async()=>{
|
|
53
57
|
for(const admin of [true,false]){
|
|
@@ -76,3 +80,25 @@ test('the app mount forwards ctx.executor to administrator setup only',async()=>
|
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
})
|
|
83
|
+
test('the header Refresh reloads the save destinations',async()=>{
|
|
84
|
+
const call=vi.fn(async(method:string)=>{
|
|
85
|
+
if(method==='app.status')return {package:'DelegateApp',version:'1',user:'alice',admin:false}
|
|
86
|
+
if(method==='delegate.status')return {state:'ready',disabled:false,lifetime:'until-revoked',destinations:['save']}
|
|
87
|
+
if(method==='delegate.destination')return {key:'save',selection:{scope:'home',root:'/home',generation:0},destination:null,status:'missing'}
|
|
88
|
+
if(method==='delegate.list')return {delegations:[]}
|
|
89
|
+
if(method==='delegate.operations.list')return {operations:[]}
|
|
90
|
+
throw {code:'unknown_method'}
|
|
91
|
+
})
|
|
92
|
+
const root=document.createElement('div');document.body.append(root)
|
|
93
|
+
const app=mount(root,{locale:'en',call,reopen:()=>()=>{}} as any)
|
|
94
|
+
cleanup.push(()=>{app.unmount();root.remove()});await settle()
|
|
95
|
+
const reads=()=>call.mock.calls.filter(([method])=>method==='delegate.destination').length
|
|
96
|
+
const before=reads();expect(before).toBeGreaterThan(0)
|
|
97
|
+
button(root,'Refresh').click();await settle()
|
|
98
|
+
expect(reads()).toBeGreaterThan(before)
|
|
99
|
+
})
|
|
100
|
+
test('the panels show a skeleton while the delegate status loads',async()=>{
|
|
101
|
+
const call=vi.fn(async(method:string)=>{if(method==='delegate.status')return new Promise(()=>{});throw {code:'unexpected'}})
|
|
102
|
+
const root=render({call,locale:'en'});await settle()
|
|
103
|
+
expect(root.querySelector('[data-ui-skeleton-line]')).not.toBeNull()
|
|
104
|
+
})
|
|
@@ -83,6 +83,7 @@ func TestBackgroundRequestSubmitBusyAndProductionConstruction(t *testing.T) {
|
|
|
83
83
|
}
|
|
84
84
|
r := httptest.NewRequest("POST", "/api.cgi", strings.NewReader(`{"method":"bg.jobs.list","params":{}}`))
|
|
85
85
|
r.Header.Set("Content-Type", "application/json")
|
|
86
|
+
r.Header.Set("Sec-Fetch-Site", "same-origin")
|
|
86
87
|
w := httptest.NewRecorder()
|
|
87
88
|
srv.ServeHTTP(w, r)
|
|
88
89
|
if !strings.Contains(w.Body.String(), `"jobs_busy"`) {
|
|
@@ -39,3 +39,21 @@ test('the background panel pauses a running job and resumes a paused one',async(
|
|
|
39
39
|
expect(call).toHaveBeenCalledWith('bg.jobs.resume',{id:'held'})
|
|
40
40
|
}finally{app.unmount();root.remove()}
|
|
41
41
|
})
|
|
42
|
+
test('the header Refresh reloads the background panel',async()=>{
|
|
43
|
+
const call=vi.fn(async(method:string):Promise<unknown>=>{
|
|
44
|
+
if(method==='app.status')return {package:'Jobs',version:'1',user:'alice',admin:false}
|
|
45
|
+
if(method==='bg.status')return {owner:{user:'alice',uid:'1024'},admin:false,revision:1,worker:{username:'pkg',alive:true},limits:{maxInputBytes:1024,maxQueued:8,maxOwnerPending:4,maxOwnerBytes:4096,jobTimeoutSeconds:60}}
|
|
46
|
+
if(method==='bg.jobs.list')return {jobs:[]}
|
|
47
|
+
if(method==='bg.schedules.list')return {schedules:[]}
|
|
48
|
+
return {}
|
|
49
|
+
})
|
|
50
|
+
const root=document.createElement('div');document.body.append(root)
|
|
51
|
+
const app=mount(root,{locale:'en',call,reopen:()=>()=>{}} as unknown as Context)
|
|
52
|
+
const reads=()=>call.mock.calls.filter(([method])=>method==='bg.jobs.list').length
|
|
53
|
+
try{
|
|
54
|
+
await settle();const before=reads()
|
|
55
|
+
// The header button comes first; the panels below may have their own.
|
|
56
|
+
;(Array.from(root.querySelectorAll('button')).find(b=>b.textContent==='Refresh') as HTMLButtonElement).click();await settle()
|
|
57
|
+
expect(reads()).toBeGreaterThan(before)
|
|
58
|
+
}finally{app.unmount();root.remove()}
|
|
59
|
+
})
|
|
@@ -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.9.
|
|
11
|
-
"@synopackageland/ui": "0.9.
|
|
10
|
+
"@synopackageland/sdk": "0.9.9",
|
|
11
|
+
"@synopackageland/ui": "0.9.3",
|
|
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.9.
|
|
1152
|
-
"resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.9.
|
|
1153
|
-
"integrity": "sha512-
|
|
1151
|
+
"version": "0.9.9",
|
|
1152
|
+
"resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.9.9.tgz",
|
|
1153
|
+
"integrity": "sha512-O73D+s+yJPmYLfZ2l261S1izqa5lAc9hgbuBScE4uoKyydUDfWEhAmb7CUM6Er25wY8nYqG480GMcyfLuESFGw==",
|
|
1154
1154
|
"license": "MIT",
|
|
1155
1155
|
"peerDependencies": {
|
|
1156
1156
|
"@lucide/vue": "^1.48.0",
|
|
1157
|
-
"@synopackageland/ui": "^0.9.
|
|
1157
|
+
"@synopackageland/ui": "^0.9.3",
|
|
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.3",
|
|
1171
|
+
"resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.9.3.tgz",
|
|
1172
|
+
"integrity": "sha512-CcCpSiQ2y4GJnbWjBxRrPO6QzLboMdEzqgbMdpXGaEI3IaTrxwS6zy8yXuuMBQHuqAcfBf3jL7fmcL9A58oVqw==",
|
|
1173
1173
|
"license": "MIT",
|
|
1174
1174
|
"dependencies": {
|
|
1175
1175
|
"@internationalized/date": "3.12.4",
|
|
@@ -2538,9 +2538,9 @@
|
|
|
2538
2538
|
"license": "MIT"
|
|
2539
2539
|
},
|
|
2540
2540
|
"node_modules/zod": {
|
|
2541
|
-
"version": "4.6.
|
|
2542
|
-
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.
|
|
2543
|
-
"integrity": "sha512-
|
|
2541
|
+
"version": "4.6.7",
|
|
2542
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz",
|
|
2543
|
+
"integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==",
|
|
2544
2544
|
"license": "MIT",
|
|
2545
2545
|
"funding": {
|
|
2546
2546
|
"url": "https://github.com/sponsors/colinhacks"
|
|
@@ -45,6 +45,14 @@ async function refresh() {
|
|
|
45
45
|
} catch(e) {if(!disposed && !(early && login.enabled.value)) error.value = message(e)}
|
|
46
46
|
finally {if(!disposed) {busy.value=false; if(again) {again=false; void refresh()}}}
|
|
47
47
|
}
|
|
48
|
+
// The header Refresh also reloads the panels below and reads the portal again.
|
|
49
|
+
const panels = ref<{refresh(): unknown}>()
|
|
50
|
+
const background = ref<{refresh(): unknown}>()
|
|
51
|
+
const portalReads = ref(0)
|
|
52
|
+
const portalKey = computed(() => `${app.value?.user}:${portalReads.value}`)
|
|
53
|
+
function refreshAll() {
|
|
54
|
+
void refresh(); void panels.value?.refresh(); void background.value?.refresh(); portalReads.value++
|
|
55
|
+
}
|
|
48
56
|
async function save() {
|
|
49
57
|
if(disposed || busy.value) return
|
|
50
58
|
busy.value = true; error.value='';notice.value=''
|
|
@@ -72,27 +80,26 @@ onUnmounted(()=>{disposed=true;unsubscribe?.();login.dispose();text.value=''})
|
|
|
72
80
|
<ConfigProvider :locale="uiLocale(ctx.locale)">
|
|
73
81
|
<LoginPage v-if="login.ready.value && login.enabled.value && !login.subject.value" :session="login" :locale="ctx.locale" />
|
|
74
82
|
<AppShell v-else v-model="page" :locale="ctx.locale" :pages="pages" :description="subtitle">
|
|
75
|
-
<template #actions><Button variant="secondary" :disabled="busy" @click="
|
|
83
|
+
<template #actions><Button variant="secondary" :disabled="busy" @click="refreshAll">{{ copy.refresh }}</Button><Button v-if="login.enabled.value" variant="secondary" :disabled="busy" @click="signOut">{{ copy.signOut }}</Button></template>
|
|
76
84
|
<div class="integration-app" :aria-busy="busy" :aria-label="copy.settings">
|
|
77
|
-
<p v-if="busy" role="status">{{ copy.loading }}</p>
|
|
78
85
|
<Callout v-if="error" tone="danger" :title="copy.actionRequired"><span role="alert">{{ error }}</span></Callout>
|
|
79
86
|
<p v-if="notice" role="status">{{ notice }}</p>
|
|
80
87
|
<div data-page="status" v-show="page==='status'">
|
|
81
|
-
<SettingsSection :title="copy.statusTitle" :collapsible="false">
|
|
88
|
+
<SettingsSection :title="copy.statusTitle" :collapsible="false" :loading="busy && !app">
|
|
82
89
|
<p v-if="app">{{ app.package }} · {{ app.user }} · {{ app.version }}</p>
|
|
83
90
|
<p v-else-if="error" role="status">{{ copy.statusError }}</p>
|
|
84
91
|
</SettingsSection>
|
|
85
|
-
<PortalSettings v-if="ctx.portal && app" :key="
|
|
92
|
+
<PortalSettings v-if="ctx.portal && app" :key="portalKey" :portal="ctx.portal" />
|
|
86
93
|
<SettingsSection v-if="app?.admin" :title="copy.greeting" :collapsible="false">
|
|
87
|
-
<Form @submit.prevent="save"><FormItem :label="copy.greeting"><Textarea v-model="text" :disabled="busy" /></FormItem>
|
|
94
|
+
<Form direction="vertical" @submit.prevent="save"><FormItem :label="copy.greeting"><Textarea v-model="text" :disabled="busy" /></FormItem>
|
|
88
95
|
<FormActions><Button variant="primary" :disabled="busy || !text.trim()" @click="save">{{ copy.save }}</Button></FormActions>
|
|
89
96
|
</Form>
|
|
90
97
|
</SettingsSection>
|
|
91
98
|
</div>
|
|
92
|
-
<CapabilityPanels v-if="app" :key="app.user" :ctx="ctx" :admin="app.admin" />
|
|
99
|
+
<CapabilityPanels v-if="app" ref="panels" :key="app.user" :ctx="ctx" :admin="app.admin" />
|
|
93
100
|
<FilesPage v-if="page==='status'" :ctx="ctx" />
|
|
94
101
|
<div data-page="background" v-show="page==='background'">
|
|
95
|
-
<BackgroundPanel v-if="backgroundEnabled && app" :key="app.user" :ctx="ctx" />
|
|
102
|
+
<BackgroundPanel v-if="backgroundEnabled && app" ref="background" :key="app.user" :ctx="ctx" />
|
|
96
103
|
</div>
|
|
97
104
|
</div>
|
|
98
105
|
</AppShell>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import {ref} from 'vue'
|
|
2
3
|
import type {Context} from '@synopackageland/sdk'
|
|
3
4
|
import {ExecutorSettings} from '@synopackageland/sdk/vue'
|
|
4
5
|
defineProps<{ctx:Context;admin:boolean}>()
|
|
6
|
+
const executor=ref<{refresh():unknown}>()
|
|
7
|
+
defineExpose({refresh:()=>void executor.value?.refresh()})
|
|
5
8
|
</script>
|
|
6
|
-
<template><ExecutorSettings v-if="ctx.executor" :admin="admin" :locale="ctx.locale" /></template>
|
|
9
|
+
<template><ExecutorSettings v-if="ctx.executor" ref="executor" :admin="admin" :locale="ctx.locale" /></template>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"refresh": "Aktualisieren",
|
|
3
3
|
"signOut": "Abmelden",
|
|
4
4
|
"settings": "Paketeinstellungen",
|
|
5
|
-
"loading": "Paketdaten werden geladen…",
|
|
6
5
|
"actionRequired": "Aktion erforderlich",
|
|
7
6
|
"statusTitle": "Paketstatus",
|
|
8
7
|
"statusError": "Der Status konnte nicht geladen werden. Aktualisieren Sie die Ansicht und versuchen Sie es erneut.",
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"refresh": "Actualizar",
|
|
3
3
|
"signOut": "Cerrar sesión",
|
|
4
4
|
"settings": "Configuración del paquete",
|
|
5
|
-
"loading": "Cargando datos del paquete…",
|
|
6
5
|
"actionRequired": "Acción necesaria",
|
|
7
6
|
"statusTitle": "Estado del paquete",
|
|
8
7
|
"statusError": "No se pudo cargar el estado. Actualice e inténtelo de nuevo.",
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"refresh": "Actualiser",
|
|
3
3
|
"signOut": "Se déconnecter",
|
|
4
4
|
"settings": "Paramètres du paquet",
|
|
5
|
-
"loading": "Chargement des données du paquet…",
|
|
6
5
|
"actionRequired": "Action requise",
|
|
7
6
|
"statusTitle": "État du paquet",
|
|
8
7
|
"statusError": "Impossible de charger l’état. Actualisez et réessayez.",
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"refresh": "Aggiorna",
|
|
3
3
|
"signOut": "Disconnetti",
|
|
4
4
|
"settings": "Impostazioni del pacchetto",
|
|
5
|
-
"loading": "Caricamento dei dati del pacchetto…",
|
|
6
5
|
"actionRequired": "Azione richiesta",
|
|
7
6
|
"statusTitle": "Stato del pacchetto",
|
|
8
7
|
"statusError": "Impossibile caricare lo stato. Aggiornare e riprovare.",
|
|
@@ -55,6 +55,22 @@ test('a portal app shows the Application Portal section on the status page',asyn
|
|
|
55
55
|
app.unmount()
|
|
56
56
|
}finally{without.close();f.root.remove()}
|
|
57
57
|
})
|
|
58
|
+
test('the header Refresh reads the portal and executor status again',async()=>{
|
|
59
|
+
const f=fixture('Portal')
|
|
60
|
+
try{
|
|
61
|
+
await settle();f.app.unmount()
|
|
62
|
+
const status={state:'active',alias:'portal',url:'https://nas.example/portal/',canManage:false,controlPanel:false,defaultAlias:'portal'} as const
|
|
63
|
+
const portal=vi.fn(async()=>status),executor=vi.fn(async()=>({state:'ready' as const}))
|
|
64
|
+
f.ctx.portal={messages:{title:'Application Portal',link:'Direct link:'},status:portal,apply:vi.fn(),disable:vi.fn(),openControlPanel:vi.fn(),mountPanel:vi.fn()}
|
|
65
|
+
f.ctx.executor={status:executor,authorize:vi.fn(),control:vi.fn()} as unknown as Context['executor']
|
|
66
|
+
const app=mount(f.root,f.ctx);await settle()
|
|
67
|
+
const [portalReads,executorReads]=[portal.mock.calls.length,executor.mock.calls.length]
|
|
68
|
+
;[...f.root.querySelectorAll('button')].find(n=>n.textContent==='Refresh')!.click();await settle()
|
|
69
|
+
expect(portal.mock.calls.length).toBeGreaterThan(portalReads)
|
|
70
|
+
expect(executor.mock.calls.length).toBeGreaterThan(executorReads)
|
|
71
|
+
app.unmount()
|
|
72
|
+
}finally{f.root.remove()}
|
|
73
|
+
})
|
|
58
74
|
test('the app renders and loads status while the session check is still pending',async()=>{
|
|
59
75
|
const call=vi.fn(async(method:string)=>{
|
|
60
76
|
if(method==='identity.status')return new Promise(()=>{})
|
|
@@ -129,3 +145,24 @@ test('outside a DSM window dialogs stay inside the app',async()=>{
|
|
|
129
145
|
try{await settle();expect(document.querySelector('[data-ui-floating]')).toBeNull()}finally{f.close()}
|
|
130
146
|
}
|
|
131
147
|
})
|
|
148
|
+
test('the status page follows the UI layout rules',async()=>{
|
|
149
|
+
let answer:(value:unknown)=>void=()=>{}
|
|
150
|
+
const call=vi.fn(async(method:string)=>{
|
|
151
|
+
if(method==='app.status')return new Promise(resolve=>{answer=resolve})
|
|
152
|
+
if(method==='greeting.get')return {text:'Hello'}
|
|
153
|
+
throw {code:'unknown_method'}
|
|
154
|
+
})
|
|
155
|
+
const root=document.createElement('div');document.body.append(root)
|
|
156
|
+
const app=mount(root,{locale:'en',call,reopen:()=>()=>{}} as unknown as Context)
|
|
157
|
+
try{
|
|
158
|
+
await settle()
|
|
159
|
+
// A loading section keeps its title and shows a skeleton, not a sentence.
|
|
160
|
+
expect(root.textContent).toContain('Package status')
|
|
161
|
+
expect(root.querySelector('[data-ui-skeleton-line]')).not.toBeNull()
|
|
162
|
+
expect(root.textContent).not.toContain('Loading package data')
|
|
163
|
+
answer({package:'Pkg',version:'1',user:'admin',admin:true});await settle()
|
|
164
|
+
expect(root.querySelector('[data-ui-skeleton-line]')).toBeNull()
|
|
165
|
+
// Labels sit above their fields.
|
|
166
|
+
expect(root.querySelector('textarea')!.closest('[data-form-item]')!.getAttribute('data-direction')).toBe('vertical')
|
|
167
|
+
}finally{app.unmount();root.remove()}
|
|
168
|
+
})
|
|
@@ -10,12 +10,12 @@ const uses=(file:string,tag:string)=>new RegExp(`<${tag}[\\s>/]`).test(template(
|
|
|
10
10
|
test.each(['select','option','textarea','label','input','a'])('no vue template renders a native <%s>',tag=>{
|
|
11
11
|
expect(sources(root).filter(file=>uses(file,tag))).toEqual([])
|
|
12
12
|
})
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
test('FormItem controls
|
|
13
|
+
// UI layout rules: labels sit above their controls, so a page Form is vertical; a control
|
|
14
|
+
// inside a FormItem binds its id/ARIA by itself.
|
|
15
|
+
test('FormItem labels sit above their controls and the UI binds the control id',()=>{
|
|
16
16
|
const items=sources(root).filter(file=>uses(file,'FormItem'))
|
|
17
17
|
expect(items.length).toBeGreaterThan(0)
|
|
18
|
-
expect(items.filter(file
|
|
18
|
+
expect(items.filter(file=>/<Form(?![^>]*direction="vertical")[\s>]|field\.controlId|#default="\{field\}"/.test(template(file)))).toEqual([])
|
|
19
19
|
})
|
|
20
20
|
// UI built-in text follows the Provider locale (nine locales), so every app root sets it once and no
|
|
21
21
|
// component repeats a per-control locale or download label.
|
|
@@ -104,8 +104,8 @@ dependencies = [
|
|
|
104
104
|
|
|
105
105
|
[[package]]
|
|
106
106
|
name = "syno-pkg-land-sdk"
|
|
107
|
-
version = "0.9.
|
|
108
|
-
source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.9.
|
|
107
|
+
version = "0.9.9"
|
|
108
|
+
source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.9.9#28da1726b0c06f4428153555314d33c7a3ca8a8b"
|
|
109
109
|
dependencies = [
|
|
110
110
|
"libc",
|
|
111
111
|
"serde",
|
|
@@ -17,7 +17,7 @@ libc = "0.2"
|
|
|
17
17
|
serde = { version = "1", features = ["derive"] }
|
|
18
18
|
serde_json = "1"
|
|
19
19
|
# The SDK version is the tag; `synopkgland check` reads it from Cargo.lock.
|
|
20
|
-
syno-pkg-land-sdk = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.9.
|
|
20
|
+
syno-pkg-land-sdk = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.9.9" }
|
|
21
21
|
|
|
22
22
|
[profile.release]
|
|
23
23
|
# A handler panic becomes an "internal" reply (api::Server); abort would end the CGI instead.
|
|
@@ -130,6 +130,8 @@ pub fn api_request(method: &str, params: &str) -> Request {
|
|
|
130
130
|
let body = format!(r#"{{"method":"{method}","params":{params}}}"#);
|
|
131
131
|
let mut req = Request::new("POST", "/api.cgi").with_body(body);
|
|
132
132
|
req.header.set("Content-Type", "application/json");
|
|
133
|
+
// A browser says which page sent a POST; the SDK refuses one that does not.
|
|
134
|
+
req.header.set("Sec-Fetch-Site", "same-origin");
|
|
133
135
|
req
|
|
134
136
|
}
|
|
135
137
|
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Überprüfen Sie den eingegebenen Wert und versuchen Sie es erneut.",
|
|
9
9
|
"error.bad_response": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
10
10
|
"error.cross_origin": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
11
|
+
"error.origin_required": "Die Anfrage wurde abgelehnt: Der Browser hat nicht angegeben, dass sie von der Seite dieser App stammt. Öffnen Sie die App in einem aktuellen Browser erneut.",
|
|
11
12
|
"error.internal": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
12
13
|
"error.json_required": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
13
14
|
"error.method_not_allowed": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
14
15
|
"error.network": "Verbindung fehlgeschlagen. Bitte überprüfen Sie Ihre Netzwerkeinstellungen.",
|
|
15
16
|
"error.request_too_large": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
17
|
+
"error.request_timeout": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
18
|
+
"error.request_canceled": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
16
19
|
"error.session_required": "Verbindung abgelaufen. Melden Sie sich erneut an.",
|
|
17
20
|
"error.unknown_method": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
18
21
|
"greeting.adminOnly": "Nur Mitglieder der Gruppe administrators können den Gruß ändern.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Check the entered value and try again.",
|
|
9
9
|
"error.bad_response": "Unable to perform this operation. Please try again later.",
|
|
10
10
|
"error.cross_origin": "Unable to perform this operation. Please try again later.",
|
|
11
|
+
"error.origin_required": "The request was refused: the browser did not show that it came from this App's page. Open the App again in a current browser.",
|
|
11
12
|
"error.internal": "Unable to perform this operation. Please try again later.",
|
|
12
13
|
"error.json_required": "Unable to perform this operation. Please try again later.",
|
|
13
14
|
"error.method_not_allowed": "Unable to perform this operation. Please try again later.",
|
|
14
15
|
"error.network": "Connection failed. Please check your network settings.",
|
|
15
16
|
"error.request_too_large": "Unable to perform this operation. Please try again later.",
|
|
17
|
+
"error.request_timeout": "Unable to perform this operation. Please try again later.",
|
|
18
|
+
"error.request_canceled": "Unable to perform this operation. Please try again later.",
|
|
16
19
|
"error.session_required": "Connection expired. Please sign in again.",
|
|
17
20
|
"error.unknown_method": "Unable to perform this operation. Please try again later.",
|
|
18
21
|
"greeting.adminOnly": "Only members of the administrators group can change the greeting.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Compruebe el valor introducido y vuelva a intentarlo.",
|
|
9
9
|
"error.bad_response": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
10
10
|
"error.cross_origin": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
11
|
+
"error.origin_required": "Se rechazó la solicitud: el navegador no indicó que procediera de la página de esta App. Vuelva a abrir la App en un navegador actual.",
|
|
11
12
|
"error.internal": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
12
13
|
"error.json_required": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
13
14
|
"error.method_not_allowed": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
14
15
|
"error.network": "Error de la conexión. Compruebe su conexión de red.",
|
|
15
16
|
"error.request_too_large": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
17
|
+
"error.request_timeout": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
18
|
+
"error.request_canceled": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
16
19
|
"error.session_required": "Conexión caducada. Vuelva a conectarse.",
|
|
17
20
|
"error.unknown_method": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
18
21
|
"greeting.adminOnly": "Solo los miembros del grupo administrators pueden cambiar el saludo.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Vérifiez la valeur saisie et réessayez.",
|
|
9
9
|
"error.bad_response": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
10
10
|
"error.cross_origin": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
11
|
+
"error.origin_required": "La requête a été refusée : le navigateur n’a pas indiqué qu’elle provenait de la page de cette App. Rouvrez l’App dans un navigateur récent.",
|
|
11
12
|
"error.internal": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
12
13
|
"error.json_required": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
13
14
|
"error.method_not_allowed": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
14
15
|
"error.network": "La connexion a échoué. Vérifiez vos paramètres réseau.",
|
|
15
16
|
"error.request_too_large": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
17
|
+
"error.request_timeout": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
18
|
+
"error.request_canceled": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
16
19
|
"error.session_required": "La connexion a expiré. Reconnectez-vous.",
|
|
17
20
|
"error.unknown_method": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
18
21
|
"greeting.adminOnly": "Seuls les membres du groupe administrators peuvent modifier le message d'accueil.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "Controllare il valore inserito e riprovare.",
|
|
9
9
|
"error.bad_response": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
10
10
|
"error.cross_origin": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
11
|
+
"error.origin_required": "La richiesta è stata rifiutata: il browser non ha indicato che proviene dalla pagina di questa App. Riaprire l’App in un browser aggiornato.",
|
|
11
12
|
"error.internal": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
12
13
|
"error.json_required": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
13
14
|
"error.method_not_allowed": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
14
15
|
"error.network": "Connessione non riuscita. Controllare le impostazioni di rete.",
|
|
15
16
|
"error.request_too_large": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
17
|
+
"error.request_timeout": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
18
|
+
"error.request_canceled": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
16
19
|
"error.session_required": "Connessione scaduta. Accedere nuovamente.",
|
|
17
20
|
"error.unknown_method": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
18
21
|
"greeting.adminOnly": "Solo i membri del gruppo administrators possono modificare il saluto.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "入力した値を確認して、もう一度お試しください。",
|
|
9
9
|
"error.bad_response": "この操作は実行できません。後でもう一度お試しください。",
|
|
10
10
|
"error.cross_origin": "この操作は実行できません。後でもう一度お試しください。",
|
|
11
|
+
"error.origin_required": "リクエストは拒否されました。ブラウザーがこの App のページからのリクエストであることを示しませんでした。最新のブラウザーで App を開き直してください。",
|
|
11
12
|
"error.internal": "この操作は実行できません。後でもう一度お試しください。",
|
|
12
13
|
"error.json_required": "この操作は実行できません。後でもう一度お試しください。",
|
|
13
14
|
"error.method_not_allowed": "この操作は実行できません。後でもう一度お試しください。",
|
|
14
15
|
"error.network": "接続に失敗しました。ネットワーク設定を確認してください。",
|
|
15
16
|
"error.request_too_large": "この操作は実行できません。後でもう一度お試しください。",
|
|
17
|
+
"error.request_timeout": "この操作は実行できません。後でもう一度お試しください。",
|
|
18
|
+
"error.request_canceled": "この操作は実行できません。後でもう一度お試しください。",
|
|
16
19
|
"error.session_required": "接続の期限が切れています。もう 1 度サイン インしてください。",
|
|
17
20
|
"error.unknown_method": "この操作は実行できません。後でもう一度お試しください。",
|
|
18
21
|
"greeting.adminOnly": "あいさつを変更できるのは administrators グループのメンバーのみです。",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "입력한 값을 확인한 후 다시 시도하십시오.",
|
|
9
9
|
"error.bad_response": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
10
10
|
"error.cross_origin": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
11
|
+
"error.origin_required": "요청이 거부되었습니다. 브라우저가 이 App 페이지에서 보낸 요청임을 알리지 않았습니다. 최신 브라우저에서 App을 다시 여십시오.",
|
|
11
12
|
"error.internal": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
12
13
|
"error.json_required": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
13
14
|
"error.method_not_allowed": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
14
15
|
"error.network": "연결 실패. 네트워크 설정을 확인해 주십시오.",
|
|
15
16
|
"error.request_too_large": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
17
|
+
"error.request_timeout": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
18
|
+
"error.request_canceled": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
16
19
|
"error.session_required": "연결 만료됨. 다시 로그인 해 주십시오.",
|
|
17
20
|
"error.unknown_method": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
18
21
|
"greeting.adminOnly": "administrators 그룹의 구성원만 인사말을 변경할 수 있습니다.",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "请检查输入的值后重试。",
|
|
9
9
|
"error.bad_response": "无法执行此操作。请稍后再试。",
|
|
10
10
|
"error.cross_origin": "无法执行此操作。请稍后再试。",
|
|
11
|
+
"error.origin_required": "请求已被拒绝:浏览器没有表明它来自此 App 的页面。请使用新版浏览器重新打开 App。",
|
|
11
12
|
"error.internal": "无法执行此操作。请稍后再试。",
|
|
12
13
|
"error.json_required": "无法执行此操作。请稍后再试。",
|
|
13
14
|
"error.method_not_allowed": "无法执行此操作。请稍后再试。",
|
|
14
15
|
"error.network": "无法联机,请检查你的网络设置。",
|
|
15
16
|
"error.request_too_large": "无法执行此操作。请稍后再试。",
|
|
17
|
+
"error.request_timeout": "无法执行此操作。请稍后再试。",
|
|
18
|
+
"error.request_canceled": "无法执行此操作。请稍后再试。",
|
|
16
19
|
"error.session_required": "联机超时,请重新登录。",
|
|
17
20
|
"error.unknown_method": "无法执行此操作。请稍后再试。",
|
|
18
21
|
"greeting.adminOnly": "只有 administrators 群组的成员可以更改问候语。",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"error.bad_request": "請檢查輸入的值後再試一次。",
|
|
9
9
|
"error.bad_response": "無法執行此操作。請稍後再試。",
|
|
10
10
|
"error.cross_origin": "無法執行此操作。請稍後再試。",
|
|
11
|
+
"error.origin_required": "要求已被拒絕:瀏覽器沒有表明它來自這個 App 的頁面。請使用新版瀏覽器重新開啟 App。",
|
|
11
12
|
"error.internal": "無法執行此操作。請稍後再試。",
|
|
12
13
|
"error.json_required": "無法執行此操作。請稍後再試。",
|
|
13
14
|
"error.method_not_allowed": "無法執行此操作。請稍後再試。",
|
|
14
15
|
"error.network": "無法連線,請檢查你的網路設定。",
|
|
15
16
|
"error.request_too_large": "無法執行此操作。請稍後再試。",
|
|
17
|
+
"error.request_timeout": "無法執行此操作。請稍後再試。",
|
|
18
|
+
"error.request_canceled": "無法執行此操作。請稍後再試。",
|
|
16
19
|
"error.session_required": "連線逾時。請重新登入。",
|
|
17
20
|
"error.unknown_method": "無法執行此操作。請稍後再試。",
|
|
18
21
|
"greeting.adminOnly": "只有 administrators 群組的成員可以變更問候語。",
|