@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.
Files changed (117) hide show
  1. package/.dockerignore +9 -0
  2. package/Dockerfile +26 -0
  3. package/FEATURES.md +32 -0
  4. package/LICENSE.md +17 -0
  5. package/MIGRATION.md +49 -0
  6. package/NOTICE.md +5 -0
  7. package/README.md +26 -0
  8. package/adapter.go +31 -0
  9. package/admin/register.go +60 -0
  10. package/admin/register_test.go +37 -0
  11. package/backups/backup_encryption_test.go +82 -0
  12. package/backups/encryption.go +138 -0
  13. package/backups/integration_test.go +51 -0
  14. package/backups/register.go +120 -0
  15. package/backups/restore.go +119 -0
  16. package/backups/s3_test.go +52 -0
  17. package/backups/swap.go +53 -0
  18. package/backups/swap_test.go +75 -0
  19. package/backups/upload.go +52 -0
  20. package/bin/pocketbase-extension.mjs +25 -0
  21. package/cmd/edge/main.go +123 -0
  22. package/cmd/import-fork/main.go +37 -0
  23. package/cmd/loadtest/main.go +61 -0
  24. package/cmd/loadtest/sandbox.go +73 -0
  25. package/cmd/loadtest/sandbox_test.go +20 -0
  26. package/cmd/pocketbase/main.go +78 -0
  27. package/deploy/README.md +148 -0
  28. package/deploy/app/hooks/README.md +2 -0
  29. package/deploy/app/migrations/1789000000_notes.js +16 -0
  30. package/deploy/app/public/README.md +2 -0
  31. package/deploy/compose.secrets.yaml +8 -0
  32. package/deploy/compose.yaml +68 -0
  33. package/deploy/edge.json +13 -0
  34. package/edge/gateway.go +295 -0
  35. package/edge/gateway_test.go +296 -0
  36. package/edge/openapi.json +1 -0
  37. package/edge/policy.go +150 -0
  38. package/features/collection_singleton.go +13 -0
  39. package/features/collection_singleton_test.go +46 -0
  40. package/features/dimensions_test.go +65 -0
  41. package/features/duplicate.go +178 -0
  42. package/features/duplicate_test.go +128 -0
  43. package/features/field_color.go +46 -0
  44. package/features/field_date_only.go +39 -0
  45. package/features/field_json_schema.go +92 -0
  46. package/features/field_scalar_extensions_test.go +66 -0
  47. package/features/files.go +36 -0
  48. package/features/filter_has_any_test.go +81 -0
  49. package/features/generate_test.go +72 -0
  50. package/features/has_any_visibility_test.go +62 -0
  51. package/features/json.go +50 -0
  52. package/features/membership.go +64 -0
  53. package/features/register.go +45 -0
  54. package/features/schema_test.go +58 -0
  55. package/features/ui/main.js +133 -0
  56. package/features/ui/settings.js +31 -0
  57. package/go.mod +54 -0
  58. package/go.sum +159 -0
  59. package/internal/archive/create.go +91 -0
  60. package/internal/archive/create_test.go +125 -0
  61. package/internal/archive/extract.go +99 -0
  62. package/internal/archive/extract_test.go +88 -0
  63. package/jsvm/binds.go +1273 -0
  64. package/jsvm/binds_app_reset_test.go +314 -0
  65. package/jsvm/binds_test.go +1870 -0
  66. package/jsvm/form_data.go +149 -0
  67. package/jsvm/form_data_test.go +225 -0
  68. package/jsvm/internal/types/generated/embed.go +6 -0
  69. package/jsvm/internal/types/generated/types.d.ts +24820 -0
  70. package/jsvm/internal/types/types.go +1408 -0
  71. package/jsvm/jsvm.go +587 -0
  72. package/jsvm/mapper.go +67 -0
  73. package/jsvm/mapper_test.go +42 -0
  74. package/jsvm/pool.go +73 -0
  75. package/jsvm/program_source_test.go +24 -0
  76. package/loadtest/loadtest.go +202 -0
  77. package/loadtest/loadtest_test.go +84 -0
  78. package/localization/README.md +23 -0
  79. package/localization/catalogue.json +483 -0
  80. package/localization/localization.go +94 -0
  81. package/localization/localization_test.go +21 -0
  82. package/mail/register.go +80 -0
  83. package/mail/register_test.go +49 -0
  84. package/mail/resolve.go +91 -0
  85. package/migration/import.go +96 -0
  86. package/migration/import_test.go +58 -0
  87. package/otp/otp.go +56 -0
  88. package/otp/otp_test.go +56 -0
  89. package/package.json +51 -0
  90. package/scripts/check-edge.py +42 -0
  91. package/scripts/check.sh +11 -0
  92. package/scripts/sync-jsvm-types.sh +10 -0
  93. package/security/README.md +94 -0
  94. package/security/assurance_test.go +149 -0
  95. package/security/compatibility_test.go +128 -0
  96. package/security/config.go +78 -0
  97. package/security/dashboard_test.go +103 -0
  98. package/security/management.go +169 -0
  99. package/security/openapi.json +508 -0
  100. package/security/review.go +34 -0
  101. package/security/security.go +503 -0
  102. package/security/security_test.go +146 -0
  103. package/security/state.go +116 -0
  104. package/security/ui/dashboard.css +4 -0
  105. package/security/ui/dashboard.js +83 -0
  106. package/security/ui/main.js +15 -0
  107. package/security/ui/model.js +32 -0
  108. package/security/ui/model.test.mjs +25 -0
  109. package/security/ui/registration.test.mjs +10 -0
  110. package/settings/env_test.go +41 -0
  111. package/settings/openapi.json +193 -0
  112. package/settings/settings.go +155 -0
  113. package/settings/settings_test.go +31 -0
  114. package/watcher/watcher.go +192 -0
  115. package/watcher/watcher_test.go +200 -0
  116. package/web/static.go +99 -0
  117. package/web/static_test.go +48 -0
@@ -0,0 +1,508 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "PocketBase adapter security",
5
+ "version": "0.1.0"
6
+ },
7
+ "servers": [
8
+ {
9
+ "url": "/api/security"
10
+ }
11
+ ],
12
+ "security": [
13
+ {
14
+ "superuserToken": []
15
+ }
16
+ ],
17
+ "components": {
18
+ "securitySchemes": {
19
+ "superuserToken": {
20
+ "type": "apiKey",
21
+ "in": "header",
22
+ "name": "Authorization",
23
+ "description": "PocketBase superuser token; immediate peer must also belong to configured management networks."
24
+ }
25
+ },
26
+ "schemas": {
27
+ "Policy": {
28
+ "type": "object",
29
+ "required": [
30
+ "mode",
31
+ "requestsPerSecond",
32
+ "burst",
33
+ "identityPerSecond",
34
+ "identityBurst",
35
+ "maxConcurrent",
36
+ "maxWrites",
37
+ "maxFiles",
38
+ "maxRealtime",
39
+ "maxSubscriptions",
40
+ "maxSubscriptionsTotal",
41
+ "maxIdentities"
42
+ ],
43
+ "properties": {
44
+ "mode": {
45
+ "type": "string",
46
+ "enum": [
47
+ "disabled",
48
+ "observe",
49
+ "enforce"
50
+ ]
51
+ },
52
+ "requestsPerSecond": {
53
+ "type": "number",
54
+ "minimum": 0.001,
55
+ "maximum": 100000
56
+ },
57
+ "burst": {
58
+ "type": "integer",
59
+ "minimum": 1,
60
+ "maximum": 50000
61
+ },
62
+ "identityPerSecond": {
63
+ "type": "number",
64
+ "minimum": 0.001,
65
+ "maximum": 100000
66
+ },
67
+ "identityBurst": {
68
+ "type": "integer",
69
+ "minimum": 1,
70
+ "maximum": 50000
71
+ },
72
+ "maxConcurrent": {
73
+ "type": "integer",
74
+ "minimum": 1,
75
+ "maximum": 50000
76
+ },
77
+ "maxWrites": {
78
+ "type": "integer",
79
+ "minimum": 1,
80
+ "maximum": 50000
81
+ },
82
+ "maxFiles": {
83
+ "type": "integer",
84
+ "minimum": 1,
85
+ "maximum": 50000
86
+ },
87
+ "maxRealtime": {
88
+ "type": "integer",
89
+ "minimum": 1,
90
+ "maximum": 50000
91
+ },
92
+ "maxSubscriptions": {
93
+ "type": "integer",
94
+ "minimum": 1,
95
+ "maximum": 50000
96
+ },
97
+ "maxSubscriptionsTotal": {
98
+ "type": "integer",
99
+ "minimum": 1,
100
+ "maximum": 50000
101
+ },
102
+ "maxIdentities": {
103
+ "type": "integer",
104
+ "minimum": 1,
105
+ "maximum": 50000
106
+ },
107
+ "trustedPeers": {
108
+ "type": "array",
109
+ "maxItems": 64,
110
+ "items": {
111
+ "type": "string",
112
+ "description": "IPv4 or IPv6 CIDR"
113
+ }
114
+ },
115
+ "managementPeers": {
116
+ "type": "array",
117
+ "maxItems": 64,
118
+ "items": {
119
+ "type": "string",
120
+ "description": "IPv4 or IPv6 CIDR"
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "Action": {
126
+ "type": "object",
127
+ "required": [
128
+ "family",
129
+ "reason",
130
+ "expiresAt"
131
+ ],
132
+ "properties": {
133
+ "family": {
134
+ "type": "string",
135
+ "enum": [
136
+ "auth",
137
+ "reads",
138
+ "writes",
139
+ "files",
140
+ "realtime",
141
+ "other"
142
+ ]
143
+ },
144
+ "reason": {
145
+ "type": "string",
146
+ "minLength": 1,
147
+ "maxLength": 200
148
+ },
149
+ "expiresAt": {
150
+ "type": "string",
151
+ "format": "date-time",
152
+ "description": "Must be within the next 15 minutes"
153
+ }
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "paths": {
159
+ "/status": {
160
+ "get": {
161
+ "summary": "Process counters, bounded traffic history, checkpoint health and unexpired actions",
162
+ "responses": {
163
+ "200": {
164
+ "description": "Success",
165
+ "content": {
166
+ "application/json": {
167
+ "schema": {
168
+ "type": "object",
169
+ "properties": {
170
+ "mode": {
171
+ "type": "string",
172
+ "enum": [
173
+ "disabled",
174
+ "observe",
175
+ "enforce"
176
+ ]
177
+ },
178
+ "revision": {
179
+ "type": "integer"
180
+ },
181
+ "observed": {
182
+ "type": "integer"
183
+ },
184
+ "rejected": {
185
+ "type": "integer"
186
+ },
187
+ "storageDegraded": {
188
+ "type": "boolean"
189
+ },
190
+ "checkpointEnabled": {
191
+ "type": "boolean"
192
+ },
193
+ "startedAt": {
194
+ "type": "string",
195
+ "format": "date-time"
196
+ },
197
+ "lastEvaluated": {
198
+ "type": "string",
199
+ "format": "date-time"
200
+ },
201
+ "traffic": {
202
+ "type": "array",
203
+ "maxItems": 60,
204
+ "items": {
205
+ "type": "object",
206
+ "properties": {
207
+ "count": {
208
+ "type": "integer"
209
+ },
210
+ "rejected": {
211
+ "type": "integer"
212
+ },
213
+ "failures": {
214
+ "type": "integer"
215
+ },
216
+ "slow": {
217
+ "type": "integer"
218
+ }
219
+ }
220
+ }
221
+ },
222
+ "actions": {
223
+ "type": "object",
224
+ "additionalProperties": {
225
+ "$ref": "#/components/schemas/Action"
226
+ }
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ },
233
+ "401": {
234
+ "description": "Authentication required"
235
+ },
236
+ "403": {
237
+ "description": "Superuser or management network required"
238
+ }
239
+ }
240
+ }
241
+ },
242
+ "/policy/snapshot": {
243
+ "get": {
244
+ "summary": "Atomic policy and revision",
245
+ "responses": {
246
+ "200": {
247
+ "description": "Success",
248
+ "content": {
249
+ "application/json": {
250
+ "schema": {
251
+ "type": "object",
252
+ "properties": {
253
+ "revision": {
254
+ "type": "integer"
255
+ },
256
+ "policy": {
257
+ "$ref": "#/components/schemas/Policy"
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ },
264
+ "401": {
265
+ "description": "Authentication required"
266
+ },
267
+ "403": {
268
+ "description": "Superuser or management network required"
269
+ }
270
+ }
271
+ }
272
+ },
273
+ "/policy": {
274
+ "get": {
275
+ "summary": "Policy with numeric ETag",
276
+ "responses": {
277
+ "200": {
278
+ "description": "Success",
279
+ "content": {
280
+ "application/json": {
281
+ "schema": {
282
+ "$ref": "#/components/schemas/Policy"
283
+ }
284
+ }
285
+ }
286
+ },
287
+ "401": {
288
+ "description": "Authentication required"
289
+ },
290
+ "403": {
291
+ "description": "Superuser or management network required"
292
+ }
293
+ }
294
+ },
295
+ "put": {
296
+ "summary": "Replace policy using current numeric If-Match revision",
297
+ "responses": {
298
+ "200": {
299
+ "description": "Success",
300
+ "content": {
301
+ "application/json": {
302
+ "schema": {
303
+ "type": "object",
304
+ "properties": {
305
+ "revision": {
306
+ "type": "integer"
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ },
313
+ "401": {
314
+ "description": "Authentication required"
315
+ },
316
+ "403": {
317
+ "description": "Superuser or management network required"
318
+ },
319
+ "409": {
320
+ "description": "Stale revision; draft must not be silently overwritten"
321
+ },
322
+ "400": {
323
+ "description": "Invalid policy"
324
+ }
325
+ },
326
+ "requestBody": {
327
+ "required": true,
328
+ "content": {
329
+ "application/json": {
330
+ "schema": {
331
+ "$ref": "#/components/schemas/Policy"
332
+ }
333
+ }
334
+ }
335
+ },
336
+ "parameters": [
337
+ {
338
+ "name": "If-Match",
339
+ "in": "header",
340
+ "required": true,
341
+ "schema": {
342
+ "type": "string"
343
+ }
344
+ }
345
+ ]
346
+ }
347
+ },
348
+ "/incidents": {
349
+ "get": {
350
+ "summary": "Bounded ascending incident history",
351
+ "responses": {
352
+ "200": {
353
+ "description": "Success",
354
+ "content": {
355
+ "application/json": {
356
+ "schema": {
357
+ "type": "object",
358
+ "properties": {
359
+ "items": {
360
+ "type": "array",
361
+ "items": {
362
+ "type": "object"
363
+ }
364
+ },
365
+ "nextCursor": {
366
+ "type": "integer",
367
+ "description": "Zero at end"
368
+ }
369
+ }
370
+ }
371
+ }
372
+ }
373
+ },
374
+ "401": {
375
+ "description": "Authentication required"
376
+ },
377
+ "403": {
378
+ "description": "Superuser or management network required"
379
+ }
380
+ },
381
+ "parameters": [
382
+ {
383
+ "name": "limit",
384
+ "in": "query",
385
+ "schema": {
386
+ "type": "integer",
387
+ "minimum": 1,
388
+ "maximum": 100,
389
+ "default": 50
390
+ }
391
+ },
392
+ {
393
+ "name": "after",
394
+ "in": "query",
395
+ "schema": {
396
+ "type": "integer",
397
+ "minimum": 0
398
+ }
399
+ }
400
+ ]
401
+ }
402
+ },
403
+ "/incidents/{id}/acknowledge": {
404
+ "post": {
405
+ "summary": "Idempotent review acknowledgement",
406
+ "responses": {
407
+ "204": {
408
+ "description": "Success"
409
+ },
410
+ "401": {
411
+ "description": "Authentication required"
412
+ },
413
+ "403": {
414
+ "description": "Superuser or management network required"
415
+ }
416
+ },
417
+ "parameters": [
418
+ {
419
+ "name": "id",
420
+ "in": "path",
421
+ "required": true,
422
+ "schema": {
423
+ "type": "integer"
424
+ }
425
+ }
426
+ ]
427
+ }
428
+ },
429
+ "/actions": {
430
+ "post": {
431
+ "summary": "Set or replace an expiring family block",
432
+ "responses": {
433
+ "200": {
434
+ "description": "Success",
435
+ "content": {
436
+ "application/json": {
437
+ "schema": {
438
+ "$ref": "#/components/schemas/Action"
439
+ }
440
+ }
441
+ }
442
+ },
443
+ "401": {
444
+ "description": "Authentication required"
445
+ },
446
+ "403": {
447
+ "description": "Superuser or management network required"
448
+ }
449
+ },
450
+ "requestBody": {
451
+ "required": true,
452
+ "content": {
453
+ "application/json": {
454
+ "schema": {
455
+ "$ref": "#/components/schemas/Action"
456
+ }
457
+ }
458
+ }
459
+ }
460
+ }
461
+ },
462
+ "/actions/{family}": {
463
+ "delete": {
464
+ "summary": "Cancel a family block",
465
+ "responses": {
466
+ "204": {
467
+ "description": "Success"
468
+ },
469
+ "401": {
470
+ "description": "Authentication required"
471
+ },
472
+ "403": {
473
+ "description": "Superuser or management network required"
474
+ }
475
+ },
476
+ "parameters": [
477
+ {
478
+ "name": "family",
479
+ "in": "path",
480
+ "required": true,
481
+ "schema": {
482
+ "type": "string",
483
+ "enum": [
484
+ "auth",
485
+ "reads",
486
+ "writes",
487
+ "files",
488
+ "realtime",
489
+ "other"
490
+ ]
491
+ }
492
+ }
493
+ ]
494
+ }
495
+ },
496
+ "/openapi.json": {
497
+ "get": {
498
+ "summary": "Public API schema, no instance data",
499
+ "security": [],
500
+ "responses": {
501
+ "200": {
502
+ "description": "OpenAPI document"
503
+ }
504
+ }
505
+ }
506
+ }
507
+ }
508
+ }
@@ -0,0 +1,34 @@
1
+ package security
2
+
3
+ import (
4
+ "embed"
5
+ "github.com/pocketbase/pocketbase/core"
6
+ "github.com/pocketbase/pocketbase/tools/router"
7
+ "io/fs"
8
+ "strings"
9
+ )
10
+
11
+ //go:embed ui/*.js ui/*.css
12
+ var dashboardBundle embed.FS
13
+
14
+ //go:embed openapi.json
15
+ var apiContract []byte
16
+ var dashboardFiles fs.FS = func() fs.FS {
17
+ f, err := fs.Sub(dashboardBundle, "ui")
18
+ if err != nil {
19
+ panic(err)
20
+ }
21
+ return f
22
+ }()
23
+
24
+ func (s *Service) bindReview(r *router.Router[*core.RequestEvent]) {
25
+ r.BindFunc(func(e *core.RequestEvent) error {
26
+ path := e.Request.URL.Path
27
+ if path == "/_/extensions.js" || strings.HasPrefix(path, "/_/extensions/security/") || strings.HasPrefix(path, "/_/extensions/adapter-features/") {
28
+ e.Response.Header().Set("Cache-Control", "no-store")
29
+ }
30
+ return e.Next()
31
+ })
32
+ r.GET("/api/security/openapi.json", func(e *core.RequestEvent) error { return e.Blob(200, "application/json", apiContract) })
33
+ r.GET("/_security/", func(e *core.RequestEvent) error { return e.Redirect(307, "/_/#/security") })
34
+ }