From 788690086ecc1a77f024ba7778733520dc1dd760 Mon Sep 17 00:00:00 2001
From: Christian Beutel <>
Date: Sat, 4 Jan 2025 14:24:00 +0100
Subject: [PATCH] adds email notifications
---
db/main.go | 12 +-
.../1713027641_created_trails_bounding_box.go | 2 +-
db/migrations/1735935107_updated_lists.go | 76 +++++
db/templates/mail/notification.html | 122 +++++++
db/util/email_templates.go | 67 ++++
db/util/notification.go | 85 ++---
.../docs/getting-started/Installation.md | 18 +-
docs/wanderer.openapi.yaml | 308 +++++++++---------
.../trail/map_with_elevation_maplibre.svelte | 8 +-
web/src/lib/models/api/base_schema.ts | 2 +-
web/src/routes/map/+page.svelte | 17 +-
web/src/routes/profile/[id]/+layout.svelte | 12 +-
web/src/routes/register/+page.svelte | 2 +-
web/static/docs/api/wanderer.openapi.yaml | 308 +++++++++---------
web/static/imgs/logo_text_light.png | Bin 0 -> 3732 bytes
15 files changed, 666 insertions(+), 373 deletions(-)
create mode 100644 db/migrations/1735935107_updated_lists.go
create mode 100644 db/templates/mail/notification.html
create mode 100644 db/util/email_templates.go
create mode 100644 web/static/imgs/logo_text_light.png
diff --git a/db/main.go b/db/main.go
index 96e97bbd..cc1ecca8 100644
--- a/db/main.go
+++ b/db/main.go
@@ -114,7 +114,7 @@ func createTrailHandler(app *pocketbase.PocketBase, client meilisearch.ServiceMa
if e.Record.GetBool("public") {
notification := util.Notification{
Type: util.TrailCreate,
- Metadata: map[string]interface{}{
+ Metadata: map[string]string{
"id": e.Record.Id,
"trail": e.Record.GetString("name"),
},
@@ -163,7 +163,7 @@ func createTrailShareHandler(app *pocketbase.PocketBase, client meilisearch.Serv
notification := util.Notification{
Type: util.TrailShare,
- Metadata: map[string]interface{}{
+ Metadata: map[string]string{
"id": shareTrail.Id,
"trail": shareTrail.GetString("name"),
"author": shareTrailAuthor.GetString("username"),
@@ -185,7 +185,7 @@ func createListShareHandler(app *pocketbase.PocketBase) func(e *core.RecordCreat
notification := util.Notification{
Type: util.ListShare,
- Metadata: map[string]interface{}{
+ Metadata: map[string]string{
"id": shareList.Id,
"list": shareList.GetString("name"),
"author": shareListAuthor.GetString("username"),
@@ -211,7 +211,7 @@ func createListHandler(app *pocketbase.PocketBase) func(e *core.RecordCreateEven
}
notification := util.Notification{
Type: util.ListCreate,
- Metadata: map[string]interface{}{
+ Metadata: map[string]string{
"id": e.Record.Id,
"list": e.Record.GetString("name"),
},
@@ -231,7 +231,7 @@ func createFollowHandler(app *pocketbase.PocketBase) func(e *core.RecordCreateEv
notification := util.Notification{
Type: util.NewFollower,
- Metadata: map[string]interface{}{
+ Metadata: map[string]string{
"follower": follower.GetString("username"),
},
Seen: false,
@@ -252,7 +252,7 @@ func createCommentHandler(app *pocketbase.PocketBase) func(e *core.RecordCreateE
notification := util.Notification{
Type: util.TrailComment,
- Metadata: map[string]interface{}{
+ Metadata: map[string]string{
"id": commentTrail.Id,
"author": commentAuthor.GetString("username"),
"trail": commentTrail.GetString("name"),
diff --git a/db/migrations/1713027641_created_trails_bounding_box.go b/db/migrations/1713027641_created_trails_bounding_box.go
index 261f4d1b..1fc4366e 100644
--- a/db/migrations/1713027641_created_trails_bounding_box.go
+++ b/db/migrations/1713027641_created_trails_bounding_box.go
@@ -86,7 +86,7 @@ func init() {
return daos.New(db).SaveCollection(collection)
}, func(db dbx.Builder) error {
- dao := daos.New(db);
+ dao := daos.New(db)
collection, err := dao.FindCollectionByNameOrId("urytyc428mwlbqq")
if err != nil {
diff --git a/db/migrations/1735935107_updated_lists.go b/db/migrations/1735935107_updated_lists.go
new file mode 100644
index 00000000..d35a1601
--- /dev/null
+++ b/db/migrations/1735935107_updated_lists.go
@@ -0,0 +1,76 @@
+package migrations
+
+import (
+ "encoding/json"
+
+ "github.com/pocketbase/dbx"
+ "github.com/pocketbase/pocketbase/daos"
+ m "github.com/pocketbase/pocketbase/migrations"
+ "github.com/pocketbase/pocketbase/models/schema"
+)
+
+func init() {
+ m.Register(func(db dbx.Builder) error {
+ dao := daos.New(db);
+
+ collection, err := dao.FindCollectionByNameOrId("r6gu2ajyidy1x69")
+ if err != nil {
+ return err
+ }
+
+ // update
+ edit_author := &schema.SchemaField{}
+ if err := json.Unmarshal([]byte(`{
+ "system": false,
+ "id": "kwm6zdet",
+ "name": "author",
+ "type": "relation",
+ "required": true,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }`), edit_author); err != nil {
+ return err
+ }
+ collection.Schema.AddField(edit_author)
+
+ return dao.SaveCollection(collection)
+ }, func(db dbx.Builder) error {
+ dao := daos.New(db);
+
+ collection, err := dao.FindCollectionByNameOrId("r6gu2ajyidy1x69")
+ if err != nil {
+ return err
+ }
+
+ // update
+ edit_author := &schema.SchemaField{}
+ if err := json.Unmarshal([]byte(`{
+ "system": false,
+ "id": "kwm6zdet",
+ "name": "author",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }`), edit_author); err != nil {
+ return err
+ }
+ collection.Schema.AddField(edit_author)
+
+ return dao.SaveCollection(collection)
+ })
+}
diff --git a/db/templates/mail/notification.html b/db/templates/mail/notification.html
new file mode 100644
index 00000000..6a1e84f4
--- /dev/null
+++ b/db/templates/mail/notification.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Hello {{.RecipientName}},
+
You have a new notification:
+
+
+
+
+
+
Kind regards,
+
Your wanderer team
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/db/util/email_templates.go b/db/util/email_templates.go
new file mode 100644
index 00000000..817340cd
--- /dev/null
+++ b/db/util/email_templates.go
@@ -0,0 +1,67 @@
+package util
+
+import (
+ "bytes"
+ "errors"
+ t "html/template"
+
+ "github.com/pocketbase/pocketbase/tools/template"
+)
+
+type EmailData struct {
+ AppUrl string
+ RecipientName string
+ Content t.HTML
+ Link string
+ FollowerName string
+}
+
+var notificationTemplates = map[NotificationType]string{
+ TrailCreate: "{{.Author}} has created a new trail: {{.trail}}.",
+ TrailShare: "{{.Author}} has shared a trail with you: {{.trail}}.",
+ ListCreate: "{{.Author}} has created a new list: {{.list}}.",
+ ListShare: "{{.Author}} has shared a list with you: {{.list}}.",
+ NewFollower: "Good news! You have a new follower: {{.Author}}.",
+ TrailComment: "{{.Author}} commented on your trail '{{.trail}}': '{{.comment}}'.",
+}
+
+func GenerateHTML(appUrl string, recipientName string, authorName string, notificationType NotificationType, metadata map[string]string) (string, error) {
+ registry := template.NewRegistry()
+
+ // Get custom content template for the notification type
+ customTemplate, exists := notificationTemplates[notificationType]
+ if !exists {
+ return "", errors.New("unknown notification type")
+ }
+
+ // Create content template with dynamic data
+ contentTmpl, err := t.New("content").Parse(customTemplate)
+ if err != nil {
+ return "", err
+ }
+
+ // Render the custom content with data
+ metadata["Author"] = authorName
+ var contentBuffer bytes.Buffer
+ err = contentTmpl.Execute(&contentBuffer, metadata)
+ if err != nil {
+ return "", err
+ }
+
+ // Assemble the final email with boilerplate and custom content
+ content := EmailData{
+ AppUrl: appUrl,
+ RecipientName: recipientName,
+ Content: t.HTML(contentBuffer.String()),
+ }
+
+ html, err := registry.LoadFiles(
+ "templates/mail/notification.html",
+ ).Render(content)
+
+ if err != nil {
+ return "", err
+ }
+
+ return html, nil
+}
diff --git a/db/util/notification.go b/db/util/notification.go
index 0d552bb6..20c305a7 100644
--- a/db/util/notification.go
+++ b/db/util/notification.go
@@ -2,10 +2,12 @@ package util
import (
"fmt"
+ "net/mail"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/models"
+ "github.com/pocketbase/pocketbase/tools/mailer"
)
type NotificationType string
@@ -20,10 +22,10 @@ const (
)
type Notification struct {
- Type NotificationType `json:"type"`
- Metadata interface{} `json:"metadata,omitempty"`
- Seen bool `json:"seen"`
- Author string `json:"author"`
+ Type NotificationType `json:"type"`
+ Metadata map[string]string `json:"metadata,omitempty"`
+ Seen bool `json:"seen"`
+ Author string `json:"author"`
}
type NotificationSettings struct {
@@ -59,22 +61,50 @@ func SendNotification(app *pocketbase.PocketBase, notification Notification, rec
if err != nil {
return err
}
- if !permissions.Web {
- return nil
- }
+
notifications, err := app.Dao().FindCollectionByNameOrId("notifications")
if err != nil {
return err
}
- n := models.NewRecord(notifications)
- n.Set("type", string(notification.Type))
- n.Set("metadata", notification.Metadata)
- n.Set("seen", notification.Seen)
- n.Set("recipient", recipient)
- n.Set("author", notification.Author)
- if err := app.Dao().SaveRecord(n); err != nil {
- return err
+ if permissions.Web {
+ n := models.NewRecord(notifications)
+ n.Set("type", string(notification.Type))
+ n.Set("metadata", notification.Metadata)
+ n.Set("seen", notification.Seen)
+ n.Set("recipient", recipient)
+ n.Set("author", notification.Author)
+
+ if err := app.Dao().SaveRecord(n); err != nil {
+ return err
+ }
+ }
+
+ if permissions.Email {
+ recipientUser, err := app.Dao().FindRecordById("users", recipient)
+ if err != nil {
+ return err
+ }
+ authorUser, err := app.Dao().FindRecordById("users", notification.Author)
+ if err != nil {
+ return err
+ }
+ html, err := GenerateHTML(app.Settings().Meta.AppUrl, recipientUser.Username(), authorUser.Username(), notification.Type, notification.Metadata)
+ if err != nil {
+ return err
+ }
+
+ message := &mailer.Message{
+ From: mail.Address{
+ Address: app.Settings().Meta.SenderAddress,
+ Name: app.Settings().Meta.SenderName,
+ },
+ To: []mail.Address{{Address: recipientUser.Email()}},
+ Subject: "wanderer - New Notification",
+ HTML: html,
+ }
+
+ app.NewMailClient().Send(message)
}
return nil
}
@@ -85,32 +115,11 @@ func SendNotificationToFollowers(app *pocketbase.PocketBase, notification Notifi
if err != nil {
return err
}
- notifications, err := app.Dao().FindCollectionByNameOrId("notifications")
- if err != nil {
- return err
- }
+
for _, f := range followers {
recipient := f.GetString("follower")
- if notification.Author == recipient {
- continue
- }
- permissions, err := getNotificationPermissions(app, recipient, notification.Type)
- if err != nil {
- continue
- }
- if !permissions.Web {
- continue
- }
- n := models.NewRecord(notifications)
- n.Set("type", string(notification.Type))
- n.Set("metadata", notification.Metadata)
- n.Set("seen", notification.Seen)
- n.Set("recipient", recipient)
- n.Set("author", notification.Author)
+ SendNotification(app, notification, recipient)
- if err := app.Dao().SaveRecord(n); err != nil {
- continue
- }
}
return nil
}
diff --git a/docs/src/content/docs/getting-started/Installation.md b/docs/src/content/docs/getting-started/Installation.md
index b7b36c18..e0ec5731 100644
--- a/docs/src/content/docs/getting-started/Installation.md
+++ b/docs/src/content/docs/getting-started/Installation.md
@@ -115,9 +115,10 @@ To update all containers to a new version simply run `docker compose pull && doc
While not recommended it is absolutely possible to install wanderer from source.
### Prerequisites
-1. git installed && `git clone https://github.com/Flomp/wanderer.git`
+1. git installed && `git clone https://github.com/Flomp/wanderer.git --branch v0.12.0 --single-branch`
2. go >= 1.21.1 installed
3. node >= 18.17.0 installed
+4. npm >= 8.15.0 installed
### meilisearch
wanderer uses meilisearch without any further modifications. As a result, you can simply head over to [their website](https://www.meilisearch.com/docs/learn/getting_started/installation) and follow the instructions for your preferred platform. We assume that you put the binary in the `wanderer/search` directory. If you did not, adapt the launch script below accordingly.
@@ -157,15 +158,26 @@ Caution: Ensure that you change the `MEILI_MASTER_KEY` to a different value if y
```bash
trap "kill 0" EXIT
+# learn more about the configuration:
+# https://wanderer.to/getting-started/configuration/
+# required
export ORIGIN=http://localhost:3000
export MEILI_URL=http://127.0.0.1:7700
-export MEILI_MASTER_KEY=CHANGE_ME!
+export MEILI_MASTER_KEY=YOU_SHOULD_DEFINITELY_CHANGE_ME
export PUBLIC_POCKETBASE_URL=http://127.0.0.1:8090
export PUBLIC_VALHALLA_URL=https://valhalla1.openstreetmap.de
+# optional
+# export MEILI_NO_ANALYTICS=true
+# export BODY_SIZE_LIMIT=Infinity
+# export PUBLIC_DISABLE_SIGNUP=false
+# export UPLOAD_FOLDER=/app/uploads
+# export UPLOAD_USER=
+# export UPLOAD_PASSWORD=
+
cd search && ./meilisearch --master-key $MEILI_MASTER_KEY &
cd db && ./pocketbase serve &
-cd web/build && node build &
+cd web && node build &
wait
```
diff --git a/docs/wanderer.openapi.yaml b/docs/wanderer.openapi.yaml
index e7f5882b..82b03983 100644
--- a/docs/wanderer.openapi.yaml
+++ b/docs/wanderer.openapi.yaml
@@ -325,7 +325,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -348,13 +348,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -583,7 +583,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -602,13 +602,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: undefined
@@ -794,7 +794,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -817,13 +817,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -1232,7 +1232,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -1255,13 +1255,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -1659,7 +1659,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -1678,13 +1678,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -1834,7 +1834,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -1857,13 +1857,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2028,7 +2028,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2051,13 +2051,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2163,7 +2163,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2186,13 +2186,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2283,7 +2283,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2306,13 +2306,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2442,7 +2442,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2465,13 +2465,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2755,7 +2755,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2778,13 +2778,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -3060,7 +3060,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3083,13 +3083,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -3192,7 +3192,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3215,13 +3215,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -3379,7 +3379,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3402,13 +3402,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -3514,7 +3514,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3537,13 +3537,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -4044,7 +4044,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -4067,7 +4067,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -4449,7 +4449,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -4468,13 +4468,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -4577,7 +4577,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -4600,13 +4600,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5006,7 +5006,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5029,7 +5029,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -5197,7 +5197,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5220,13 +5220,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5421,7 +5421,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5444,13 +5444,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5556,7 +5556,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5579,13 +5579,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5810,7 +5810,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5829,13 +5829,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -5949,7 +5949,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5972,13 +5972,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6135,7 +6135,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -6158,13 +6158,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6366,7 +6366,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -6389,13 +6389,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6501,7 +6501,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -6524,13 +6524,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6985,7 +6985,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7008,7 +7008,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -7246,7 +7246,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7265,13 +7265,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -7449,7 +7449,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7472,13 +7472,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -7666,7 +7666,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7689,13 +7689,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -7801,7 +7801,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7824,13 +7824,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -8245,7 +8245,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8268,13 +8268,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -8509,7 +8509,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8528,13 +8528,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -8644,7 +8644,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8667,13 +8667,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -8812,7 +8812,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8835,13 +8835,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -8999,7 +8999,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9022,13 +9022,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -9134,7 +9134,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9157,13 +9157,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -9515,7 +9515,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9534,13 +9534,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -9925,7 +9925,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9948,7 +9948,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -10440,7 +10440,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -10463,7 +10463,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -10636,7 +10636,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -10659,13 +10659,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -11135,7 +11135,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11158,7 +11158,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -11402,7 +11402,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11423,13 +11423,13 @@ paths:
- path
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_string
validation: date
message: Invalid date
@@ -11624,7 +11624,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11647,13 +11647,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -11861,7 +11861,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11884,13 +11884,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -11996,7 +11996,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -12019,13 +12019,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -12202,7 +12202,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -12225,13 +12225,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -13027,7 +13027,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13050,7 +13050,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -13388,7 +13388,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13409,13 +13409,13 @@ paths:
- path
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_string
validation: date
message: Invalid date
@@ -13657,7 +13657,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13680,13 +13680,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -13967,7 +13967,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13990,7 +13990,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
'404':
description: ''
@@ -14079,7 +14079,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -14102,13 +14102,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -14333,7 +14333,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -14356,13 +14356,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
diff --git a/web/src/lib/components/trail/map_with_elevation_maplibre.svelte b/web/src/lib/components/trail/map_with_elevation_maplibre.svelte
index 013e3158..6303f672 100644
--- a/web/src/lib/components/trail/map_with_elevation_maplibre.svelte
+++ b/web/src/lib/components/trail/map_with_elevation_maplibre.svelte
@@ -140,7 +140,7 @@
const r: GeoJSON[] = [];
trails.forEach((t) => {
- if (t.expand.gpx_data) {
+ if (t.expand?.gpx_data) {
r.push(toGeoJson(t.expand.gpx_data) as GeoJSON);
} else if (t.lat && t.lon) {
r.push({
@@ -165,7 +165,7 @@
if (data[activeTrail] && showElevation) {
epc?.setData(
data[activeTrail]!,
- trails.at(activeTrail)!.expand.waypoints,
+ trails.at(activeTrail)!.expand?.waypoints,
);
epc?.showProfile();
}
@@ -409,7 +409,7 @@
if (data[activeTrail] && showElevation) {
epc?.setData(
data[activeTrail]!,
- trails.at(activeTrail)!.expand.waypoints,
+ trails.at(activeTrail)!.expand?.waypoints,
);
epc?.showProfile();
}
@@ -490,7 +490,7 @@
if (!map) {
return;
}
- for (const waypoint of trails[activeTrail]?.expand.waypoints ?? []) {
+ for (const waypoint of trails[activeTrail]?.expand?.waypoints ?? []) {
const marker = createMarkerFromWaypoint(waypoint, onMarkerDragEnd);
marker.addTo(map);
markers.push(marker);
diff --git a/web/src/lib/models/api/base_schema.ts b/web/src/lib/models/api/base_schema.ts
index 3af4b5b8..845c5081 100644
--- a/web/src/lib/models/api/base_schema.ts
+++ b/web/src/lib/models/api/base_schema.ts
@@ -7,7 +7,7 @@ const RecordOptionsSchema = z.object({
})
const RecordListOptionsSchema = RecordOptionsSchema.extend({
- page: z.number({ coerce: true }).int().positive().optional(),
+ page: z.number({ coerce: true }).int().nonnegative().optional(),
perPage: z.number({ coerce: true }).int().optional(),
sort: z.string().optional(),
filter: z.string().optional(),
diff --git a/web/src/routes/map/+page.svelte b/web/src/routes/map/+page.svelte
index d2f528ac..df9934ab 100644
--- a/web/src/routes/map/+page.svelte
+++ b/web/src/routes/map/+page.svelte
@@ -132,6 +132,15 @@
function handleMapInit() {
if (
+ $page.url.searchParams.has("lat") &&
+ $page.url.searchParams.has("lon")
+ ) {
+ const lat = $page.url.searchParams.get("lat");
+ const lon = $page.url.searchParams.get("lon");
+ map.setCenter([parseFloat(lon!), parseFloat(lat!)]);
+ map.setZoom(14);
+ console.log("Set map center to: " + lat + " " + lon);
+ } else if (
$page.url.searchParams.has("tl_lat") &&
$page.url.searchParams.has("tl_lon") &&
$page.url.searchParams.has("br_lat") &&
@@ -148,14 +157,6 @@
],
];
map.fitBounds(boundingBox, { animate: false });
- } else if (
- $page.url.searchParams.has("lat") &&
- $page.url.searchParams.has("lon")
- ) {
- const lat = $page.url.searchParams.get("lat");
- const lon = $page.url.searchParams.get("lon");
- map.setCenter([parseFloat(lon!), parseFloat(lat!)]);
- map.setZoom(14);
} else if (settings && settings.mapFocus == "trails") {
const boundingBox: M.LngLatBoundsLike = [
[maxBoundingBox.min_lon, maxBoundingBox.max_lat],
diff --git a/web/src/routes/profile/[id]/+layout.svelte b/web/src/routes/profile/[id]/+layout.svelte
index 523a6fbb..d91cf6d9 100644
--- a/web/src/routes/profile/[id]/+layout.svelte
+++ b/web/src/routes/profile/[id]/+layout.svelte
@@ -17,7 +17,7 @@
text: $_("trail", { values: { n: 2 } }),
value: `/profile/${$page.params.id}/trails`,
},
- { text: $_('statistics'), value: `/profile/${$page.params.id}/stats` },
+ { text: $_("statistics"), value: `/profile/${$page.params.id}/stats` },
];
$: activeIndex = profileLinks.findIndex(
@@ -80,14 +80,14 @@
href="/profile/{data.user.id}/users/followers"
>
{data.followers}
- {$_('followers')}
+ {$_("followers")}
{data.following}
- {$_('following')}
+ {$_("following")}
{#if !data.isOwnProfile}
@@ -113,6 +113,12 @@
>
{/each}
+ {#if data.isOwnProfile}
+
+ {/if}
diff --git a/web/src/routes/register/+page.svelte b/web/src/routes/register/+page.svelte
index d17a20b0..bf9ea942 100644
--- a/web/src/routes/register/+page.svelte
+++ b/web/src/routes/register/+page.svelte
@@ -28,7 +28,7 @@
$_("must-be-at-least-n-characters-long", {
values: { n: 3 },
}),
- ),
+ ).regex(/^[\w][\w\.]*$/, $_("invalid-username")),
email: z
.string()
.min(1, "required")
diff --git a/web/static/docs/api/wanderer.openapi.yaml b/web/static/docs/api/wanderer.openapi.yaml
index 12c8377f..90874e1e 100644
--- a/web/static/docs/api/wanderer.openapi.yaml
+++ b/web/static/docs/api/wanderer.openapi.yaml
@@ -325,7 +325,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -348,13 +348,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -583,7 +583,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -602,13 +602,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: undefined
@@ -794,7 +794,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -817,13 +817,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -1232,7 +1232,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -1255,13 +1255,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -1659,7 +1659,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -1678,13 +1678,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -1834,7 +1834,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -1857,13 +1857,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2028,7 +2028,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2051,13 +2051,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2163,7 +2163,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2186,13 +2186,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2283,7 +2283,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2306,13 +2306,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2442,7 +2442,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2465,13 +2465,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -2755,7 +2755,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -2778,13 +2778,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -3060,7 +3060,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3083,13 +3083,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -3192,7 +3192,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3215,13 +3215,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -3379,7 +3379,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3402,13 +3402,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -3514,7 +3514,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -3537,13 +3537,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -4044,7 +4044,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -4067,7 +4067,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -4449,7 +4449,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -4468,13 +4468,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -4577,7 +4577,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -4600,13 +4600,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5006,7 +5006,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5029,7 +5029,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -5197,7 +5197,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5220,13 +5220,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5421,7 +5421,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5444,13 +5444,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5556,7 +5556,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5579,13 +5579,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -5810,7 +5810,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5829,13 +5829,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -5949,7 +5949,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -5972,13 +5972,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6135,7 +6135,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -6158,13 +6158,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6366,7 +6366,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -6389,13 +6389,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6501,7 +6501,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -6524,13 +6524,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -6985,7 +6985,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7008,7 +7008,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -7246,7 +7246,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7265,13 +7265,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -7449,7 +7449,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7472,13 +7472,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -7666,7 +7666,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7689,13 +7689,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -7801,7 +7801,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -7824,13 +7824,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -8245,7 +8245,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8268,13 +8268,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 0
type: number
@@ -8509,7 +8509,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8528,13 +8528,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -8644,7 +8644,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8667,13 +8667,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -8812,7 +8812,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -8835,13 +8835,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -8999,7 +8999,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9022,13 +9022,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -9134,7 +9134,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9157,13 +9157,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -9515,7 +9515,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9534,13 +9534,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_type
expected: string
received: number
@@ -9925,7 +9925,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -9948,7 +9948,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -10440,7 +10440,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -10463,7 +10463,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -10636,7 +10636,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -10659,13 +10659,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -11135,7 +11135,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11158,7 +11158,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -11402,7 +11402,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11423,13 +11423,13 @@ paths:
- path
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_string
validation: date
message: Invalid date
@@ -11624,7 +11624,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11647,13 +11647,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -11861,7 +11861,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -11884,13 +11884,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -11996,7 +11996,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -12019,13 +12019,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -12202,7 +12202,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -12225,13 +12225,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -13027,7 +13027,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13050,7 +13050,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
x-400:Invalid sort/expand/filter:
description: ''
@@ -13388,7 +13388,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13409,13 +13409,13 @@ paths:
- path
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: invalid_string
validation: date
message: Invalid date
@@ -13657,7 +13657,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13680,13 +13680,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -13967,7 +13967,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -13990,7 +13990,7 @@ paths:
type: string
required:
- message
- - details
+ - detail
headers: {}
'404':
description: ''
@@ -14079,7 +14079,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -14102,13 +14102,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
@@ -14333,7 +14333,7 @@ paths:
properties:
message:
type: string
- details:
+ detail:
type: array
items:
type: object
@@ -14356,13 +14356,13 @@ paths:
type: string
required:
- message
- - details
+ - detail
examples:
'1':
summary: Exception
value:
message: invalid_params
- details:
+ detail:
- code: too_small
minimum: 15
type: string
diff --git a/web/static/imgs/logo_text_light.png b/web/static/imgs/logo_text_light.png
new file mode 100644
index 0000000000000000000000000000000000000000..158cb37a815e5aed230c37945de97e13f4457699
GIT binary patch
literal 3732
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