@synopackageland/templates 0.15.1 → 0.16.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 +6 -4
- package/package.json +1 -1
- package/templates/_shared/AGENTS.md +3 -2
- package/templates/binary/ui/app.css +21 -4
- package/templates/compose/dsm-package.yaml +1 -1
- package/templates/native-go/README.md +15 -0
- package/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +1 -1
- package/templates/native-go/backend/cmd/app/main.go +4 -0
- 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/routes.go +12 -2
- package/templates/native-go/backend/internal/app/routes_test.go +4 -3
- package/templates/native-go/backend/internal/app/shared_folder.go +37 -127
- package/templates/native-go/backend/internal/app/shared_folder_test.go +95 -108
- package/templates/native-go/i18n/messages/de.json +4 -3
- package/templates/native-go/i18n/messages/en.json +4 -3
- package/templates/native-go/i18n/messages/es.json +4 -3
- package/templates/native-go/i18n/messages/fr.json +4 -3
- package/templates/native-go/i18n/messages/it.json +4 -3
- package/templates/native-go/i18n/messages/ja.json +4 -3
- package/templates/native-go/i18n/messages/ko.json +4 -3
- package/templates/native-go/i18n/messages/zh-CN.json +4 -3
- package/templates/native-go/i18n/messages/zh-TW.json +4 -3
- package/templates/native-go/ui/app.css +57 -25
- package/templates/native-go/ui/app.js +7 -4
- package/templates/native-go-delegate/frontend/test/capability-panels.test.ts +3 -3
- package/templates/native-go-root-ops/backend/internal/app/executor_test.go +1 -1
- package/templates/native-go-vue/frontend/DESIGN.md +1 -1
- package/templates/native-go-vue/frontend/package-lock.json +118 -193
- package/templates/native-go-vue/frontend/package.json +3 -3
- package/templates/native-go-vue/frontend/src/App.vue +13 -5
- package/templates/native-go-vue/frontend/src/i18n/de.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/en.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/es.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/fr.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/it.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/ja.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/ko.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +5 -2
- package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +5 -2
- package/templates/native-go-vue/frontend/src/style.css +2 -2
- package/templates/native-go-vue/frontend/test/apps.test.ts +22 -2
- package/templates/native-node/dsm-package.yaml +1 -1
- package/templates/native-python/dsm-package.yaml +1 -1
- package/templates/native-rust/backend/Cargo.lock +2 -2
- package/templates/native-rust/backend/Cargo.toml +1 -1
package/README.md
CHANGED
|
@@ -38,12 +38,13 @@ calls each one from the generated script of the same name (`synopkgland skill sh
|
|
|
38
38
|
The placeholders `__PACKAGE__` (also the Go module path), `__DISPLAY_NAME__` and
|
|
39
39
|
`__BACKGROUND_ENABLED__` are substituted by `init`.
|
|
40
40
|
|
|
41
|
-
`backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.
|
|
41
|
+
`backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.16.0`; that line is
|
|
42
42
|
the single source of the SDK version and `frontend/package.json` pins
|
|
43
43
|
`@synopackageland/sdk` to the same version. Python and Node.js name it in
|
|
44
44
|
`backend.sdk`, Rust in the `syno-pkg-land-app` tag of `backend/Cargo.toml`
|
|
45
|
-
(and `Cargo.lock`); all of them pin 0.
|
|
46
|
-
`@synopackageland/ui` 0.
|
|
45
|
+
(and `Cargo.lock`); all of them pin 0.16.0. The Vue frontend pins
|
|
46
|
+
`@synopackageland/ui` 0.12.0. SDK 0.16.0 accepts UI `>=0.10.1 <0.13.0`,
|
|
47
|
+
so no UI peer override is needed. Tests use jsdom 30 for UI floating-layer styles.
|
|
47
48
|
|
|
48
49
|
## Notice writer
|
|
49
50
|
|
|
@@ -76,7 +77,8 @@ Without `SYNOPKGLAND_SDK_DIR` the combinations use the released SDK that
|
|
|
76
77
|
`backend/go.mod` and `frontend/package.json` pin. With it, `assemble.sh` adds
|
|
77
78
|
`replace github.com/synopackageland/syno-pkg-land-sdk => $SYNOPKGLAND_SDK_DIR`
|
|
78
79
|
and installs `$SYNOPKGLAND_SDK_DIR/ts` as an `npm pack` tarball (not a link, so Vue
|
|
79
|
-
resolves once).
|
|
80
|
+
resolves once). Set `SYNOPKGLAND_SDK_TARBALL` to a published tarball to test that
|
|
81
|
+
release without rebuilding the SDK frontend. With `SYNOPKGLAND_UI_TARBALL` set it installs that tarball as
|
|
80
82
|
`@synopackageland/ui`, for a UI version not yet on npm.
|
|
81
83
|
The Python and Node.js combinations need `SYNOPKGLAND_SDK_DIR` (its `python/`,
|
|
82
84
|
and `node/` built with `npm ci && npm run build`) for their App SDK, and the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/templates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Project templates for synopkgland init: native-go, native-python, native-node and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {"type": "git", "url": "git+https://github.com/synopackageland/syno-pkg-land-templates.git"},
|
|
@@ -234,8 +234,9 @@ sweep forward the cancel instead of admitting it.
|
|
|
234
234
|
repeats the original request on repeated IDs. Review it in the SDK's delegate
|
|
235
235
|
package to adapt a real producer; show Saved only from a protected committed
|
|
236
236
|
receipt.
|
|
237
|
-
-
|
|
238
|
-
|
|
237
|
+
- Verify the wizard on your own DSM release over an SSH local forward. The
|
|
238
|
+
template ships the wizard contract; it is not proof of every DSM release or of
|
|
239
|
+
full writer/reboot behavior.
|
|
239
240
|
|
|
240
241
|
## Go backend
|
|
241
242
|
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
.binary-app {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/* Static UI subset from @synopackageland/ui 0.12.0 dsm7-embedded theme. */
|
|
2
|
+
.binary-app {
|
|
3
|
+
--ui-font-sans: Verdana, Arial, "Microsoft JhengHei", "Microsoft YaHei", Meiryo, sans-serif;
|
|
4
|
+
--ui-font-size: 13px;
|
|
5
|
+
--ui-font-size-title: 16px;
|
|
6
|
+
--ui-line-height: 20px;
|
|
7
|
+
--ui-text: #414b55;
|
|
8
|
+
--ui-surface: #fff;
|
|
9
|
+
--ui-space-2: 8px;
|
|
10
|
+
--ui-space-5: 20px;
|
|
11
|
+
--ui-space-6: 24px;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
min-height: 100%;
|
|
14
|
+
padding: var(--ui-space-5) var(--ui-space-6);
|
|
15
|
+
font: var(--ui-font-size)/var(--ui-line-height) var(--ui-font-sans);
|
|
16
|
+
color: var(--ui-text);
|
|
17
|
+
background: var(--ui-surface);
|
|
18
|
+
overflow-wrap: anywhere;
|
|
19
|
+
}
|
|
20
|
+
.binary-app h1 { margin: 0 0 var(--ui-space-2); font-size: var(--ui-font-size-title); font-weight: 600; }
|
|
21
|
+
.binary-app p { margin: 0; }
|
|
@@ -14,7 +14,7 @@ compose:
|
|
|
14
14
|
web: {service: web, port: 8080, health: {path: /, status: 200}}
|
|
15
15
|
network: {egress: isolated}
|
|
16
16
|
identity_gateway:
|
|
17
|
-
sdk: 0.
|
|
17
|
+
sdk: 0.16.0
|
|
18
18
|
mode: none
|
|
19
19
|
listen: {http: 28000}
|
|
20
20
|
# Web Apps default to new-page through the shared app-launch resolver.
|
|
@@ -378,6 +378,21 @@ account or other packages. An upgrade never deletes anything. Do not write your
|
|
|
378
378
|
`synopkgland inspect` flags a deleting script that lacks the upgrade guard
|
|
379
379
|
or deletes outside the package's own folders.
|
|
380
380
|
|
|
381
|
+
## Shared Folder access
|
|
382
|
+
|
|
383
|
+
A `install.sharedFolder` in `dsm-package.yaml` lets the administrator pick a folder in the
|
|
384
|
+
install wizard, but DSM does not give the package account access to a picked folder. The
|
|
385
|
+
backend reports the real state: the `sharedfolder.status` operation (administrators only
|
|
386
|
+
unless you change `permissions.json`) returns each folder with what the App needs, what the
|
|
387
|
+
package account has (`none`, `read`, `write`) and a `state` (`ready`, `pending`, `missing`,
|
|
388
|
+
`ambiguous`). `cfg.SharedFolderPath(key)` returns the path or an error that names the
|
|
389
|
+
Control Panel step instead of failing on a file read. Keep `SharedFolderRequired` in
|
|
390
|
+
`cmd/app/main.go` equal to the permission in `dsm-package.yaml`. Set `StorageAdapter` to
|
|
391
|
+
apply a changed path (a re-picked folder, a moved volume) once; it runs only after access is
|
|
392
|
+
granted. The Vue template shows the `SharedFolderSettings` section, which offers
|
|
393
|
+
**Grant access** to an administrator and otherwise explains the Control Panel steps. See the
|
|
394
|
+
SDK's `docs/shared-folder.md` for the whole flow.
|
|
395
|
+
|
|
381
396
|
## Support matrix
|
|
382
397
|
|
|
383
398
|
| | Status |
|
|
@@ -20,6 +20,7 @@ import (
|
|
|
20
20
|
"github.com/synopackageland/syno-pkg-land-sdk/dsm"
|
|
21
21
|
"github.com/synopackageland/syno-pkg-land-sdk/filestation"
|
|
22
22
|
standaloneidentity "github.com/synopackageland/syno-pkg-land-sdk/identity"
|
|
23
|
+
"github.com/synopackageland/syno-pkg-land-sdk/sharedfolder"
|
|
23
24
|
|
|
24
25
|
appapi "__PACKAGE__/internal/api"
|
|
25
26
|
"__PACKAGE__/internal/app"
|
|
@@ -100,6 +101,9 @@ func main() {
|
|
|
100
101
|
DataDir: "/var/packages/" + packageID + "/var",
|
|
101
102
|
Portal: portal != "",
|
|
102
103
|
PortalExecutor: portal == "executor",
|
|
104
|
+
// install.sharedFolder in dsm-package.yaml: `synopkgland init --shared-folder`
|
|
105
|
+
// writes key "shared" with permission rw. Keep this equal to it.
|
|
106
|
+
SharedFolderRequired: map[string]sharedfolder.Access{"shared": sharedfolder.Write},
|
|
103
107
|
}
|
|
104
108
|
if _, err := app.ParseDelegateConfig(cfg); err != nil {
|
|
105
109
|
log.Fatal(err)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
github.com/synopackageland/syno-pkg-land-sdk v0.
|
|
2
|
-
github.com/synopackageland/syno-pkg-land-sdk v0.
|
|
1
|
+
github.com/synopackageland/syno-pkg-land-sdk v0.16.0 h1:9MYFliraGfCE+1TXyFQD6s/YPMBMhGbj4bY+HHh9Y08=
|
|
2
|
+
github.com/synopackageland/syno-pkg-land-sdk v0.16.0/go.mod h1:vixf5Lo4PssQDGU5zam0Mb5sFu9wPUP4Ck36BMAVbNc=
|
|
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=
|
|
@@ -20,6 +20,7 @@ import (
|
|
|
20
20
|
|
|
21
21
|
"github.com/synopackageland/syno-pkg-land-sdk/api"
|
|
22
22
|
"github.com/synopackageland/syno-pkg-land-sdk/filestation"
|
|
23
|
+
"github.com/synopackageland/syno-pkg-land-sdk/sharedfolder"
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
// Config is fixed at build time and startup, never taken from a request.
|
|
@@ -37,6 +38,14 @@ type Config struct {
|
|
|
37
38
|
DataDir string
|
|
38
39
|
// VolumeRoot is where /volumeN lives; empty means "/". Tests set it.
|
|
39
40
|
VolumeRoot string
|
|
41
|
+
// SharedFolderRequired is the access each Shared Folder key needs. Keep it
|
|
42
|
+
// equal to install.sharedFolder.permission in dsm-package.yaml: ro is
|
|
43
|
+
// sharedfolder.Read, rw is sharedfolder.Write.
|
|
44
|
+
SharedFolderRequired map[string]sharedfolder.Access
|
|
45
|
+
// StorageAdapter, when set, hands a Shared Folder's path to the App's own
|
|
46
|
+
// storage settings (a Local storage, a library root): the first time the
|
|
47
|
+
// folder is ready, and again whenever its path changes. Make it idempotent.
|
|
48
|
+
StorageAdapter func(ctx context.Context, key, path string) error
|
|
40
49
|
// Portal serves portal.status; PortalExecutor adds the executor's entry
|
|
41
50
|
// below DSM 7.2. PortalConfig overrides DSM's saved AppPortal.json path in
|
|
42
51
|
// tests.
|
|
@@ -49,6 +58,7 @@ func Routes(cfg Config) []api.Route {
|
|
|
49
58
|
routes := cfg.delegateRoutes()
|
|
50
59
|
routes = append(routes, []api.Route{
|
|
51
60
|
api.Handle("app.status", cfg.status),
|
|
61
|
+
api.Handle("sharedfolder.status", cfg.sharedFolderStatus),
|
|
52
62
|
api.Handle("greeting.get", cfg.getGreeting),
|
|
53
63
|
api.Handle("greeting.set", cfg.setGreeting),
|
|
54
64
|
}...)
|
|
@@ -59,7 +69,7 @@ func Routes(cfg Config) []api.Route {
|
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
func (cfg Config) status(_ context.Context, caller api.Caller, _ json.RawMessage) (any, error) {
|
|
62
|
-
return
|
|
72
|
+
return map[string]any{
|
|
63
73
|
"package": cfg.PackageID,
|
|
64
74
|
"version": cfg.Version,
|
|
65
75
|
"user": caller.User(),
|
|
@@ -68,7 +78,7 @@ func (cfg Config) status(_ context.Context, caller api.Caller, _ json.RawMessage
|
|
|
68
78
|
"go_version": runtime.Version(),
|
|
69
79
|
"platform": runtime.GOOS + "/" + runtime.GOARCH,
|
|
70
80
|
"time": time.Now().UTC().Format(time.RFC3339),
|
|
71
|
-
}
|
|
81
|
+
}, nil
|
|
72
82
|
}
|
|
73
83
|
|
|
74
84
|
type greeting struct {
|
|
@@ -107,9 +107,10 @@ var (
|
|
|
107
107
|
var sampleOutcome = map[string]string{}
|
|
108
108
|
|
|
109
109
|
var sampleParams = map[string]string{
|
|
110
|
-
"app.status":
|
|
111
|
-
"
|
|
112
|
-
"greeting.
|
|
110
|
+
"app.status": `{}`,
|
|
111
|
+
"sharedfolder.status": `{}`,
|
|
112
|
+
"greeting.get": `{}`,
|
|
113
|
+
"greeting.set": `{"text":"Hi"}`,
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
func TestEveryRouteHasSampleParamsAndNoneIsPublic(t *testing.T) {
|
|
@@ -1,144 +1,54 @@
|
|
|
1
1
|
package app
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"context"
|
|
4
5
|
"encoding/json"
|
|
5
|
-
"errors"
|
|
6
|
-
"fmt"
|
|
7
|
-
"io/fs"
|
|
8
|
-
"os"
|
|
9
|
-
"path/filepath"
|
|
10
|
-
"sort"
|
|
11
|
-
"strings"
|
|
12
|
-
"syscall"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
// The install wizard stores the chosen Shared Folder's *name* in
|
|
16
|
-
// <DataDir>/config.json (see dsm-package.yaml install.sharedFolder). The path
|
|
17
|
-
// is resolved on every call, so a Shared Folder moved to another volume keeps
|
|
18
|
-
// working. config.json is written by the package's postinst/postupgrade, not
|
|
19
|
-
// by the app.
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ErrSharedFolderAmbiguous = errors.New("shared folder name matches more than one volume")
|
|
7
|
+
"github.com/synopackageland/syno-pkg-land-sdk/api"
|
|
8
|
+
"github.com/synopackageland/syno-pkg-land-sdk/sharedfolder"
|
|
24
9
|
)
|
|
25
10
|
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
func validShareName(name string) bool {
|
|
39
|
-
return name != "" && !strings.ContainsAny(name, "/\\\x00") && !strings.Contains(name, "..") &&
|
|
40
|
-
!strings.HasPrefix(name, "@") && !strings.HasPrefix(name, ".")
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ReadSharedFolders returns the Shared Folder names the installer stored, by
|
|
44
|
-
// key. A package without config.json has none.
|
|
45
|
-
func ReadSharedFolders(dataDir string) (map[string]string, error) {
|
|
46
|
-
data, err := os.ReadFile(filepath.Join(dataDir, "config.json"))
|
|
47
|
-
if errors.Is(err, fs.ErrNotExist) {
|
|
48
|
-
return nil, nil
|
|
49
|
-
}
|
|
50
|
-
if err != nil {
|
|
51
|
-
return nil, err
|
|
52
|
-
}
|
|
53
|
-
var cfg struct {
|
|
54
|
-
Schema int `json:"schema"`
|
|
55
|
-
SharedFolders map[string]string `json:"shared_folders"`
|
|
56
|
-
}
|
|
57
|
-
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
58
|
-
return nil, fmt.Errorf("config.json: %w", err)
|
|
59
|
-
}
|
|
60
|
-
if cfg.Schema != 1 {
|
|
61
|
-
return nil, fmt.Errorf("config.json: unsupported schema %d", cfg.Schema)
|
|
62
|
-
}
|
|
63
|
-
for key, name := range cfg.SharedFolders {
|
|
64
|
-
if !validShareName(name) {
|
|
65
|
-
return nil, fmt.Errorf("config.json: invalid shared folder name for %q", key)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return cfg.SharedFolders, nil
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ResolveSharedFolder finds /volumeN/<name> under root ("/" on a NAS).
|
|
72
|
-
func ResolveSharedFolder(root, name string) (string, error) {
|
|
73
|
-
if !validShareName(name) {
|
|
74
|
-
return "", fmt.Errorf("invalid shared folder name %q", name)
|
|
75
|
-
}
|
|
76
|
-
volumes, err := filepath.Glob(filepath.Join(root, "volume[0-9]*"))
|
|
77
|
-
if err != nil {
|
|
78
|
-
return "", err
|
|
79
|
-
}
|
|
80
|
-
var found []string
|
|
81
|
-
for _, volume := range volumes {
|
|
82
|
-
path := filepath.Join(volume, name)
|
|
83
|
-
if info, err := os.Stat(path); err == nil && info.IsDir() {
|
|
84
|
-
found = append(found, path)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
switch len(found) {
|
|
88
|
-
case 0:
|
|
89
|
-
return "", ErrSharedFolderNotFound
|
|
90
|
-
case 1:
|
|
91
|
-
return found[0], nil
|
|
92
|
-
}
|
|
93
|
-
return "", ErrSharedFolderAmbiguous
|
|
11
|
+
// The install wizard stores the chosen Shared Folder's *name* in
|
|
12
|
+
// <DataDir>/config.json (see dsm-package.yaml install.sharedFolder). DSM does
|
|
13
|
+
// not give the package account access to a folder chosen at install time: an
|
|
14
|
+
// administrator grants it in Control Panel (or with one click in the settings
|
|
15
|
+
// page). The SDK's sharedfolder package measures what the account may do, so
|
|
16
|
+
// this route, the settings page and any daemon read one state.
|
|
17
|
+
|
|
18
|
+
// sharedFolders is the SDK's view of this package's Shared Folders.
|
|
19
|
+
func (cfg Config) sharedFolders() sharedfolder.Config {
|
|
20
|
+
return sharedfolder.Config{DataDir: cfg.DataDir, Root: cfg.VolumeRoot, Required: cfg.SharedFolderRequired}
|
|
94
21
|
}
|
|
95
22
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// error is a corrupt config.json.
|
|
104
|
-
func (cfg Config) SharedFolders() ([]SharedFolder, error) {
|
|
105
|
-
names, err := ReadSharedFolders(cfg.DataDir)
|
|
23
|
+
// sharedFolderStatus answers the settings page: each folder, what the package
|
|
24
|
+
// account has and what the App declared it needs, and whether an
|
|
25
|
+
// administrator still has to grant. Administrators only, as the default
|
|
26
|
+
// permissions say; list the method in internal/api/permissions.json to let
|
|
27
|
+
// every DSM user read it.
|
|
28
|
+
func (cfg Config) sharedFolderStatus(ctx context.Context, _ api.Caller, _ json.RawMessage) (any, error) {
|
|
29
|
+
status, err := cfg.sharedFolders().Status()
|
|
106
30
|
if err != nil {
|
|
107
31
|
return nil, err
|
|
108
32
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
folder := SharedFolder{Key: key, Name: names[key]}
|
|
121
|
-
path, err := ResolveSharedFolder(root, folder.Name)
|
|
122
|
-
if err != nil {
|
|
123
|
-
folder.Error = err.Error()
|
|
124
|
-
} else {
|
|
125
|
-
folder.Path, folder.Exists = path, true
|
|
126
|
-
folder.Readable = syscall.Access(path, accessRead|accessExec) == nil
|
|
127
|
-
folder.Writable = syscall.Access(path, accessWrite|accessExec) == nil
|
|
33
|
+
// The App's own storage follows a changed path (another folder chosen,
|
|
34
|
+
// the share moved to another volume) here, the first time anything reads it.
|
|
35
|
+
if cfg.StorageAdapter != nil {
|
|
36
|
+
for _, folder := range status.Folders {
|
|
37
|
+
if folder.State == sharedfolder.Ready {
|
|
38
|
+
if _, err := cfg.sharedFolders().Sync(folder.Key, func(path string) error {
|
|
39
|
+
return cfg.StorageAdapter(ctx, folder.Key, path)
|
|
40
|
+
}); err != nil {
|
|
41
|
+
return nil, sharedfolder.APIError(err)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
128
44
|
}
|
|
129
|
-
folders = append(folders, folder)
|
|
130
45
|
}
|
|
131
|
-
return
|
|
46
|
+
return status, nil
|
|
132
47
|
}
|
|
133
48
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
status["shared_folders_error"] = err.Error()
|
|
140
|
-
} else if len(folders) > 0 {
|
|
141
|
-
status["shared_folders"] = folders
|
|
142
|
-
}
|
|
143
|
-
return status
|
|
49
|
+
// SharedFolderPath is what a daemon calls before it touches the folder: the
|
|
50
|
+
// path once the account has the declared access, otherwise an error naming
|
|
51
|
+
// what to grant, never a failed file read.
|
|
52
|
+
func (cfg Config) SharedFolderPath(key string) (string, error) {
|
|
53
|
+
return cfg.sharedFolders().Require(key)
|
|
144
54
|
}
|