diff --git a/db/migrations/1715276397_created_users_anonymous.go b/db/migrations/1715276397_created_users_anonymous.go new file mode 100644 index 00000000..dfa1df95 --- /dev/null +++ b/db/migrations/1715276397_created_users_anonymous.go @@ -0,0 +1,64 @@ +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" +) + +func init() { + m.Register(func(db dbx.Builder) error { + jsonData := `{ + "id": "xku110v5a5xbufa", + "created": "2024-05-09 17:39:57.419Z", + "updated": "2024-05-09 17:39:57.419Z", + "name": "users_anonymous", + "type": "view", + "system": false, + "schema": [ + { + "system": false, + "id": "cknini6y", + "name": "username", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + } + ], + "indexes": [], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": { + "query": "SELECT id, username FROM users" + } + }` + + collection := &models.Collection{} + if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { + return err + } + + return daos.New(db).SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("xku110v5a5xbufa") + if err != nil { + return err + } + + return dao.DeleteCollection(collection) + }) +} diff --git a/db/migrations/1715276572_updated_users_anonymous.go b/db/migrations/1715276572_updated_users_anonymous.go new file mode 100644 index 00000000..c20a7a53 --- /dev/null +++ b/db/migrations/1715276572_updated_users_anonymous.go @@ -0,0 +1,87 @@ +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" + "github.com/pocketbase/pocketbase/tools/types" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("xku110v5a5xbufa") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("@request.auth.id != \"\"") + + collection.ViewRule = types.Pointer("@request.auth.id != \"\"") + + // remove + collection.Schema.RemoveField("cknini6y") + + // add + new_username := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "qzkmy7nv", + "name": "username", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }`), new_username); err != nil { + return err + } + collection.Schema.AddField(new_username) + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("xku110v5a5xbufa") + if err != nil { + return err + } + + collection.ListRule = nil + + collection.ViewRule = nil + + // add + del_username := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "cknini6y", + "name": "username", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }`), del_username); err != nil { + return err + } + collection.Schema.AddField(del_username) + + // remove + collection.Schema.RemoveField("qzkmy7nv") + + return dao.SaveCollection(collection) + }) +} diff --git a/db/migrations/1715276881_created_trail_share.go b/db/migrations/1715276881_created_trail_share.go new file mode 100644 index 00000000..da519cd1 --- /dev/null +++ b/db/migrations/1715276881_created_trail_share.go @@ -0,0 +1,96 @@ +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" +) + +func init() { + m.Register(func(db dbx.Builder) error { + jsonData := `{ + "id": "1mns8mlal6uf9ku", + "created": "2024-05-09 17:48:01.382Z", + "updated": "2024-05-09 17:48:01.382Z", + "name": "trail_share", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "eskurfx6", + "name": "trail", + "type": "relation", + "required": true, + "presentable": false, + "unique": false, + "options": { + "collectionId": "e864strfxo14pm4", + "cascadeDelete": true, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "yyzimwee", + "name": "user", + "type": "relation", + "required": true, + "presentable": false, + "unique": false, + "options": { + "collectionId": "_pb_users_auth_", + "cascadeDelete": true, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "zr7aaqxl", + "name": "permission", + "type": "select", + "required": true, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "view", + "edit" + ] + } + } + ], + "indexes": [], + "listRule": "trail.author = @request.auth.id", + "viewRule": "trail.author = @request.auth.id", + "createRule": "trail.author = @request.auth.id", + "updateRule": "trail.author = @request.auth.id", + "deleteRule": "trail.author = @request.auth.id", + "options": {} + }` + + collection := &models.Collection{} + if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { + return err + } + + return daos.New(db).SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("1mns8mlal6uf9ku") + if err != nil { + return err + } + + return dao.DeleteCollection(collection) + }) +} diff --git a/docker-compose.yml b/docker-compose.yml index f5680eb5..8704ad8d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,8 +54,8 @@ services: PUBLIC_POCKETBASE_URL: http://db:8090 PUBLIC_DISABLE_SIGNUP: false UPLOAD_FOLDER: /app/uploads - UPLOAD_USER: - UPLOAD_PASSWORD: + UPLOAD_USER: Flomp + UPLOAD_PASSWORD: 12345678 PUBLIC_VALHALLA_URL: https://valhalla1.openstreetmap.de volumes: - ./data/uploads:/app/uploads diff --git a/web/src/lib/components/trail/trail_dropdown.svelte b/web/src/lib/components/trail/trail_dropdown.svelte index 71160517..197e7544 100644 --- a/web/src/lib/components/trail/trail_dropdown.svelte +++ b/web/src/lib/components/trail/trail_dropdown.svelte @@ -18,6 +18,7 @@ import TrailExportModal from "./trail_export_modal.svelte"; import JSZip from "jszip"; import { getFileURL, saveAs } from "$lib/util/file_util"; + import TrailShareModal from "./trail_share_modal.svelte"; export let trail: Trail; export let mode: "overview" | "map"; @@ -25,6 +26,7 @@ let openConfirmModal: () => void; let openListSelectModal: () => void; let openExportModal: () => void; + let openShareModal: () => void; const dropdownItems: DropdownItem[] = [ mode == "overview" @@ -47,6 +49,7 @@ : []), { text: $_("print"), value: "print", icon: "print" }, { text: $_("add-to-list"), value: "list", icon: "bookmark" }, + { text: $_("share"), value: "share", icon: "share" }, { text: $_("edit"), value: "edit", icon: "pen" }, { text: $_("delete"), value: "delete", icon: "trash" }, ]; @@ -69,6 +72,8 @@ ?.focus(); } else if (item.value == "print") { goto(`/map/trail/${trail.id}/print`); + } else if (item.value == "share") { + openShareModal(); } else if (item.value == "download") { openExportModal(); } else if (item.value == "edit") { @@ -84,9 +89,11 @@ summitLog: boolean; }) { try { - let fileData: string = await trail2gpx(trail); + let fileData: string = await trail2gpx(trail); if (exportSettings.fileFormat == "json") { - fileData = JSON.stringify(gpx(new DOMParser().parseFromString(fileData, "text/xml"))); + fileData = JSON.stringify( + gpx(new DOMParser().parseFromString(fileData, "text/xml")), + ); } const zip = new JSZip(); zip.file(`${trail.name}.${exportSettings.fileFormat}`, fileData); @@ -101,12 +108,15 @@ photoFolder?.file(photo, photoData, { base64: true }); } } - if(exportSettings.summitLog) { - let summitLogString = "" + if (exportSettings.summitLog) { + let summitLogString = ""; for (const summitLog of trail.expand.summit_logs) { - summitLogString += `${summitLog.date},${summitLog.text}\n` + summitLogString += `${summitLog.date},${summitLog.text}\n`; } - zip.file(`${trail.name} - ${$_("summit-book")}.csv`, summitLogString) + zip.file( + `${trail.name} - ${$_("summit-book")}.csv`, + summitLogString, + ); } const blob = await zip.generateAsync({ type: "blob" }); saveAs(blob, `${trail.name}.zip`); @@ -179,3 +189,4 @@ bind:openModal={openExportModal} on:export={(e) => exportTrail(e.detail)} > + diff --git a/web/src/lib/components/trail/trail_share_modal.svelte b/web/src/lib/components/trail/trail_share_modal.svelte new file mode 100644 index 00000000..61f5d369 --- /dev/null +++ b/web/src/lib/components/trail/trail_share_modal.svelte @@ -0,0 +1,77 @@ + + + +
+ updateUsers(e.detail)} + placeholder={`${$_("username")}`} + items={searchItems} + > +
+
+ + +
diff --git a/web/src/lib/i18n/locales/de.json b/web/src/lib/i18n/locales/de.json index c1945a81..534ec7f6 100644 --- a/web/src/lib/i18n/locales/de.json +++ b/web/src/lib/i18n/locales/de.json @@ -18,10 +18,12 @@ "change": "Ändern", "changelog": "Changelog", "chinese": "Chinesisch (vereinfacht)", + "close": "", "comment": "{n, plural, =1 {Kommentar} other {Kommentare}}", "completed": "Abgeschlossen", "completion-status": "Abschlussstatus", "contribute": "Mitwirken", + "copy-link": "", "create-new-list": "Neue Liste erstellen", "creation-date": "Erstellungsdatum", "cycling": "Radfahren", @@ -81,6 +83,7 @@ "language": "Sprache", "latitude": "Breitengrad", "license": "Lizenz", + "link-copied": "", "list": "{n, plural, =1 {Liste} other {Listen}}", "location": "Standort", "login": "Login", @@ -130,6 +133,7 @@ "search-trails": "Route suchen", "select-list": "Liste auswählen", "settings": "Einstellungen", + "share": "", "show-in-overview": "In der Übersicht anzeigen", "show-on-map": "Auf der Karte anzeigen", "slogan": "Speichere deine Abenteuer!", diff --git a/web/src/lib/i18n/locales/en.json b/web/src/lib/i18n/locales/en.json index 6c801ba1..8a13b31d 100644 --- a/web/src/lib/i18n/locales/en.json +++ b/web/src/lib/i18n/locales/en.json @@ -18,10 +18,12 @@ "change": "Change", "changelog": "Changelog", "chinese": "Chinese (simplified)", + "close": "Close", "comment": "{n, plural, =1 {Comment} other {Comments}}", "completed": "Completed", "completion-status": "Completion Status", "contribute": "Contribute", + "copy-link": "Copy Link", "create-new-list": "Create new list", "creation-date": "Creation date", "cycling": "Cycling", @@ -81,6 +83,7 @@ "language": "Language", "latitude": "Latitude", "license": "License", + "link-copied": "Link copied!", "list": "{n, plural, =1 {List} other {Lists}}", "location": "Location", "login": "Login", @@ -130,6 +133,7 @@ "search-trails": "Search trails", "select-list": "Select List", "settings": "Settings", + "share": "Share", "show-in-overview": "Show in overview", "show-on-map": "Show on map", "slogan": "Save your adventures!", diff --git a/web/src/lib/i18n/locales/fr.json b/web/src/lib/i18n/locales/fr.json index e7959947..f191cf51 100644 --- a/web/src/lib/i18n/locales/fr.json +++ b/web/src/lib/i18n/locales/fr.json @@ -18,10 +18,12 @@ "change": "Changement", "changelog": "Journal des modifications", "chinese": "Chinois (simplifié)", + "close": "", "comment": "{n, plural, =1 {Commentaire} other {Commentaires}}", "completed": "Compléter", "completion-status": "L'état d'achèvement", "contribute": "Contribuer", + "copy-link": "", "create-new-list": "Créer une nouvelle liste", "creation-date": "Date de création", "cycling": "", @@ -81,6 +83,7 @@ "language": "Langue", "latitude": "Latitude", "license": "Licence", + "link-copied": "", "list": "{n, plural, =1 {Liste} other {Listes}}", "location": "Localisation", "login": "Connexion", @@ -130,6 +133,7 @@ "search-trails": "Chercher un itinéraire", "select-list": "Liste de choix", "settings": "Paramètres", + "share": "", "show-in-overview": "Voir dans l'aperçu", "show-on-map": "Voir sur la carte", "slogan": "Sauvegarder vos aventures !", diff --git a/web/src/lib/i18n/locales/hu.json b/web/src/lib/i18n/locales/hu.json index ea91b0a9..491e5dd8 100644 --- a/web/src/lib/i18n/locales/hu.json +++ b/web/src/lib/i18n/locales/hu.json @@ -18,10 +18,12 @@ "change": "Változás", "changelog": "Változás napló", "chinese": "Kínai (egyszerűsítve)", + "close": "", "comment": "{n, plural, =1 {Megjegyzés} other {Megjegyzés}}", "completed": "Teljesítve", "completion-status": "Befejezés állapota", "contribute": "Hozzájárulás", + "copy-link": "", "create-new-list": "Új lista létrehozása", "creation-date": "létrehozás dátuma", "cycling": "", @@ -81,6 +83,7 @@ "language": "Nyelf", "latitude": "Szélesség", "license": "License", + "link-copied": "", "list": "{n, plural, =1 {Lista} other {Listák}}", "location": "Helyszín", "login": "Bejelentkezés", @@ -130,6 +133,7 @@ "search-trails": "Nyomvonalak keresése", "select-list": "Lista kiválasztása", "settings": "Beállítások", + "share": "", "show-in-overview": "Áttekintés", "show-on-map": "Mutatás térképen", "slogan": "Mentse el a kalandjait!", diff --git a/web/src/lib/i18n/locales/nl.json b/web/src/lib/i18n/locales/nl.json index f62ca4f9..283b285b 100644 --- a/web/src/lib/i18n/locales/nl.json +++ b/web/src/lib/i18n/locales/nl.json @@ -18,10 +18,12 @@ "change": "Wijzigen", "changelog": "Wijzigingslog", "chinese": "Versimpeld Chinees", + "close": "", "comment": "{n, plural, =1 {Opmerking} other {Opmerkingen}}", "completed": "Voltooid", "completion-status": "Voltooiingsstatus", "contribute": "Bijdragen", + "copy-link": "", "create-new-list": "Nieuwe lijst", "creation-date": "Aanmaakdatum", "cycling": "", @@ -81,6 +83,7 @@ "language": "Taal", "latitude": "Breedtegraad", "license": "Licentie", + "link-copied": "", "list": "{n, plural, =1 {Lijst} other {Lijsten}}", "location": "Locatie", "login": "Inloggen", @@ -130,6 +133,7 @@ "search-trails": "Zoeken naar wandelroutes", "select-list": "Kies een lijst", "settings": "Instellingen", + "share": "", "show-in-overview": "Tonen op overzicht", "show-on-map": "Tonen op kaart", "slogan": "Bewaar je avonturen!", diff --git a/web/src/lib/i18n/locales/pl.json b/web/src/lib/i18n/locales/pl.json index 866f5367..2f240479 100644 --- a/web/src/lib/i18n/locales/pl.json +++ b/web/src/lib/i18n/locales/pl.json @@ -18,10 +18,12 @@ "change": "Zmień", "changelog": "Dziennik zmian", "chinese": "Uproszczony chiński", + "close": "", "comment": "{n, plural, =1 {Komentarz} other {Komentarze}}", "completed": "Zakończono", "completion-status": "Stan ukończenia", "contribute": "Kontrybuuj", + "copy-link": "", "create-new-list": "Stwórz nową listę", "creation-date": "Data dodania", "cycling": "", @@ -81,6 +83,7 @@ "language": "Język", "latitude": "Szerokość", "license": "Licencja", + "link-copied": "", "list": "{n, plural, =1 {Lista} other {Listy}}", "location": "Lokalizacja", "login": "Zaloguj się", @@ -130,6 +133,7 @@ "search-trails": "Szukaj ścieżek", "select-list": "Wybierz Listę", "settings": "Ustawienia", + "share": "", "show-in-overview": "Pokaż w przeglądzie", "show-on-map": "Pokaż na mapie", "slogan": "Zapisz swoją wyprawę!", diff --git a/web/src/lib/i18n/locales/pt.json b/web/src/lib/i18n/locales/pt.json index b68cd1f3..2be53605 100644 --- a/web/src/lib/i18n/locales/pt.json +++ b/web/src/lib/i18n/locales/pt.json @@ -18,10 +18,12 @@ "change": "Alterar", "changelog": "Registo de alterações", "chinese": "Chinês (simplificado)", + "close": "", "comment": "{n, plural, =1 {Comentário} other {Comentários}}", "completed": "Completada", "completion-status": "Status de conclusão", "contribute": "Contribuir", + "copy-link": "", "create-new-list": "Criar nova lista", "creation-date": "Data de criação", "cycling": "", @@ -81,6 +83,7 @@ "language": "Língua", "latitude": "Latitude", "license": "Licença", + "link-copied": "", "list": "{n, plural, =1 {Lista} other {Listas}}", "location": "Localização", "login": "Login", @@ -130,6 +133,7 @@ "search-trails": "Procurar trilhos", "select-list": "Selecionar lista", "settings": "Definições", + "share": "", "show-in-overview": "Mostrar na vista geral", "show-on-map": "Mostrar no mapa", "slogan": "Guarde as suas aventuras!", diff --git a/web/src/lib/i18n/locales/zh.json b/web/src/lib/i18n/locales/zh.json index 096147c9..b19243d9 100644 --- a/web/src/lib/i18n/locales/zh.json +++ b/web/src/lib/i18n/locales/zh.json @@ -18,10 +18,12 @@ "change": "更换", "changelog": "变更日志", "chinese": "华人", + "close": "", "comment": "{n, plural, =1 {评论} other {评论}}", "completed": "已完成", "completion-status": "完成状态", "contribute": "贡献", + "copy-link": "", "create-new-list": "创建新列表", "creation-date": "创建日期", "cycling": "", @@ -81,6 +83,7 @@ "language": "语言", "latitude": "维度", "license": "开源协议", + "link-copied": "", "list": "{n, plural, =1 {列表} other {列表}}", "location": "地点", "login": "登录", @@ -130,6 +133,7 @@ "search-trails": "搜索路线", "select-list": "选择列表", "settings": "设置", + "share": "", "show-in-overview": "详情中展示", "show-on-map": "地图中展示", "slogan": "保存你的冒险!", diff --git a/web/src/lib/stores/user_store.ts b/web/src/lib/stores/user_store.ts index de50fd8d..7b9e6c3b 100644 --- a/web/src/lib/stores/user_store.ts +++ b/web/src/lib/stores/user_store.ts @@ -23,6 +23,21 @@ export async function users_create(user: User) { await settings_create(settings); } +export async function users_search(q: string) { + let r = await fetch('/api/v1/user?' + new URLSearchParams({ + "filter": `username~"${q}"`, + }), { + method: 'GET', + }) + const response = await r.json() + + if (r.ok) { + return response.items; + } else { + throw new ClientResponseError(response) + } +} + export async function users_auth_methods(f: (url: RequestInfo | URL, config?: RequestInit) => Promise = fetch): Promise { const r = await f('/api/v1/auth/oauth', { method: 'GET', diff --git a/web/src/lib/util/gpx_util.ts b/web/src/lib/util/gpx_util.ts index ea132838..8730d7b7 100644 --- a/web/src/lib/util/gpx_util.ts +++ b/web/src/lib/util/gpx_util.ts @@ -16,7 +16,7 @@ export async function gpx2trail(gpxString: string) { const trail = new Trail(""); - trail.name = gpx.metadata?.name ?? ""; + trail.name = gpx.metadata?.name ?? `trail-${new Date().toISOString()}`; trail.description = gpx.metadata?.desc; diff --git a/web/src/routes/api/v1/user/+server.ts b/web/src/routes/api/v1/user/+server.ts index ae5cb0c6..e2877275 100644 --- a/web/src/routes/api/v1/user/+server.ts +++ b/web/src/routes/api/v1/user/+server.ts @@ -1,9 +1,25 @@ import { env } from '$env/dynamic/public'; +import type { User } from '$lib/models/user'; import { pb } from '$lib/pocketbase'; -import type { User } from '$lib/stores/user_store'; import { error, json, type RequestEvent } from '@sveltejs/kit' import { ClientResponseError } from 'pocketbase'; +export async function GET(event: RequestEvent) { + const page = event.url.searchParams.get("page") ?? "0"; + const perPage = event.url.searchParams.get("per-page") ?? "10"; + const sort = event.url.searchParams.get("sort") ?? "" + const filter = event.url.searchParams.get("filter") ?? ""; + + try { + const r = await pb.collection('users_anonymous') + .getList(parseInt(page), parseInt(perPage), { sort: sort ?? "", filter: filter ?? "" }) + return json(r) + } catch (e: any) { + throw error(e.status, e); + } +} + + export async function PUT(event: RequestEvent) { const data = await event.request.json() try {