diff --git a/.env.dist b/.env.dist
new file mode 100644
index 0000000..660424e
--- /dev/null
+++ b/.env.dist
@@ -0,0 +1 @@
+RUN_APP_ARGS=""
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 289dff7..778275d 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,9 @@ ESBUILD_VERSION ?= v0.17.5
GIT_VERSION := $(shell git describe --always)
DATE_VERSION := $(shell date +%Y.%-m.%-d)
FULL_VERSION := v$(DATE_VERSION)-$(GIT_VERSION)$(if $(shell git diff --stat),-dirty,)
+APP_PATH ?= misc/client-sdk-testsuite/dist
+RUN_APP_ARGS ?=
+SHELL := bash
build: build-edge-cli build-client-sdk-test-app
@@ -55,12 +58,18 @@ pkg/sdk/client/dist/client.js: tools/esbuild/bin/esbuild node_modules
--global-name=Edge \
--define:global=window \
--platform=browser \
- --footer:js="EdgeFrame=Edge.crossFrameMessenger;Edge=Edge.client" \
+ --loader:.svg=dataurl \
--outfile=pkg/sdk/client/dist/client.js
node_modules:
npm ci
+run-app: .env
+ ( set -o allexport && source .env && set +o allexport && bin/cli app run -p $(APP_PATH) $$RUN_APP_ARGS )
+
+.env:
+ cp .env.dist .env
+
gitea-release: tools/yq/bin/yq tools/gitea-release/bin/gitea-release.sh build
mkdir -p .gitea-release
rm -rf .gitea-release/*
diff --git a/cmd/cli/command/app/default-accounts.json b/cmd/cli/command/app/default-accounts.json
index 39aa79d..a18e6e0 100644
--- a/cmd/cli/command/app/default-accounts.json
+++ b/cmd/cli/command/app/default-accounts.json
@@ -4,9 +4,9 @@
"algo": "argon2id",
"password": "$argon2id$v=19$m=65536,t=3,p=2$cWOxfEyBy4EyKZR5usB2Pw$xG+Z/E2DUJP9kF0s1fhZjIuP03gFQ65dP7pHRJz7eR8",
"claims": {
- "arcad_entrypoint": "edge",
- "arcad_role": "superadmin",
- "arcad_tenant": "dev.cli",
+ "edge_entrypoint": "edge",
+ "edge_role": "superadmin",
+ "edge_tenant": "dev.cli",
"preferred_username": "SuperAdmin",
"sub": "superadmin"
}
@@ -16,9 +16,9 @@
"algo": "argon2id",
"password": "$argon2id$v=19$m=65536,t=3,p=2$WXXc4ECnkej6WO7f0Xya6Q$UG2wcGltJcuW0cNTR85mAl65tI1kFWMMw7ADS2FMOvY",
"claims": {
- "arcad_entrypoint": "edge",
- "arcad_role": "admin",
- "arcad_tenant": "dev.cli",
+ "edge_entrypoint": "edge",
+ "edge_role": "admin",
+ "edge_tenant": "dev.cli",
"preferred_username": "Admin",
"sub": "admin"
}
@@ -28,9 +28,9 @@
"algo": "argon2id",
"password": "$argon2id$v=19$m=65536,t=3,p=2$gkDAWCzfU23+un3x0ny+YA$L/NSPrd5iKPK/UnSCKfSz4EO+v94N3LTLky4QGJOfpI",
"claims": {
- "arcad_entrypoint": "edge",
- "arcad_role": "superuser",
- "arcad_tenant": "dev.cli",
+ "edge_entrypoint": "edge",
+ "edge_role": "superuser",
+ "edge_tenant": "dev.cli",
"preferred_username": "SuperUser",
"sub": "superuser"
}
@@ -40,9 +40,9 @@
"algo": "argon2id",
"password": "$argon2id$v=19$m=65536,t=3,p=2$DhUm9qXUKP35Lzp5M37eZA$2+h6yDxSTHZqFZIuI7JZfFWozwrObna8a8yCgEEPlPE",
"claims": {
- "arcad_entrypoint": "edge",
- "arcad_role": "user",
- "arcad_tenant": "dev.cli",
+ "edge_entrypoint": "edge",
+ "edge_role": "user",
+ "edge_tenant": "dev.cli",
"preferred_username": "User",
"sub": "user"
}
diff --git a/cmd/cli/command/app/run.go b/cmd/cli/command/app/run.go
index 9fe5169..a03c4a9 100644
--- a/cmd/cli/command/app/run.go
+++ b/cmd/cli/command/app/run.go
@@ -9,7 +9,9 @@ import (
"net/http"
"os"
"path/filepath"
+ "strconv"
"strings"
+ "sync"
"forge.cadoles.com/arcad/edge/pkg/app"
"forge.cadoles.com/arcad/edge/pkg/bus"
@@ -18,7 +20,7 @@ import (
"forge.cadoles.com/arcad/edge/pkg/module"
appModule "forge.cadoles.com/arcad/edge/pkg/module/app"
appModuleMemory "forge.cadoles.com/arcad/edge/pkg/module/app/memory"
- "forge.cadoles.com/arcad/edge/pkg/module/auth"
+ authModule "forge.cadoles.com/arcad/edge/pkg/module/auth"
authHTTP "forge.cadoles.com/arcad/edge/pkg/module/auth/http"
"forge.cadoles.com/arcad/edge/pkg/module/blob"
"forge.cadoles.com/arcad/edge/pkg/module/cast"
@@ -29,7 +31,6 @@ import (
"gitlab.com/wpetit/goweb/logger"
"forge.cadoles.com/arcad/edge/pkg/bundle"
- "github.com/dop251/goja"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/lestrrat-go/jwx/v2/jwa"
@@ -48,15 +49,15 @@ func RunCommand() *cli.Command {
Name: "run",
Usage: "Run the specified app bundle",
Flags: []cli.Flag{
- &cli.StringFlag{
+ &cli.StringSliceFlag{
Name: "path",
Usage: "use `PATH` as app bundle (zipped bundle or directory)",
Aliases: []string{"p"},
- Value: ".",
+ Value: cli.NewStringSlice("."),
},
&cli.StringFlag{
Name: "address",
- Usage: "use `ADDRESS` as http server listening address",
+ Usage: "use `ADDRESS` as http server base listening address",
Aliases: []string{"a"},
Value: ":8080",
},
@@ -83,96 +84,157 @@ func RunCommand() *cli.Command {
},
Action: func(ctx *cli.Context) error {
address := ctx.String("address")
- path := ctx.String("path")
+ paths := ctx.StringSlice("path")
logFormat := ctx.String("log-format")
logLevel := ctx.Int("log-level")
+ storageFile := ctx.String("storage-file")
+ accountsFile := ctx.String("accounts-file")
logger.SetFormat(logger.Format(logFormat))
logger.SetLevel(logger.Level(logLevel))
cmdCtx := ctx.Context
- absPath, err := filepath.Abs(path)
- if err != nil {
- return errors.Wrapf(err, "could not resolve path '%s'", path)
- }
-
- logger.Info(cmdCtx, "opening app bundle", logger.F("path", absPath))
-
- bundle, err := bundle.FromPath(path)
- if err != nil {
- return errors.Wrapf(err, "could not open path '%s' as an app bundle", path)
- }
-
- manifest, err := app.LoadManifest(bundle)
- if err != nil {
- return errors.Wrap(err, "could not load manifest from app bundle")
- }
-
- if valid, err := manifest.Validate(manifestMetadataValidators...); !valid {
- return errors.Wrap(err, "invalid app manifest")
- }
-
- storageFile := injectAppID(ctx.String("storage-file"), manifest.ID)
-
- if err := ensureDir(storageFile); err != nil {
- return errors.WithStack(err)
- }
-
- db, err := sqlite.Open(storageFile)
+ host, portStr, err := net.SplitHostPort(address)
if err != nil {
return errors.WithStack(err)
}
- ds := sqlite.NewDocumentStoreWithDB(db)
- bs := sqlite.NewBlobStoreWithDB(db)
- bus := memory.NewBus()
-
- handler := appHTTP.NewHandler(
- appHTTP.WithBus(bus),
- appHTTP.WithServerModules(getServerModules(bus, ds, bs, manifest, address)...),
- )
- if err := handler.Load(bundle); err != nil {
- return errors.Wrap(err, "could not load app bundle")
- }
-
- router := chi.NewRouter()
- router.Use(middleware.Logger)
-
- accountsFile := injectAppID(ctx.String("accounts-file"), manifest.ID)
-
- accounts, err := loadLocalAccounts(accountsFile)
- if err != nil {
- return errors.Wrap(err, "could not load local accounts")
- }
-
- // Add auth handler
- key, err := dummyKey()
+ port, err := strconv.ParseUint(portStr, 10, 32)
if err != nil {
return errors.WithStack(err)
}
- router.Handle("/auth/*", authHTTP.NewLocalHandler(
- jwa.HS256, key,
- authHTTP.WithRoutePrefix("/auth"),
- authHTTP.WithAccounts(accounts...),
- ))
- // Add app handler
- router.Handle("/*", handler)
+ manifests := make([]*app.Manifest, len(paths))
+ for idx, pth := range paths {
+ bdl, err := bundle.FromPath(pth)
+ if err != nil {
+ return errors.WithStack(err)
+ }
- logger.Info(cmdCtx, "listening", logger.F("address", address))
+ manifest, err := app.LoadManifest(bdl)
+ if err != nil {
+ return errors.WithStack(err)
+ }
- if err := http.ListenAndServe(address, router); err != nil {
- return errors.WithStack(err)
+ manifests[idx] = manifest
}
+ var wg sync.WaitGroup
+
+ for idx, p := range paths {
+ wg.Add(1)
+
+ go func(path string, basePort uint64, appIndex int) {
+ defer wg.Done()
+
+ port := basePort + uint64(appIndex)
+ address := fmt.Sprintf("%s:%d", host, port)
+ appsRepository := newAppRepository(host, basePort, manifests...)
+
+ appCtx := logger.With(cmdCtx, logger.F("address", address))
+
+ if err := runApp(appCtx, path, address, storageFile, accountsFile, appsRepository); err != nil {
+ logger.Error(appCtx, "could not run app", logger.E(errors.WithStack(err)))
+ }
+ }(p, port, idx)
+ }
+
+ wg.Wait()
+
return nil
},
}
}
-func getServerModules(bus bus.Bus, ds storage.DocumentStore, bs storage.BlobStore, manifest *app.Manifest, address string) []app.ServerModuleFactory {
+func runApp(ctx context.Context, path string, address string, storageFile string, accountsFile string, appRepository appModule.Repository) error {
+ absPath, err := filepath.Abs(path)
+ if err != nil {
+ return errors.Wrapf(err, "could not resolve path '%s'", path)
+ }
+
+ logger.Info(ctx, "opening app bundle", logger.F("path", absPath))
+
+ bundle, err := bundle.FromPath(path)
+ if err != nil {
+ return errors.Wrapf(err, "could not open path '%s' as an app bundle", path)
+ }
+
+ manifest, err := app.LoadManifest(bundle)
+ if err != nil {
+ return errors.Wrap(err, "could not load manifest from app bundle")
+ }
+
+ if valid, err := manifest.Validate(manifestMetadataValidators...); !valid {
+ return errors.Wrap(err, "invalid app manifest")
+ }
+
+ ctx = logger.With(ctx, logger.F("appID", manifest.ID))
+
+ storageFile = injectAppID(storageFile, manifest.ID)
+
+ if err := ensureDir(storageFile); err != nil {
+ return errors.WithStack(err)
+ }
+
+ db, err := sqlite.Open(storageFile)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ accountsFile = injectAppID(accountsFile, manifest.ID)
+
+ accounts, err := loadLocalAccounts(accountsFile)
+ if err != nil {
+ return errors.Wrap(err, "could not load local accounts")
+ }
+
+ // Add auth handler
+ key, err := dummyKey()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ ds := sqlite.NewDocumentStoreWithDB(db)
+ bs := sqlite.NewBlobStoreWithDB(db)
+ bus := memory.NewBus()
+
+ handler := appHTTP.NewHandler(
+ appHTTP.WithBus(bus),
+ appHTTP.WithServerModules(getServerModules(bus, ds, bs, appRepository)...),
+ appHTTP.WithHTTPMounts(
+ appModule.Mount(appRepository),
+ authModule.Mount(
+ authHTTP.NewLocalHandler(
+ jwa.HS256, key,
+ authHTTP.WithRoutePrefix("/auth"),
+ authHTTP.WithAccounts(accounts...),
+ ),
+ authModule.WithJWT(dummyKeySet),
+ ),
+ ),
+ )
+ if err := handler.Load(bundle); err != nil {
+ return errors.Wrap(err, "could not load app bundle")
+ }
+
+ router := chi.NewRouter()
+ router.Use(middleware.Logger)
+
+ // Add app handler
+ router.Handle("/*", handler)
+
+ logger.Info(ctx, "listening", logger.F("address", address))
+
+ if err := http.ListenAndServe(address, router); err != nil {
+ return errors.WithStack(err)
+ }
+
+ return nil
+}
+
+func getServerModules(bus bus.Bus, ds storage.DocumentStore, bs storage.BlobStore, appRepository appModule.Repository) []app.ServerModuleFactory {
return []app.ServerModuleFactory{
module.ContextModuleFactory(),
module.ConsoleModuleFactory(),
@@ -182,49 +244,10 @@ func getServerModules(bus bus.Bus, ds storage.DocumentStore, bs storage.BlobStor
module.RPCModuleFactory(bus),
module.StoreModuleFactory(ds),
blob.ModuleFactory(bus, bs),
- module.Extends(
- auth.ModuleFactory(
- auth.WithJWT(dummyKeySet),
- ),
- func(o *goja.Object) {
- if err := o.Set("CLAIM_TENANT", "arcad_tenant"); err != nil {
- panic(errors.New("could not set 'CLAIM_TENANT' property"))
- }
-
- if err := o.Set("CLAIM_ENTRYPOINT", "arcad_entrypoint"); err != nil {
- panic(errors.New("could not set 'CLAIM_ENTRYPOINT' property"))
- }
-
- if err := o.Set("CLAIM_ROLE", "arcad_role"); err != nil {
- panic(errors.New("could not set 'CLAIM_ROLE' property"))
- }
-
- if err := o.Set("CLAIM_PREFERRED_USERNAME", "preferred_username"); err != nil {
- panic(errors.New("could not set 'CLAIM_PREFERRED_USERNAME' property"))
- }
- },
+ authModule.ModuleFactory(
+ authModule.WithJWT(dummyKeySet),
),
- appModule.ModuleFactory(appModuleMemory.NewRepository(
- func(ctx context.Context, id app.ID, from string) (string, error) {
- addr := address
- if strings.HasPrefix(addr, ":") {
- addr = "0.0.0.0" + addr
- }
-
- host, port, err := net.SplitHostPort(addr)
- if err != nil {
- return "", errors.WithStack(err)
- }
-
- addr, err = findMatchingDeviceAddress(ctx, from, host)
- if err != nil {
- return "", errors.WithStack(err)
- }
-
- return fmt.Sprintf("http://%s:%s", addr, port), nil
- },
- manifest,
- )),
+ appModule.ModuleFactory(appRepository),
fetch.ModuleFactory(bus),
}
}
@@ -349,3 +372,25 @@ func findMatchingDeviceAddress(ctx context.Context, from string, defaultAddr str
return defaultAddr, nil
}
+
+func newAppRepository(host string, basePort uint64, manifests ...*app.Manifest) *appModuleMemory.Repository {
+ return appModuleMemory.NewRepository(
+ func(ctx context.Context, id app.ID, from string) (string, error) {
+ appIndex := 0
+ for i := 0; i < len(manifests); i++ {
+ if manifests[i].ID == id {
+ appIndex = i
+ break
+ }
+ }
+
+ addr, err := findMatchingDeviceAddress(ctx, from, host)
+ if err != nil {
+ return "", errors.WithStack(err)
+ }
+
+ return fmt.Sprintf("http://%s:%d", addr, int(basePort)+appIndex), nil
+ },
+ manifests...,
+ )
+}
diff --git a/doc/apps/client-api/README.md b/doc/apps/client-api/README.md
index 9174744..4223896 100644
--- a/doc/apps/client-api/README.md
+++ b/doc/apps/client-api/README.md
@@ -10,5 +10,6 @@ Afin de pouvoir utiliser le SDK "client", vous devez inclure dans la page HTML d
Vous pourrez ensuite accéder aux variables globales suivantes:
-- [`Edge`](./edge.md) - Client principal d'échange avec le serveur
-- [`EdgeFrame`](./edge-frame.md)
\ No newline at end of file
+- [`Edge.Client`](./edge-client.md) - Client principal d'échange avec le serveur
+- [`Edge.Frame`](./edge-frame.md) - Utilitaire de communication avec une frame parente
+- [`Edge.Menu`](./edge-menu.md) - Gestionnaire de menu
\ No newline at end of file
diff --git a/doc/apps/client-api/edge.md b/doc/apps/client-api/edge-client.md
similarity index 51%
rename from doc/apps/client-api/edge.md
rename to doc/apps/client-api/edge-client.md
index 8d09f7e..2ecf646 100644
--- a/doc/apps/client-api/edge.md
+++ b/doc/apps/client-api/edge-client.md
@@ -1,22 +1,22 @@
-# `Edge`
+# `Edge.Client`
## Méthodes
-### `Edge.connect(): Promise`
+### `Edge.Client.connect(): Promise`
> `TODO`
-### `Edge.disconnect(): void`
+### `Edge.Client.disconnect(): void`
> `TODO`
-### `Edge.send(message: Object): void`
+### `Edge.Client.send(message: Object): void`
> `TODO`
-### `Edge.rpc(method: string, params: Object): Promise`
+### `Edge.Client.rpc(method: string, params: Object): Promise`
> `TODO`
#### Exemple
@@ -36,22 +36,22 @@ function echo(ctx, params) {
**Côté client**
```js
-Edge.connect().then(() => {
- Edge.rpc("echo", { hello: "world!" })
+Edge.Client.connect().then(() => {
+ Edge.Client.rpc("echo", { hello: "world!" })
.then(result => console.log(result))
.catch(err => console.error(err));
});
```
-### `Edge.upload(blob: Blob, metadata: Object): Promise`
+### `Edge.Client.upload(blob: Blob, metadata: Object): Promise`
> `TODO`
-### `Edge.blobUrl(bucketName: string, blobId: string): string`
+### `Edge.Client.blobUrl(bucketName: string, blobId: string): string`
> `TODO`
-### `Edge.externalUrl(url: string): string`
+### `Edge.Client.externalUrl(url: string): string`
Retourne une URL "locale" permettant d'accéder à une ressource externe, en fonction de règles propres à l'application. Voir module [`fetch`](../server-api/fetch.md).
@@ -64,5 +64,5 @@ Retourne une URL "locale" permettant d'accéder à une ressource externe, en fon
#### Exemple
```js
-Edge.addEventListener("message", evt => console.log(evt.detail));
+Edge.Client.addEventListener("message", evt => console.log(evt.detail));
```
\ No newline at end of file
diff --git a/doc/apps/client-api/edge-frame.md b/doc/apps/client-api/edge-frame.md
index 1579dd8..8f2c14b 100644
--- a/doc/apps/client-api/edge-frame.md
+++ b/doc/apps/client-api/edge-frame.md
@@ -1,8 +1,8 @@
-# `EdgeFrame`
+# `Edge.Frame`
## Méthodes
-### `EdgeFrame.addEventListener(name: string, listener: (event) => void)`
+### `Edge.Frame.addEventListener(name: string, listener: (event) => void)`
> `TODO`
diff --git a/doc/apps/client-api/edge-menu.md b/doc/apps/client-api/edge-menu.md
new file mode 100644
index 0000000..2592948
--- /dev/null
+++ b/doc/apps/client-api/edge-menu.md
@@ -0,0 +1,27 @@
+# `Edge.Menu`
+
+## Méthodes
+
+### `Edge.Menu.show()`
+
+Afficher le menu.
+
+### `Edge.Menu.hide()`
+
+Cacher le menu.
+
+### `setItem(name: string, label:string, options?: { iconUrl?: string, linkUrl?: string, order?: number })`
+
+Créer/mettre à jour l'item nommé de la section du menu associée à l'application.
+
+### `removeItem(name: string)`
+
+Supprimer l'item de la section du menu associée à l'application.
+
+### `setAppIconUrl(url: string)`
+
+Mettre à jour l'URL de l'icône de la section du menu associée à l'application.
+
+### `setAppTitle(title: string)`
+
+Mettre à jour le titre de la section du menu associée à l'application.
\ No newline at end of file
diff --git a/doc/apps/my-first-app.md b/doc/apps/my-first-app.md
index 8777247..620d002 100644
--- a/doc/apps/my-first-app.md
+++ b/doc/apps/my-first-app.md
@@ -40,13 +40,13 @@ Créer le fichier `my-app/public/index.html`:
diff --git a/doc/apps/server-api/blob.md b/doc/apps/server-api/blob.md
index cb0eb7c..c771d7d 100644
--- a/doc/apps/server-api/blob.md
+++ b/doc/apps/server-api/blob.md
@@ -86,4 +86,4 @@ interface BlobInfo {
### `Metadata`
-L'objet `Metadata` est un objet clé/valeur arbitraire transmis avec la requête de téléversement. Voir la méthode [`Edge.upload(blob, metadata)`](../client-api/README.md#edge-upload-blob-blob-metadata-object-promise) du SDK client.
\ No newline at end of file
+L'objet `Metadata` est un objet clé/valeur arbitraire transmis avec la requête de téléversement. Voir la méthode [`Edge.Client.upload(blob, metadata)`](../client-api/README.md#edge-upload-blob-blob-metadata-object-promise) du SDK client.
\ No newline at end of file
diff --git a/doc/apps/server-api/fetch.md b/doc/apps/server-api/fetch.md
index f9d01a2..325d910 100644
--- a/doc/apps/server-api/fetch.md
+++ b/doc/apps/server-api/fetch.md
@@ -13,7 +13,7 @@ Pour permettre aux utilisateurs d'accéder à des ressources distantes, vous dev
**Côté client**
```js
// Création d'une URL "locale" permettant d'accéder à la ressource distante
-var url = Edge.externalUrl("http://example.com")
+var url = Edge.Client.externalUrl("http://example.com")
// Vous pouvez utiliser l'URL comme attribut `src` d'une balise par exemple
// ou effectuer une requête fetch() avec celle ci.
diff --git a/doc/apps/server-api/net.md b/doc/apps/server-api/net.md
index 41e6f1f..dbb7553 100644
--- a/doc/apps/server-api/net.md
+++ b/doc/apps/server-api/net.md
@@ -32,9 +32,9 @@ Aucune
```js
// Les données envoyées par le serveur sont accessibles
// via la propriété evt.detail.
-Edge.on('message', evt => console.log(evt.detail));
+Edge.Client.on('message', evt => console.log(evt.detail));
-Edge.connect();
+Edge.Client.connect();
```
**Côté serveur**
diff --git a/doc/apps/server-api/rpc.md b/doc/apps/server-api/rpc.md
index 3ae975e..9f7fca7 100644
--- a/doc/apps/server-api/rpc.md
+++ b/doc/apps/server-api/rpc.md
@@ -1,6 +1,6 @@
# Module `rpc`
-Ce module permet de déclarer des méthodes côté serveur qui seront "invoquable" côté client via la méthode [`Edge.rpc(method: string, params: Object): Promise`](../client-api/README.md#edgerpcmethod-string-params-object-promise).
+Ce module permet de déclarer des méthodes côté serveur qui seront "invoquable" côté client via la méthode [`Edge.Client.rpc(method: string, params: Object): Promise`](../client-api/README.md#edgerpcmethod-string-params-object-promise).
## Méthodes
@@ -31,8 +31,8 @@ function echo(ctx, params) {
**Côté client**
```js
-Edge.connect().then(() => {
- Edge.rpc("echo", { hello: "world!" })
+Edge.Client.connect().then(() => {
+ Edge.Client.rpc("echo", { hello: "world!" })
.then(result => console.log(result))
.catch(err => console.error(err));
});
diff --git a/go.mod b/go.mod
index 7e24124..bfbc640 100644
--- a/go.mod
+++ b/go.mod
@@ -10,17 +10,21 @@ require (
require (
github.com/brutella/dnssd v1.2.6 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
+ github.com/go-playground/locales v0.12.1 // indirect
+ github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/gogo/protobuf v0.0.0-20161014173244-50d1bd39ce4e // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/hashicorp/go.net v0.0.0-20151006203346-104dcad90073 // indirect
github.com/hashicorp/mdns v0.0.0-20151206042412-9d85cf22f9f8 // indirect
+ github.com/leodido/go-urn v1.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.0 // indirect
github.com/miekg/dns v1.1.50 // indirect
+ gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
)
require (
@@ -54,7 +58,7 @@ require (
github.com/spf13/afero v1.9.3 // indirect
github.com/urfave/cli/v2 v2.24.3
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
- gitlab.com/wpetit/goweb v0.0.0-20230206085656-dec695f0e2e9
+ gitlab.com/wpetit/goweb v0.0.0-20230419082146-a94d9ed7202b
go.opencensus.io v0.22.5 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/mod v0.8.0 // indirect
diff --git a/go.sum b/go.sum
index e8c9084..7b9adba 100644
--- a/go.sum
+++ b/go.sum
@@ -109,7 +109,9 @@ github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITL
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
+github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
@@ -210,6 +212,7 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
@@ -291,6 +294,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
gitlab.com/wpetit/goweb v0.0.0-20230206085656-dec695f0e2e9 h1:6JlkcdjYVQglPWYuemK2MoZAtRE4vFx85zLXflGIyI8=
gitlab.com/wpetit/goweb v0.0.0-20230206085656-dec695f0e2e9/go.mod h1:3sus4zjoUv1GB7eDLL60QaPkUnXJCWBpjvbe0jWifeY=
+gitlab.com/wpetit/goweb v0.0.0-20230419082146-a94d9ed7202b h1:nkvOl8TCj/mErADnwFFynjxBtC+hHsrESw6rw56JGmg=
+gitlab.com/wpetit/goweb v0.0.0-20230419082146-a94d9ed7202b/go.mod h1:3sus4zjoUv1GB7eDLL60QaPkUnXJCWBpjvbe0jWifeY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -636,6 +641,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
diff --git a/misc/client-sdk-testsuite/src/manifest.yml b/misc/client-sdk-testsuite/src/manifest.yml
index af5bed1..f7a88b6 100644
--- a/misc/client-sdk-testsuite/src/manifest.yml
+++ b/misc/client-sdk-testsuite/src/manifest.yml
@@ -9,4 +9,4 @@ tags: ["test"]
metadata:
paths:
icon: /icon.png
- minimumRole: visitor
\ No newline at end of file
+ minimumRole: superadmin
\ No newline at end of file
diff --git a/misc/client-sdk-testsuite/src/public/index.html b/misc/client-sdk-testsuite/src/public/index.html
index b1d9e97..0511898 100644
--- a/misc/client-sdk-testsuite/src/public/index.html
+++ b/misc/client-sdk-testsuite/src/public/index.html
@@ -8,7 +8,10 @@
@@ -30,6 +33,17 @@