From dcc8da83e7685b2d22a47184363c8545ddec54ae Mon Sep 17 00:00:00 2001 From: Christian Beutel <> Date: Sat, 16 Nov 2024 15:13:22 +0100 Subject: [PATCH] makes profiles public --- .../1731762218_updated_summit_logs.go | 38 +++++ .../1731762869_updated_users_anonymous.go | 156 ++++++++++++++++++ db/migrations/1731764020_updated_waypoints.go | 34 ++++ .../1731764074_updated_summit_logs.go | 38 +++++ web/src/lib/components/nav_bar.svelte | 6 +- web/src/lib/components/share_info.svelte | 4 +- .../summit_log/summit_log_table.svelte | 11 +- .../summit_log/summit_log_table_row.svelte | 33 +++- .../components/trail/trail_info_panel.svelte | 8 +- web/src/lib/models/summit_log.ts | 2 + web/src/lib/models/trail_share.ts | 4 +- web/src/lib/models/user.ts | 1 + web/src/lib/stores/summit_log_store.ts | 14 +- web/src/lib/stores/user_store.ts | 8 +- web/src/routes/api/v1/summit-log/+server.ts | 10 ++ .../routes/api/v1/summit-log/[id]/+server.ts | 6 + web/src/routes/api/v1/trail/[id]/+server.ts | 6 + .../routes/profile/{ => [id]}/+page.svelte | 14 +- web/src/routes/profile/{ => [id]}/+page.ts | 12 +- 19 files changed, 364 insertions(+), 41 deletions(-) create mode 100644 db/migrations/1731762218_updated_summit_logs.go create mode 100644 db/migrations/1731762869_updated_users_anonymous.go create mode 100644 db/migrations/1731764020_updated_waypoints.go create mode 100644 db/migrations/1731764074_updated_summit_logs.go rename web/src/routes/profile/{ => [id]}/+page.svelte (97%) rename web/src/routes/profile/{ => [id]}/+page.ts (68%) diff --git a/db/migrations/1731762218_updated_summit_logs.go b/db/migrations/1731762218_updated_summit_logs.go new file mode 100644 index 00000000..a5371b8d --- /dev/null +++ b/db/migrations/1731762218_updated_summit_logs.go @@ -0,0 +1,38 @@ +package migrations + +import ( + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/tools/types" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("dd2l9a4vxpy2ni8") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public ?= true) || (author = @request.auth.id)") + + collection.ViewRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public ?= true) || (author = @request.auth.id)") + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("dd2l9a4vxpy2ni8") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public = true) || (author = @request.auth.id)") + + collection.ViewRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public = true) || (author = @request.auth.id)") + + return dao.SaveCollection(collection) + }) +} diff --git a/db/migrations/1731762869_updated_users_anonymous.go b/db/migrations/1731762869_updated_users_anonymous.go new file mode 100644 index 00000000..07164348 --- /dev/null +++ b/db/migrations/1731762869_updated_users_anonymous.go @@ -0,0 +1,156 @@ +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("xku110v5a5xbufa") + if err != nil { + return err + } + + options := map[string]any{} + if err := json.Unmarshal([]byte(`{ + "query": "SELECT id, username, avatar, created FROM users" + }`), &options); err != nil { + return err + } + collection.SetOptions(options) + + // remove + collection.Schema.RemoveField("ogrxf4ps") + + // remove + collection.Schema.RemoveField("7k4bozux") + + // add + new_username := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "kvteagv6", + "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) + + // add + new_avatar := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "m9efnpak", + "name": "avatar", + "type": "file", + "required": false, + "presentable": false, + "unique": false, + "options": { + "mimeTypes": [ + "image/jpeg", + "image/png", + "image/svg+xml", + "image/gif", + "image/webp" + ], + "thumbs": null, + "maxSelect": 1, + "maxSize": 5242880, + "protected": false + } + }`), new_avatar); err != nil { + return err + } + collection.Schema.AddField(new_avatar) + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("xku110v5a5xbufa") + if err != nil { + return err + } + + options := map[string]any{} + if err := json.Unmarshal([]byte(`{ + "query": "SELECT id, username, avatar FROM users" + }`), &options); err != nil { + return err + } + collection.SetOptions(options) + + // add + del_username := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "ogrxf4ps", + "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) + + // add + del_avatar := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "7k4bozux", + "name": "avatar", + "type": "file", + "required": false, + "presentable": false, + "unique": false, + "options": { + "mimeTypes": [ + "image/jpeg", + "image/png", + "image/svg+xml", + "image/gif", + "image/webp" + ], + "thumbs": null, + "maxSelect": 1, + "maxSize": 5242880, + "protected": false + } + }`), del_avatar); err != nil { + return err + } + collection.Schema.AddField(del_avatar) + + // remove + collection.Schema.RemoveField("kvteagv6") + + // remove + collection.Schema.RemoveField("m9efnpak") + + return dao.SaveCollection(collection) + }) +} diff --git a/db/migrations/1731764020_updated_waypoints.go b/db/migrations/1731764020_updated_waypoints.go new file mode 100644 index 00000000..44b39785 --- /dev/null +++ b/db/migrations/1731764020_updated_waypoints.go @@ -0,0 +1,34 @@ +package migrations + +import ( + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/tools/types" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("goeo2ubp103rzp9") + if err != nil { + return err + } + + collection.ViewRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.waypoints.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.waypoints.id ?= id && @collection.trails.public ?= true) || \n(@collection.trail_share.trail.waypoints.id ?= id && @collection.trail_share.user ?= @request.auth.id) ||\n(author = @request.auth.id)") + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("goeo2ubp103rzp9") + if err != nil { + return err + } + + collection.ViewRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.waypoints.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.waypoints.id ?= id && @collection.trails.public ?= true) || (author = @request.auth.id)") + + return dao.SaveCollection(collection) + }) +} diff --git a/db/migrations/1731764074_updated_summit_logs.go b/db/migrations/1731764074_updated_summit_logs.go new file mode 100644 index 00000000..732b80e2 --- /dev/null +++ b/db/migrations/1731764074_updated_summit_logs.go @@ -0,0 +1,38 @@ +package migrations + +import ( + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/tools/types" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("dd2l9a4vxpy2ni8") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public ?= true) ||\n(@collection.trail_share.trail.summit_logs.id ?= id && @collection.trail_share.user ?= @request.auth.id) ||\n(author = @request.auth.id)") + + collection.ViewRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public ?= true) || \n(@collection.trail_share.trail.summit_logs.id ?= id && @collection.trail_share.user ?= @request.auth.id) ||\n(author = @request.auth.id)") + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("dd2l9a4vxpy2ni8") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public ?= true) || (author = @request.auth.id)") + + collection.ViewRule = types.Pointer("(@request.auth.id != \"\" && @collection.trails.summit_logs.id ?= id && @collection.trails.author ?= @request.auth.id) || (@collection.trails.summit_logs.id ?= id && @collection.trails.public ?= true) || (author = @request.auth.id)") + + return dao.SaveCollection(collection) + }) +} diff --git a/web/src/lib/components/nav_bar.svelte b/web/src/lib/components/nav_bar.svelte index fe78731b..1dedfe08 100644 --- a/web/src/lib/components/nav_bar.svelte +++ b/web/src/lib/components/nav_bar.svelte @@ -78,7 +78,7 @@ function handleDropdownClick(item: { text: string; value: any }) { if (item.value == "profile") { - goto("/profile"); + goto(`/profile/${$currentUser?.id}`); } else if (item.value == "logout") { logout(); window.location.href = "/"; @@ -128,7 +128,7 @@

- + avatar - +

{$currentUser.username}

{$currentUser.email} diff --git a/web/src/lib/components/share_info.svelte b/web/src/lib/components/share_info.svelte index cb789fe3..a1a82eaa 100644 --- a/web/src/lib/components/share_info.svelte +++ b/web/src/lib/components/share_info.svelte @@ -1,7 +1,7 @@ @@ -130,6 +129,26 @@ > {/if} + {#if showAuthor && log.expand.author} + +

+ + avatar + +

+ + {/if}
diff --git a/web/src/lib/components/trail/trail_info_panel.svelte b/web/src/lib/components/trail/trail_info_panel.svelte index c58943f8..2dea8a60 100644 --- a/web/src/lib/components/trail/trail_info_panel.svelte +++ b/web/src/lib/components/trail/trail_info_panel.svelte @@ -172,7 +172,7 @@ class="trail-info-panel mx-auto {mode == 'list' ? '' : 'border border-input-border rounded-3xl'} h-full" - style="max-width: min(100%, 64rem);" + style="max-width: min(100%, 76rem);" >
@@ -239,7 +239,7 @@ `https://api.dicebear.com/7.x/initials/svg?seed=${trail.expand.author.username}&backgroundType=gradientLinear`} alt="avatar" /> - {trail.expand.author.username} + {trail.expand.author.username}

{/if}
@@ -357,8 +357,8 @@
{/if} {#if activeTab == 3} -
- +
{/if} diff --git a/web/src/lib/models/summit_log.ts b/web/src/lib/models/summit_log.ts index 5ac75443..e3429b7c 100644 --- a/web/src/lib/models/summit_log.ts +++ b/web/src/lib/models/summit_log.ts @@ -1,4 +1,5 @@ import type { Trail } from "./trail"; +import type { UserAnonymous } from "./user"; class SummitLog { id?: string; @@ -15,6 +16,7 @@ class SummitLog { expand: { gpx_data?: string; trails_via_summit_logs?: Trail[]; + author?: UserAnonymous } constructor(date: string, params?: { id?: string, text?: string, distance?: number, elevation_loss?: number, elevation_gain?: number, duration?: number }) { diff --git a/web/src/lib/models/trail_share.ts b/web/src/lib/models/trail_share.ts index e84e8c24..3f9774c3 100644 --- a/web/src/lib/models/trail_share.ts +++ b/web/src/lib/models/trail_share.ts @@ -1,4 +1,4 @@ -import type { User } from "./user"; +import type { User, UserAnonymous } from "./user"; export class TrailShare { id?: string; @@ -6,7 +6,7 @@ export class TrailShare { trail: string; permission: "view" | "edit" expand?: { - user: User + user: UserAnonymous } constructor(user: string, trail: string, permission: "view" | "edit") { diff --git a/web/src/lib/models/user.ts b/web/src/lib/models/user.ts index 4759f081..3be3c99d 100644 --- a/web/src/lib/models/user.ts +++ b/web/src/lib/models/user.ts @@ -14,4 +14,5 @@ export type UserAnonymous = { id: string, username?: string, avatar?: string; + created?: string; } \ No newline at end of file diff --git a/web/src/lib/stores/summit_log_store.ts b/web/src/lib/stores/summit_log_store.ts index 6627f904..36b1bdf0 100644 --- a/web/src/lib/stores/summit_log_store.ts +++ b/web/src/lib/stores/summit_log_store.ts @@ -7,8 +7,10 @@ import { fetchGPX } from "./trail_store"; export const summitLog: Writable = writable(new SummitLog(new Date().toISOString().substring(0, 10))); export const summitLogs: Writable = writable([]); -export async function summit_logs_index(filter?: SummitLogFilter, f: (url: RequestInfo | URL, config?: RequestInit) => Promise = fetch) { - const filterText = filter ? buildFilterText(filter) : ""; +export async function summit_logs_index(author: string, filter?: SummitLogFilter, f: (url: RequestInfo | URL, config?: RequestInit) => Promise = fetch) { + + let filterText = `author='${author}'` + filterText += filter ? "&&" + buildFilterText(filter) : ""; const r = await f('/api/v1/summit-log?' + new URLSearchParams({ filter: filterText, @@ -35,7 +37,7 @@ export async function summit_logs_index(filter?: SummitLogFilter, f: (url: Reque } summitLogs.set(fetchedSummitLogs); - + return fetchedSummitLogs; } @@ -117,15 +119,15 @@ function buildFilterText(filter: SummitLogFilter,): string { let filterText: string = ""; if (filter.category.length > 0) { - filterText += `trails_via_summit_logs.category != null && '${filter.category.join(",")}' ~ trails_via_summit_logs.category`; + filterText += `trails_via_summit_logs.category!=null&&'${filter.category.join(",")}'~trails_via_summit_logs.category`; } if (filter.startDate) { - filterText += `${filter.category.length ? ' && ' : ''}date >= '${filter.startDate}'` + filterText += `${filter.category.length ? '&&' : ''}date>='${filter.startDate}'` } if (filter.endDate) { - filterText += `${filter.category.length || filter.startDate ? ' && ' : ''} date <= '${filter.endDate}'` + filterText += `${filter.category.length || filter.startDate ? '&&' : ''}date<='${filter.endDate}'` } return filterText; diff --git a/web/src/lib/stores/user_store.ts b/web/src/lib/stores/user_store.ts index 7bc9dbe9..7f7aa1a0 100644 --- a/web/src/lib/stores/user_store.ts +++ b/web/src/lib/stores/user_store.ts @@ -1,4 +1,4 @@ -import type { User } from "$lib/models/user"; +import type { User, UserAnonymous } from "$lib/models/user"; import { pb } from "$lib/pocketbase"; import { ClientResponseError, type AuthMethodsList } from "pocketbase"; import { writable, type Writable } from "svelte/store"; @@ -35,11 +35,11 @@ export async function users_search(q: string, includeSelf: boolean = true) { } } -export async function users_show(id: string) { - let r = await fetch(`/api/v1/user/anonymous/${id}`, { +export async function users_show(id: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise = fetch) { + let r = await f(`/api/v1/user/anonymous/${id}`, { method: 'GET', }) - const response = await r.json() + const response: UserAnonymous = await r.json() if (r.ok) { return response; diff --git a/web/src/routes/api/v1/summit-log/+server.ts b/web/src/routes/api/v1/summit-log/+server.ts index 54962d2e..257c2413 100644 --- a/web/src/routes/api/v1/summit-log/+server.ts +++ b/web/src/routes/api/v1/summit-log/+server.ts @@ -11,6 +11,16 @@ export async function GET(event: RequestEvent) { sort: "+date", filter: filter }) + + for (const t of r) { + if (!t.author || !pb.authStore.model) { + continue; + } + if (!t.expand) { + t.expand = {} as any + } + t.expand.author = await pb.collection("users_anonymous").getOne(t.author); + } return json(r) } catch (e: any) { throw error(e.status, e); diff --git a/web/src/routes/api/v1/summit-log/[id]/+server.ts b/web/src/routes/api/v1/summit-log/[id]/+server.ts index 92cc0e70..ba954552 100644 --- a/web/src/routes/api/v1/summit-log/[id]/+server.ts +++ b/web/src/routes/api/v1/summit-log/[id]/+server.ts @@ -7,6 +7,12 @@ export async function GET(event: RequestEvent) { try { const r = await pb.collection('summit_logs') .getOne(event.params.id as string) + + if (!r.expand) { + r.expand = {} as any + } + r.expand.author = await pb.collection("users_anonymous").getOne(r.author!); + return json(r) } catch (e: any) { throw error(e.status, e); diff --git a/web/src/routes/api/v1/trail/[id]/+server.ts b/web/src/routes/api/v1/trail/[id]/+server.ts index 66b5d86c..892a197a 100644 --- a/web/src/routes/api/v1/trail/[id]/+server.ts +++ b/web/src/routes/api/v1/trail/[id]/+server.ts @@ -21,9 +21,15 @@ export async function GET(event: RequestEvent) { r.date = r.date?.substring(0, 10) ?? "" for (const log of r.expand?.summit_logs ?? []) { log.date = log.date.substring(0, 10) + + if (!log.expand) { + log.expand = {} as any + } + log.expand.author = await pb.collection("users_anonymous").getOne(log.author!); } return json(r) } catch (e: any) { + console.error(e) throw error(e.status || 500, e); } } diff --git a/web/src/routes/profile/+page.svelte b/web/src/routes/profile/[id]/+page.svelte similarity index 97% rename from web/src/routes/profile/+page.svelte rename to web/src/routes/profile/[id]/+page.svelte index c4d6ba55..658f102d 100644 --- a/web/src/routes/profile/+page.svelte +++ b/web/src/routes/profile/[id]/+page.svelte @@ -218,7 +218,7 @@ } async function loadSummitLogs() { - const logs = await summit_logs_index(filter); + const logs = await summit_logs_index($page.params.id, filter); summitLogs.set(logs); } @@ -230,21 +230,21 @@
- {#if $currentUser} + {#if data.user}
avatar

- {$currentUser.username} + {data.user.username}

Joined: - {new Date($currentUser.created ?? "").toLocaleDateString( + {new Date(data.user.created ?? "").toLocaleDateString( undefined, { month: "2-digit", @@ -420,7 +420,7 @@ {$_("all-activities")} -

+
diff --git a/web/src/routes/profile/+page.ts b/web/src/routes/profile/[id]/+page.ts similarity index 68% rename from web/src/routes/profile/+page.ts rename to web/src/routes/profile/[id]/+page.ts index 04bab69c..35bf32b6 100644 --- a/web/src/routes/profile/+page.ts +++ b/web/src/routes/profile/[id]/+page.ts @@ -1,10 +1,15 @@ import type { SummitLogFilter } from "$lib/models/summit_log"; import { categories_index } from "$lib/stores/category_store"; import { summit_logs_index } from "$lib/stores/summit_log_store"; -import { type ServerLoad } from "@sveltejs/kit"; +import { users_show } from "$lib/stores/user_store"; +import { error, type ServerLoad } from "@sveltejs/kit"; export const load: ServerLoad = async ({ params, locals, fetch }) => { + if(!params.id) { + error(404, "Not found") + } + const date = new Date() date.setUTCHours(6) const y = date.getFullYear() @@ -19,7 +24,8 @@ export const load: ServerLoad = async ({ params, locals, fetch }) => { endDate: lastDay.toISOString().slice(0, 10), category: [] } - const logs = await summit_logs_index(filter, fetch); + const logs = await summit_logs_index(params.id, filter, fetch); + const user = await users_show(params.id, fetch); - return {filter} + return { filter, user } }; \ No newline at end of file