@x47base/pocketbase-addon 0.1.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/.dockerignore +9 -0
- package/Dockerfile +26 -0
- package/FEATURES.md +32 -0
- package/LICENSE.md +17 -0
- package/MIGRATION.md +49 -0
- package/NOTICE.md +5 -0
- package/README.md +26 -0
- package/adapter.go +31 -0
- package/admin/register.go +60 -0
- package/admin/register_test.go +37 -0
- package/backups/backup_encryption_test.go +82 -0
- package/backups/encryption.go +138 -0
- package/backups/integration_test.go +51 -0
- package/backups/register.go +120 -0
- package/backups/restore.go +119 -0
- package/backups/s3_test.go +52 -0
- package/backups/swap.go +53 -0
- package/backups/swap_test.go +75 -0
- package/backups/upload.go +52 -0
- package/bin/pocketbase-extension.mjs +25 -0
- package/cmd/edge/main.go +123 -0
- package/cmd/import-fork/main.go +37 -0
- package/cmd/loadtest/main.go +61 -0
- package/cmd/loadtest/sandbox.go +73 -0
- package/cmd/loadtest/sandbox_test.go +20 -0
- package/cmd/pocketbase/main.go +78 -0
- package/deploy/README.md +148 -0
- package/deploy/app/hooks/README.md +2 -0
- package/deploy/app/migrations/1789000000_notes.js +16 -0
- package/deploy/app/public/README.md +2 -0
- package/deploy/compose.secrets.yaml +8 -0
- package/deploy/compose.yaml +68 -0
- package/deploy/edge.json +13 -0
- package/edge/gateway.go +295 -0
- package/edge/gateway_test.go +296 -0
- package/edge/openapi.json +1 -0
- package/edge/policy.go +150 -0
- package/features/collection_singleton.go +13 -0
- package/features/collection_singleton_test.go +46 -0
- package/features/dimensions_test.go +65 -0
- package/features/duplicate.go +178 -0
- package/features/duplicate_test.go +128 -0
- package/features/field_color.go +46 -0
- package/features/field_date_only.go +39 -0
- package/features/field_json_schema.go +92 -0
- package/features/field_scalar_extensions_test.go +66 -0
- package/features/files.go +36 -0
- package/features/filter_has_any_test.go +81 -0
- package/features/generate_test.go +72 -0
- package/features/has_any_visibility_test.go +62 -0
- package/features/json.go +50 -0
- package/features/membership.go +64 -0
- package/features/register.go +45 -0
- package/features/schema_test.go +58 -0
- package/features/ui/main.js +133 -0
- package/features/ui/settings.js +31 -0
- package/go.mod +54 -0
- package/go.sum +159 -0
- package/internal/archive/create.go +91 -0
- package/internal/archive/create_test.go +125 -0
- package/internal/archive/extract.go +99 -0
- package/internal/archive/extract_test.go +88 -0
- package/jsvm/binds.go +1273 -0
- package/jsvm/binds_app_reset_test.go +314 -0
- package/jsvm/binds_test.go +1870 -0
- package/jsvm/form_data.go +149 -0
- package/jsvm/form_data_test.go +225 -0
- package/jsvm/internal/types/generated/embed.go +6 -0
- package/jsvm/internal/types/generated/types.d.ts +24820 -0
- package/jsvm/internal/types/types.go +1408 -0
- package/jsvm/jsvm.go +587 -0
- package/jsvm/mapper.go +67 -0
- package/jsvm/mapper_test.go +42 -0
- package/jsvm/pool.go +73 -0
- package/jsvm/program_source_test.go +24 -0
- package/loadtest/loadtest.go +202 -0
- package/loadtest/loadtest_test.go +84 -0
- package/localization/README.md +23 -0
- package/localization/catalogue.json +483 -0
- package/localization/localization.go +94 -0
- package/localization/localization_test.go +21 -0
- package/mail/register.go +80 -0
- package/mail/register_test.go +49 -0
- package/mail/resolve.go +91 -0
- package/migration/import.go +96 -0
- package/migration/import_test.go +58 -0
- package/otp/otp.go +56 -0
- package/otp/otp_test.go +56 -0
- package/package.json +51 -0
- package/scripts/check-edge.py +42 -0
- package/scripts/check.sh +11 -0
- package/scripts/sync-jsvm-types.sh +10 -0
- package/security/README.md +94 -0
- package/security/assurance_test.go +149 -0
- package/security/compatibility_test.go +128 -0
- package/security/config.go +78 -0
- package/security/dashboard_test.go +103 -0
- package/security/management.go +169 -0
- package/security/openapi.json +508 -0
- package/security/review.go +34 -0
- package/security/security.go +503 -0
- package/security/security_test.go +146 -0
- package/security/state.go +116 -0
- package/security/ui/dashboard.css +4 -0
- package/security/ui/dashboard.js +83 -0
- package/security/ui/main.js +15 -0
- package/security/ui/model.js +32 -0
- package/security/ui/model.test.mjs +25 -0
- package/security/ui/registration.test.mjs +10 -0
- package/settings/env_test.go +41 -0
- package/settings/openapi.json +193 -0
- package/settings/settings.go +155 -0
- package/settings/settings_test.go +31 -0
- package/watcher/watcher.go +192 -0
- package/watcher/watcher_test.go +200 -0
- package/web/static.go +99 -0
- package/web/static_test.go +48 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
package archive
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"archive/zip"
|
|
5
|
+
"context"
|
|
6
|
+
"fmt"
|
|
7
|
+
"io"
|
|
8
|
+
"os"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"strings"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// Extract extracts the zip archive at "src" to "dest".
|
|
14
|
+
//
|
|
15
|
+
// Note that only dirs and regular files will be extracted.
|
|
16
|
+
// Symbolic links, named pipes, sockets, or any other irregular files
|
|
17
|
+
// are skipped because they come with too many edge cases and ambiguities.
|
|
18
|
+
func Extract(src, dest string) error { return ExtractContext(context.Background(), src, dest) }
|
|
19
|
+
|
|
20
|
+
func ExtractContext(ctx context.Context, src, dest string) error {
|
|
21
|
+
zr, err := zip.OpenReader(src)
|
|
22
|
+
if err != nil {
|
|
23
|
+
return err
|
|
24
|
+
}
|
|
25
|
+
defer zr.Close()
|
|
26
|
+
|
|
27
|
+
// normalize dest path to check later for Zip Slip
|
|
28
|
+
dest = filepath.Clean(dest) + string(os.PathSeparator)
|
|
29
|
+
|
|
30
|
+
for _, f := range zr.File {
|
|
31
|
+
if err := ctx.Err(); err != nil {
|
|
32
|
+
return err
|
|
33
|
+
}
|
|
34
|
+
err := extractFileContext(ctx, f, dest)
|
|
35
|
+
if err != nil {
|
|
36
|
+
return err
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return nil
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// extractFile extracts the provided zipFile into "basePath/zipFileName" path,
|
|
44
|
+
// creating all the necessary path directories.
|
|
45
|
+
func extractFile(zipFile *zip.File, basePath string) error {
|
|
46
|
+
return extractFileContext(context.Background(), zipFile, basePath)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func extractFileContext(ctx context.Context, zipFile *zip.File, basePath string) error {
|
|
50
|
+
path := filepath.Join(basePath, zipFile.Name)
|
|
51
|
+
|
|
52
|
+
// check for Zip Slip
|
|
53
|
+
if !strings.HasPrefix(path, basePath) {
|
|
54
|
+
return fmt.Errorf("invalid file path: %s", path)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
r, err := zipFile.Open()
|
|
58
|
+
if err != nil {
|
|
59
|
+
return err
|
|
60
|
+
}
|
|
61
|
+
defer r.Close()
|
|
62
|
+
|
|
63
|
+
// allow only dirs or regular files
|
|
64
|
+
if zipFile.FileInfo().IsDir() {
|
|
65
|
+
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
|
66
|
+
return err
|
|
67
|
+
}
|
|
68
|
+
} else if zipFile.FileInfo().Mode().IsRegular() {
|
|
69
|
+
// ensure that the file path directories are created
|
|
70
|
+
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
|
71
|
+
return err
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, zipFile.Mode())
|
|
75
|
+
if err != nil {
|
|
76
|
+
return err
|
|
77
|
+
}
|
|
78
|
+
defer f.Close()
|
|
79
|
+
|
|
80
|
+
_, err = io.Copy(f, contextReader{ctx, r})
|
|
81
|
+
if err != nil {
|
|
82
|
+
return err
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return nil
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type contextReader struct {
|
|
90
|
+
ctx context.Context
|
|
91
|
+
reader io.Reader
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func (r contextReader) Read(p []byte) (int, error) {
|
|
95
|
+
if err := r.ctx.Err(); err != nil {
|
|
96
|
+
return 0, err
|
|
97
|
+
}
|
|
98
|
+
return r.reader.Read(p)
|
|
99
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
package archive_test
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"io/fs"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
|
|
9
|
+
"github.com/spink-dev/pocketbase-extension/internal/archive"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func TestExtractFailure(t *testing.T) {
|
|
13
|
+
testDir := createTestDir(t)
|
|
14
|
+
defer os.RemoveAll(testDir)
|
|
15
|
+
|
|
16
|
+
missingZipPath := filepath.Join(os.TempDir(), "pb_missing_test.zip")
|
|
17
|
+
extractedPath := filepath.Join(os.TempDir(), "pb_zip_extract")
|
|
18
|
+
defer os.RemoveAll(extractedPath)
|
|
19
|
+
|
|
20
|
+
if err := archive.Extract(missingZipPath, extractedPath); err == nil {
|
|
21
|
+
t.Fatal("Expected Extract to fail due to missing zipPath")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if _, err := os.Stat(extractedPath); err == nil {
|
|
25
|
+
t.Fatalf("Expected %q to not be created", extractedPath)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func TestExtractSuccess(t *testing.T) {
|
|
30
|
+
testDir := createTestDir(t)
|
|
31
|
+
defer os.RemoveAll(testDir)
|
|
32
|
+
|
|
33
|
+
zipPath := filepath.Join(os.TempDir(), "pb_test.zip")
|
|
34
|
+
defer os.RemoveAll(zipPath)
|
|
35
|
+
|
|
36
|
+
extractedPath := filepath.Join(os.TempDir(), "pb_zip_extract")
|
|
37
|
+
defer os.RemoveAll(extractedPath)
|
|
38
|
+
|
|
39
|
+
// zip testDir content (with exclude)
|
|
40
|
+
if err := archive.Create(testDir, zipPath, "a/b/c", "test2", "sub2"); err != nil {
|
|
41
|
+
t.Fatalf("Failed to create archive: %v", err)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if err := archive.Extract(zipPath, extractedPath); err != nil {
|
|
45
|
+
t.Fatalf("Failed to extract %q in %q", zipPath, extractedPath)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
availableFiles := []string{}
|
|
49
|
+
|
|
50
|
+
walkErr := filepath.WalkDir(extractedPath, func(path string, d fs.DirEntry, err error) error {
|
|
51
|
+
if err != nil {
|
|
52
|
+
return err
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if d.IsDir() {
|
|
56
|
+
return nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
availableFiles = append(availableFiles, path)
|
|
60
|
+
|
|
61
|
+
return nil
|
|
62
|
+
})
|
|
63
|
+
if walkErr != nil {
|
|
64
|
+
t.Fatalf("Failed to read the extracted dir: %v", walkErr)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// (note: symbolic links and other regular files should be missing)
|
|
68
|
+
expectedFiles := []string{
|
|
69
|
+
filepath.Join(extractedPath, "test"),
|
|
70
|
+
filepath.Join(extractedPath, "a/test"),
|
|
71
|
+
filepath.Join(extractedPath, "a/b/sub1"),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if len(availableFiles) != len(expectedFiles) {
|
|
75
|
+
t.Fatalf("Expected \n%v, \ngot \n%v", expectedFiles, availableFiles)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ExpectedLoop:
|
|
79
|
+
for _, expected := range expectedFiles {
|
|
80
|
+
for _, available := range availableFiles {
|
|
81
|
+
if available == expected {
|
|
82
|
+
continue ExpectedLoop
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
t.Fatalf("Missing file %q in \n%v", expected, availableFiles)
|
|
87
|
+
}
|
|
88
|
+
}
|