Adds API docs annotations & generator (#927)

* initial commit

* fixes category api route

* fixes follow api routes

---------

Co-authored-by: Christian Beutel <>
This commit is contained in:
Flomp
2026-04-18 17:04:49 +02:00
committed by GitHub
parent da38cfc19a
commit b7b16e5a6e
91 changed files with 18668 additions and 37201 deletions

View File

@@ -28,7 +28,7 @@ export default defineConfig({
{
base: 'api-reference',
label: 'API Reference',
schema: 'wanderer.openapi.yaml',
schema: 'wanderer.openapi.json',
sidebar: {
operations: {

6616
docs/wanderer.openapi.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

827
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,7 @@
"postcss": "^8.5.6",
"svelte": "^5.53.6",
"svelte-check": "^4.3.6",
"sveltekit-openapi-generator": "^0.1.5",
"tailwindcss": "^4.0.0",
"tslib": "^2.4.1",
"typescript": "^5.9.3",
@@ -78,5 +79,10 @@
"three": "^0.182.0",
"vitest": "^4.1.4",
"zod": "^3.24.1"
},
"overrides": {
"sveltekit-openapi-generator": {
"vite": "$vite"
}
}
}

View File

@@ -1,79 +0,0 @@
<script lang="ts">
import type { User } from "$lib/models/user";
import { show_toast } from "$lib/stores/toast_store.svelte";
import { users_search } from "$lib/stores/user_store";
import { getFileURL } from "$lib/util/file_util";
import Search, { type SearchItem } from "./base/search.svelte";
import { _ } from "svelte-i18n";
interface Props {
label?: string;
value?: string;
includeSelf?: boolean;
clearAfterSelect?: boolean;
onclear?: () => void
onclick?: (item: SearchItem) => void
}
let {
label = "",
value = $bindable(""),
includeSelf = true,
clearAfterSelect = true,
onclear,
onclick
}: Props = $props();
let searchItems: SearchItem[] = $state([]);
async function updateUsers(q: string) {
if (!q.length) {
searchItems = [];
onclear?.();
return;
}
try {
const users: User[] = await users_search(q, includeSelf);
searchItems = users.map((u) => ({
text: u.username!,
value: u,
icon: "user",
}));
} catch (e) {
console.error(e);
show_toast({
type: "error",
icon: "close",
text: "Error during search",
});
}
}
function onClick(item: SearchItem) {
value = item.value.username ?? value;
onclick?.(item);
searchItems = [];
}
</script>
<Search
onupdate={(q) => updateUsers(q)}
onclick={(item) => onClick(item)}
placeholder={`${$_("username")}...`}
items={searchItems}
{clearAfterSelect}
{label}
bind:value
>
{#snippet prepend({ item })}
<img
class="rounded-full w-8 aspect-square mr-2"
src={getFileURL(item.value, item.value.avatar) ||
`https://api.dicebear.com/7.x/initials/svg?seed=${item.value.username}&backgroundType=gradientLinear`}
alt="avatar"
/>
{/snippet}
</Search>

File diff suppressed because it is too large Load Diff

View File

@@ -23,37 +23,6 @@ export async function users_create(user: User) {
return createdUser;
}
export async function users_search(q: string, includeSelf: boolean = true) {
const user = get(currentUser)
let r = await fetch('/api/v1/user/anonymous?' + new URLSearchParams({
"filter": `username~"${q}"${includeSelf ? '' : `&&id!="${user?.id}"`}`,
}), {
method: 'GET',
})
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
const response = await r.json()
return response.items;
}
export async function users_show(id: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
let r = await f(`/api/v1/user/anonymous/${id}`, {
method: 'GET',
})
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
const response: UserAnonymous = await r.json()
return response;
}
export async function users_auth_methods(f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch): Promise<AuthMethodsList> {
const r = await f('/api/v1/auth/oauth', {
method: 'GET',

View File

@@ -3,6 +3,32 @@ import { Collection, handleError, show } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
import type { APActivity, APRoot } from 'activitypub-types';
/**
* @swagger
* /api/v1/activitypub/activity/{id}:
* get:
* summary: Get ActivityPub activity
* description: Retrieves an ActivityPub Activity object by ID
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: ActivityPub Activity object
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const a = await show<Activity>(event, Collection.activitypub_activities)

View File

@@ -3,6 +3,32 @@ import { handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/activitypub/comment/{id}:
* get:
* summary: Get ActivityPub comment
* description: Retrieves an ActivityPub Comment object by ID (proxied from backend)
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: ActivityPub Comment object
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
const id = event.params.id;

View File

@@ -3,6 +3,32 @@ import { handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/activitypub/trail/{id}:
* get:
* summary: Get ActivityPub trail
* description: Retrieves an ActivityPub Trail object by ID (proxied from backend)
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: ActivityPub Trail object
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
const id = event.params.id;

View File

@@ -8,6 +8,32 @@ import { error, json, type RequestEvent } from '@sveltejs/kit';
import { type APActor, type APRoot } from 'activitypub-types';
/**
* @swagger
* /api/v1/activitypub/user/{handle}:
* get:
* summary: Get ActivityPub actor profile
* description: Retrieves an ActivityPub Person object for a user with public key
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* responses:
* 200:
* description: ActivityPub Person object with publicKey
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {

View File

@@ -9,6 +9,37 @@ import type { APOrderedCollectionPage, APRoot } from 'activitypub-types';
import type { ListResult } from 'pocketbase';
/**
* @swagger
* /api/v1/activitypub/user/{handle}/followers:
* get:
* summary: Get ActivityPub followers collection
* description: Retrieves an OrderedCollection paginated list of followers for a user
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* - in: query
* name: page
* schema:
* type: integer
* responses:
* 200:
* description: ActivityPub OrderedCollectionPage
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {

View File

@@ -1,15 +1,45 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import type { Actor } from '$lib/models/activitypub/actor';
import type { Follow } from '$lib/models/follow';
import { splitUsername } from '$lib/util/activitypub_util';
import { handleError } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
import { json, type RequestEvent } from '@sveltejs/kit';
import type { APOrderedCollectionPage, APRoot } from 'activitypub-types';
import type { ListResult } from 'pocketbase';
/**
* @swagger
* /api/v1/activitypub/user/{handle}/following:
* get:
* summary: Get ActivityPub following collection
* description: Retrieves an OrderedCollection paginated list of accounts the user follows
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* - in: query
* name: page
* schema:
* type: integer
* responses:
* 200:
* description: ActivityPub OrderedCollectionPage
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {

View File

@@ -4,6 +4,34 @@ import { handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
import type { APActivity } from 'activitypub-types';
/**
* @swagger
* /api/v1/activitypub/user/{handle}/inbox:
* post:
* summary: Receive ActivityPub activities
* description: Receives and processes incoming ActivityPub activities (Create, Update, Delete, Follow, etc.)
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* responses:
* 200:
* description: Activity processed
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {

View File

@@ -11,6 +11,40 @@ import type { APActivity, APOrderedCollectionPage, APRoot } from 'activitypub-ty
import type { ListResult } from 'pocketbase';
/**
* @swagger
* /api/v1/activitypub/user/{handle}/outbox:
* get:
* summary: Get ActivityPub outbox collection
* description: Retrieves an OrderedCollection paginated list of activities published by this user
* tags:
* - ActivityPub
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* responses:
* 200:
* description: ActivityPub OrderedCollectionPage
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {

View File

@@ -4,6 +4,62 @@ import { Collection, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
import { APITokenCreateSchema } from "$lib/models/api/api_token_schema";
/**
* @swagger
* /api/v1/api-token:
* get:
* summary: List API tokens
* description: Retrieves a paginated list of API tokens with optional filtering and sorting
* tags:
* - API Tokens
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* - in: query
* name: perPage
* schema:
* type: integer
* default: 30
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Success
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* perPage:
* type: integer
* totalItems:
* type: integer
* totalPages:
* type: integer
* items:
* type: array
* items:
* $ref: '#/components/schemas/APIToken'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<APIToken>(event, Collection.api_tokens);
@@ -14,7 +70,37 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/api-token:
* put:
* summary: Create API token
* description: Creates a new API token
* tags:
* - API Tokens
* parameters:
* - in: query
* name: expand
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/APITokenInput'
* responses:
* 201:
* description: Created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/APIToken'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<APIToken>(event, APITokenCreateSchema, Collection.api_tokens)
@@ -22,4 +108,4 @@ export async function PUT(event: RequestEvent) {
} catch (e) {
return handleError(e)
}
}
}

View File

@@ -1,6 +1,29 @@
import { Collection, handleError, remove } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/api-token/{id}:
* delete:
* summary: Delete API token
* description: Deletes an API token by ID
* tags:
* - API Tokens
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: API token ID (15 chars)
* responses:
* 200:
* description: Success
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.api_tokens)

View File

@@ -2,6 +2,28 @@ import { handleError } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
import { z } from "zod";
/**
* @swagger
* /api/v1/auth/confirm-reset:
* post:
* summary: Confirm password reset
* description: Confirms and applies a password reset with a valid token
* tags:
* - Authentication
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* responses:
* 200:
* description: Password reset completed
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const data = await event.request.json()

View File

@@ -2,6 +2,42 @@ import { handleError } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
import { z } from "zod";
/**
* @swagger
* /api/v1/auth/login:
* post:
* summary: Authenticate user
* description: Authenticates a user with email or username and password
* tags:
* - Authentication
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - password
* properties:
* email:
* type: string
* format: email
* username:
* type: string
* password:
* type: string
* responses:
* 200:
* description: User authenticated with auth token
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 400:
* description: Bad Request - Invalid credentials
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const data = await event.request.json();

View File

@@ -7,6 +7,24 @@ import { handleError } from "$lib/util/api_util";
const redirectURL = private_env.ORIGIN + "/login/redirect"
/**
* @swagger
* /api/v1/auth/oauth:
* get:
* summary: List OAuth providers
* description: Lists available OAuth2 authentication methods with provider images
* tags:
* - Authentication
* responses:
* 200:
* description: OAuth2 providers and configuration
* content:
* application/json:
* schema:
* type: object
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await event.locals.pb.collection('users').listAuthMethods();

View File

@@ -2,6 +2,34 @@ import { handleError } from "$lib/util/api_util";
import { error, json, type RequestEvent } from "@sveltejs/kit";
import { z } from "zod";
/**
* @swagger
* /api/v1/auth/reset:
* post:
* summary: Request password reset
* description: Sends a password reset email
* tags:
* - Authentication
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - email
* properties:
* email:
* type: string
* format: email
* responses:
* 200:
* description: Password reset email sent
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const data = await event.request.json()

View File

@@ -2,6 +2,44 @@ import type { Category } from "$lib/models/category";
import { Collection, handleError, list } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/category:
* get:
* summary: List categories
* description: Retrieves a paginated list of activity categories
* tags:
* - Categories
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* responses:
* 200:
* description: List of categories
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<Category>(event, Collection.categories);

View File

@@ -4,6 +4,51 @@ import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
import { type ListResult } from "pocketbase";
/**
* @swagger
* /api/v1/comment:
* get:
* summary: List comments
* tags:
* - Comments
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* - in: query
* name: handle
* schema:
* type: string
* description: Federated query parameter
* responses:
* 200:
* description: List of comments
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
if (!event.url.searchParams.has("handle")) {

View File

@@ -3,6 +3,35 @@ import type { Comment } from "$lib/models/comment";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/comment/{id}:
* get:
* summary: Get comment
* tags:
* - Comments
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Comment details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Comment'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<Comment>(event, Collection.comments)
@@ -12,6 +41,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/comment/{id}:
* post:
* summary: Update comment
* tags:
* - Comments
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CommentUpdateInput'
* responses:
* 200:
* description: Comment updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Comment'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<Comment>(event, CommentUpdateSchema, Collection.comments)
@@ -21,6 +83,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/comment/{id}:
* delete:
* summary: Delete comment
* tags:
* - Comments
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Comment deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.comments)

View File

@@ -2,6 +2,47 @@ import type { FeedItem } from "$lib/models/feed";
import { Collection, handleError, list } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/feed:
* get:
* summary: Get activity feed
* description: Retrieves the user's activity feed
* tags:
* - Feed
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Activity feed
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<FeedItem>(event, Collection.feed);

View File

@@ -1,6 +1,49 @@
import { error, json, type RequestEvent } from "@sveltejs/kit";
import { z } from "zod";
/**
* @swagger
* /api/v1/files/{collection}/{record}/{file}:
* get:
* summary: Download file
* description: Downloads a file from a record with optional thumbnail generation
* tags:
* - Files
* parameters:
* - in: path
* name: collection
* required: true
* schema:
* type: string
* - in: path
* name: record
* required: true
* schema:
* type: string
* - in: path
* name: file
* required: true
* schema:
* type: string
* - in: query
* name: thumb
* schema:
* type: string
* responses:
* 200:
* description: File download
* content:
* application/octet-stream:
* schema:
* type: string
* format: binary
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
const safeParams = z.object({

View File

@@ -7,6 +7,56 @@ import { json, type RequestEvent } from '@sveltejs/kit';
import type { APOrderedCollectionPage } from 'activitypub-types';
import { ClientResponseError, type ListResult } from "pocketbase";
/**
* @swagger
* /api/v1/follow:
* get:
* summary: List follows
* description: Retrieves follows or ActivityPub follower/following collections. Supports federated queries via handle parameter
* tags:
* - Follows
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* - in: query
* name: handle
* schema:
* type: string
* - in: query
* name: type
* schema:
* type: string
* enum: [followers, following]
* responses:
* 200:
* description: List of follows or ActivityPub collection
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
if (!event.url.searchParams.has("handle")) {

View File

@@ -2,6 +2,27 @@ import { Collection, handleError, remove } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/follow/{id}:
* delete:
* summary: Delete follow
* tags:
* - Follows
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Follow deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.follows)

View File

@@ -3,6 +3,46 @@ import type { Integration } from "$lib/models/integration";
import { Collection, create, handleError, list } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/integration:
* get:
* summary: List integrations
* tags:
* - Integrations
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of integrations
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<Integration>(event, Collection.integrations);
@@ -13,9 +53,34 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/integration:
* put:
* summary: Create integration
* tags:
* - Integrations
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/IntegrationInput'
* responses:
* 201:
* description: Integration created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Integration'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<Comment>(event, IntegrationCreateSchema, Collection.integrations)
const r = await create<Integration>(event, IntegrationCreateSchema, Collection.integrations)
return json(r);
} catch (e) {
return handleError(e)

View File

@@ -3,6 +3,35 @@ import type { Integration } from "$lib/models/integration";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/integration/{id}:
* get:
* summary: Get integration
* tags:
* - Integrations
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Integration details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Integration'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<Integration>(event, Collection.integrations)
@@ -12,6 +41,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/integration/{id}:
* post:
* summary: Update integration
* tags:
* - Integrations
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/IntegrationUpdateInput'
* responses:
* 200:
* description: Integration updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Integration'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<Integration>(event, IntegrationUpdateSchema, Collection.integrations)
@@ -21,6 +83,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/integration/{id}:
* delete:
* summary: Delete integration
* tags:
* - Integrations
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Integration deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.integrations)

View File

@@ -1,6 +1,24 @@
import { handleError } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/integration/hammerhead/login:
* get:
* summary: Get Hammerhead login endpoint
* description: Proxies to backend to get Hammerhead login configuration
* tags:
* - Integrations
* responses:
* 200:
* description: Hammerhead login endpoint
* content:
* application/json:
* schema:
* type: object
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await event.locals.pb.send("/integration/hammerhead/login", {

View File

@@ -1,6 +1,38 @@
import { handleError } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/integration/hammerhead/upload:
* post:
* summary: Upload via Hammerhead integration
* description: Proxies file upload to backend Hammerhead integration
* tags:
* - Integrations
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* required:
* - file
* properties:
* file:
* type: string
* format: binary
* responses:
* 200:
* description: Upload result
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const formData = await event.request.formData();

View File

@@ -1,6 +1,24 @@
import { handleError } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/integration/komoot/login:
* get:
* summary: Get Komoot login endpoint
* description: Proxies to backend to get Komoot login configuration
* tags:
* - Integrations
* responses:
* 200:
* description: Komoot login endpoint
* content:
* application/json:
* schema:
* type: object
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await event.locals.pb.send("/integration/komoot/login", {

View File

@@ -4,6 +4,43 @@ import type { ListShare } from '$lib/models/list_share';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/list-share:
* get:
* summary: List list shares
* description: Retrieves a paginated list of list shares with optional ActivityPub actor resolution
* tags:
* - List Shares
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: ListResult<ListShare>
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<ListShare>(event, Collection.list_share);
@@ -13,6 +50,32 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/list-share:
* put:
* summary: Create list share
* description: Creates a new list share. Converts ActivityPub actor IRI to ID
* tags:
* - List Shares
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListShareInput'
* responses:
* 201:
* description: List share created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListShare'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const data = await event.request.json();

View File

@@ -3,6 +3,32 @@ import type { ListShare } from "$lib/models/list_share";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/list-share/{id}:
* get:
* summary: Get list share
* description: Retrieves a list share by ID
* tags:
* - List Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: ListShare
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<ListShare>(event, Collection.list_share)
@@ -12,6 +38,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/list-share/{id}:
* post:
* summary: Update list share
* tags:
* - List Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListShareUpdateInput'
* responses:
* 200:
* description: List share updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListShare'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<ListShare>(event, ListShareUpdateSchema, Collection.list_share)
@@ -21,6 +80,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/list-share/{id}:
* delete:
* summary: Delete list share
* tags:
* - List Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: List share deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.list_share)

View File

@@ -3,6 +3,46 @@ import type { List } from '$lib/models/list';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/list:
* get:
* summary: List all lists
* tags:
* - Lists
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of lists
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<List>(event, Collection.lists);
@@ -18,6 +58,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/list:
* put:
* summary: Create list
* tags:
* - Lists
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListInput'
* responses:
* 201:
* description: List created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/List'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<List>(event, ListCreateSchema, Collection.lists)

View File

@@ -5,6 +5,80 @@ import { objectToFormData } from "$lib/util/file_util";
import { json, type RequestEvent } from "@sveltejs/kit";
import { ClientResponseError } from "pocketbase";
/**
* @swagger
* /api/v1/list/{id}:
* get:
* summary: Get list
* description: Retrieves a list by ID. Supports federated queries via handle parameter, fetching from remote instances and remapping file URLs
* tags:
* - Lists
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* - in: query
* name: handle
* schema:
* type: string
* responses:
* 200:
* description: List with optional federated data
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
* post:
* summary: Update list
* description: Updates a list by ID
* tags:
* - Lists
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* responses:
* 200:
* description: List
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
* delete:
* summary: Delete list
* description: Deletes a list by ID
* tags:
* - Lists
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Success
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
if (!event.url.searchParams.has("handle")) {

View File

@@ -1,7 +1,45 @@
import { Collection, handleError, upload } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
import type { List } from "postcss/lib/list";
import type { List } from "$lib/models/list";
/**
* @swagger
* /api/v1/list/{id}/file:
* post:
* summary: Upload list file
* description: Uploads a file (cover image) for a list
* tags:
* - Lists
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* responses:
* 200:
* description: File uploaded, list updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/List'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await upload<List>(event, Collection.lists);

View File

@@ -2,6 +2,28 @@ import type { List } from '$lib/models/list';
import { Collection, handleError, uploadCreate } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/list/form:
* put:
* summary: Create list with file upload
* description: Creates a new list with file upload (avatar)
* tags:
* - Lists
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* responses:
* 201:
* description: List
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await uploadCreate<List>(event, Collection.lists)

View File

@@ -2,6 +2,36 @@ import type { List } from "$lib/models/list";
import { Collection, handleError, uploadUpdate } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/list/form/{id}:
* post:
* summary: Update list with file upload
* description: Updates a list with file upload (avatar)
* tags:
* - Lists
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* responses:
* 200:
* description: List
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await uploadUpdate<List>(event, Collection.lists)

View File

@@ -2,6 +2,46 @@ import type { Notification } from '$lib/models/notification';
import { Collection, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/notification:
* get:
* summary: List notifications
* tags:
* - Notifications
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of notifications
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<Notification>(event, Collection.notifications);

View File

@@ -1,11 +1,42 @@
import { NotificationUpdateSchema } from "$lib/models/api/notification_schema";
import type { Notification } from "$lib/models/notification";
import { Collection, handleError, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/notification/{id}:
* post:
* summary: Update notification
* description: Updates a notification by ID (typically to mark as read)
* tags:
* - Notifications
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* responses:
* 200:
* description: Notification
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<Comment>(event, NotificationUpdateSchema, Collection.notifications)
const r = await update<Notification>(event, NotificationUpdateSchema, Collection.notifications)
return json(r);
} catch (e: any) {
return handleError(e)

View File

@@ -3,6 +3,32 @@ import { getActorResponseForHandle } from '$lib/util/activitypub_server_util';
import { handleError } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/profile/{handle}:
* get:
* summary: Get user profile
* description: Retrieves a user's profile by handle, with optional federation support
* tags:
* - Profiles
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Profile and actor data
* content:
* application/json:
* schema:
* type: object
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
const handle = event.params.handle;
if (!handle) {

View File

@@ -6,6 +6,40 @@ import { Collection, handleError } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
import { ClientResponseError, type ListResult } from 'pocketbase';
/**
* @swagger
* /api/v1/profile/{handle}/feed:
* get:
* summary: Get user activity feed
* description: Retrieves activity feed for a user, with federation support
* tags:
* - Profiles
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* responses:
* 200:
* description: FeedItem list
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
const handle = event.params.handle;
if (!handle) {

View File

@@ -6,6 +6,47 @@ import { error, json, type RequestEvent } from '@sveltejs/kit';
import type { SearchResponse } from 'meilisearch';
import { ClientResponseError } from 'pocketbase';
/**
* @swagger
* /api/v1/profile/{handle}/lists:
* post:
* summary: Search user lists
* description: Searches a user's lists via Meilisearch, with federation support
* tags:
* - Profiles
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - q
* properties:
* q:
* type: string
* options:
* type: object
* responses:
* 200:
* description: Meilisearch response with list results
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const handle = event.params.handle;
if (!handle) {

View File

@@ -5,6 +5,40 @@ import { Collection, handleError } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
import { ClientResponseError, type ListResult } from 'pocketbase';
/**
* @swagger
* /api/v1/profile/{handle}/stats:
* get:
* summary: Get user summit statistics
* description: Retrieves summit log statistics for a user, with federation support
* tags:
* - Profiles
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* responses:
* 200:
* description: SummitLog statistics
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
const handle = event.params.handle;
if (!handle) {

View File

@@ -5,6 +5,47 @@ import { error, json, type RequestEvent } from '@sveltejs/kit';
import type { SearchResponse } from 'meilisearch';
import { ClientResponseError } from 'pocketbase';
/**
* @swagger
* /api/v1/profile/{handle}/trails:
* post:
* summary: Search user trails
* description: Searches a user's trails via Meilisearch, with federation support
* tags:
* - Profiles
* parameters:
* - in: path
* name: handle
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - q
* properties:
* q:
* type: string
* options:
* type: object
* responses:
* 200:
* description: Meilisearch response with trail results
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const handle = event.params.handle;
if (!handle) {

View File

@@ -1,5 +1,44 @@
import { error, json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/search/{index}:
* post:
* summary: Search Meilisearch index
* description: Performs a search on a specific Meilisearch index
* tags:
* - Search
* parameters:
* - in: path
* name: index
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - q
* properties:
* q:
* type: string
* options:
* type: object
* responses:
* 200:
* description: Meilisearch search results
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const data = await event.request.json()

View File

@@ -4,6 +4,38 @@ import { handleError } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
import { ClientResponseError, type ListResult } from "pocketbase"
/**
* @swagger
* /api/v1/search/actor:
* get:
* summary: Search actors
* description: Searches for ActivityPub actors by username, combining local and federated results
* tags:
* - Search
* parameters:
* - in: query
* name: q
* required: true
* schema:
* type: string
* - in: query
* name: includeSelf
* schema:
* type: boolean
* responses:
* 200:
* description: Array of matching actors
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {

View File

@@ -1,5 +1,38 @@
import { error, json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/search/multi:
* post:
* summary: Multi-index search
* description: Performs batch searches across multiple Meilisearch indices
* tags:
* - Search
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - queries
* properties:
* queries:
* type: array
* items:
* type: object
* responses:
* 200:
* description: Combined Meilisearch results
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const data = await event.request.json()

View File

@@ -1,10 +1,36 @@
import { SettingsCreateSchema } from '$lib/models/api/settings_schema';
import type { Settings } from '$lib/models/settings';
import { Collection, create } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/settings:
* put:
* summary: Create settings
* tags:
* - Settings
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SettingsInput'
* responses:
* 201:
* description: Settings created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Settings'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<Comment>(event, SettingsCreateSchema, Collection.settings)
const r = await create<Settings>(event, SettingsCreateSchema, Collection.settings)
return json(r);
} catch (e: any) {

View File

@@ -4,6 +4,35 @@ import { Collection, handleError, remove, show, update } from "$lib/util/api_uti
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/settings/{id}:
* get:
* summary: Get settings
* tags:
* - Settings
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Settings details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Settings'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<Settings>(event, Collection.settings)
@@ -13,6 +42,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/settings/{id}:
* post:
* summary: Update settings
* tags:
* - Settings
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SettingsInput'
* responses:
* 200:
* description: Settings updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Settings'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<Settings>(event, SettingsCreateSchema, Collection.settings)
@@ -22,6 +84,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/settings/{id}:
* delete:
* summary: Delete settings
* tags:
* - Settings
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Settings deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.settings)

View File

@@ -4,6 +4,47 @@ import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
import { type ListResult } from "pocketbase";
/**
* @swagger
* /api/v1/summit-log:
* get:
* summary: List summit logs
* description: Retrieves a paginated list of summit logs with deduplication of federated data
* tags:
* - Summit Logs
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* - in: query
* name: handle
* schema:
* type: string
* responses:
* 200:
* description: ListResult<SummitLog> with local/remote items deduplicated
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
if (!event.url.searchParams.has("handle")) {
@@ -89,6 +130,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/summit-log:
* put:
* summary: Create summit log
* tags:
* - Summit Logs
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLogInput'
* responses:
* 201:
* description: Summit log created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLog'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<SummitLog>(event, SummitLogCreateSchema, Collection.summit_logs)

View File

@@ -4,6 +4,35 @@ import { Collection, handleError, remove, show, update } from "$lib/util/api_uti
import { error, json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/summit-log/{id}:
* get:
* summary: Get summit log
* tags:
* - Summit Logs
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Summit log details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLog'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<SummitLog>(event, Collection.summit_logs)
@@ -13,6 +42,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/summit-log/{id}:
* post:
* summary: Update summit log
* tags:
* - Summit Logs
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLogUpdateInput'
* responses:
* 200:
* description: Summit log updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLog'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<SummitLog>(event, SummitLogUpdateSchema, Collection.summit_logs)
@@ -22,6 +84,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/summit-log/{id}:
* delete:
* summary: Delete summit log
* tags:
* - Summit Logs
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Summit log deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.summit_logs)

View File

@@ -2,6 +2,44 @@ import type { SummitLog } from "$lib/models/summit_log";
import { Collection, upload } from "$lib/util/api_util";
import { error, json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/summit-log/{id}/file:
* post:
* summary: Upload summit log file
* description: Uploads a file (photo or GPX) for a summit log
* tags:
* - Summit Logs
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* responses:
* 200:
* description: File uploaded, summit log updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLog'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await upload<SummitLog>(event, Collection.summit_logs);

View File

@@ -2,6 +2,32 @@ import type { SummitLog } from '$lib/models/summit_log';
import { Collection, handleError, uploadCreate } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/summit-log/form:
* put:
* summary: Create summit log with file upload
* description: Creates a new summit log with file upload (photos/GPX) and date normalization
* tags:
* - Summit Logs
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* $ref: '#/components/schemas/SummitLogInput'
* responses:
* 201:
* description: Summit log created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLog'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await uploadCreate<SummitLog>(event, Collection.summit_logs)

View File

@@ -2,6 +2,40 @@ import type { SummitLog } from "$lib/models/summit_log";
import { Collection, handleError, uploadUpdate } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/summit-log/form/{id}:
* post:
* summary: Update summit log with file upload
* description: Updates a summit log with file upload (photos/GPX) and date normalization
* tags:
* - Summit Logs
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* $ref: '#/components/schemas/SummitLogUpdateInput'
* responses:
* 200:
* description: Summit log updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SummitLog'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await uploadUpdate<SummitLog>(event, Collection.summit_logs)

View File

@@ -3,6 +3,46 @@ import type { Tag } from '$lib/models/tag';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/tag:
* get:
* summary: List tags
* tags:
* - Tags
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of tags
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<Tag>(event, Collection.tags);
@@ -13,6 +53,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/tag:
* put:
* summary: Create tag
* tags:
* - Tags
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TagInput'
* responses:
* 201:
* description: Tag created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Tag'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<Tag>(event, TagCreateSchema, Collection.tags)

View File

@@ -3,6 +3,35 @@ import type { Tag } from "$lib/models/tag";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/tag/{id}:
* get:
* summary: Get tag
* tags:
* - Tags
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Tag details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Tag'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<Tag>(event, Collection.tags)
@@ -12,6 +41,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/tag/{id}:
* post:
* summary: Update tag
* tags:
* - Tags
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TagUpdateInput'
* responses:
* 200:
* description: Tag updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Tag'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<Tag>(event, TagUpdateSchema, Collection.tags)
@@ -21,6 +83,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/tag/{id}:
* delete:
* summary: Delete tag
* tags:
* - Tags
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Tag deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.tags)

View File

@@ -3,6 +3,46 @@ import type { TrailLike } from '$lib/models/trail_like';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail-like:
* get:
* summary: List trail likes
* tags:
* - Trail Likes
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of trail likes
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<TrailLike>(event, Collection.trail_share);
@@ -12,6 +52,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-like:
* put:
* summary: Create trail like
* tags:
* - Trail Likes
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLikeInput'
* responses:
* 201:
* description: Trail like created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLike'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<TrailLike>(event, TrailLikeCreateSchema, Collection.trail_like)

View File

@@ -2,6 +2,32 @@ import type { TrailLike } from "$lib/models/trail_like";
import { Collection, handleError, remove, show } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/trail-like/{id}:
* get:
* summary: Get trail like
* description: Retrieves a trail like by ID
* tags:
* - Trail Likes
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: TrailLike
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<TrailLike>(event, Collection.trail_like)
@@ -11,6 +37,27 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-like/{id}:
* delete:
* summary: Delete trail like
* tags:
* - Trail Likes
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Trail like deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.trail_like)

View File

@@ -3,7 +3,30 @@ import { Collection, handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
// this route exists so we can delete a like without knowing its ID
/**
* @swagger
* /api/v1/trail-like/delete:
* post:
* summary: Delete trail like by actor and trail
* description: Deletes a trail like without needing to know its ID. Uses actor and trail IDs to find and delete
* tags:
* - Trail Likes
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLikeInput'
* responses:
* 200:
* description: Trail like deleted
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {

View File

@@ -3,6 +3,46 @@ import type { TrailLinkShare } from '$lib/models/trail_link_share';
import { Collection, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail-link-share:
* get:
* summary: List trail link shares
* tags:
* - Trail Link Shares
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of trail link shares
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<TrailLinkShare>(event, Collection.trail_link_share);
@@ -12,6 +52,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-link-share:
* put:
* summary: Create trail link share
* tags:
* - Trail Link Shares
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLinkShareInput'
* responses:
* 201:
* description: Trail link share created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLinkShare'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const data = await event.request.json();

View File

@@ -3,6 +3,32 @@ import type { TrailLinkShare } from "$lib/models/trail_link_share";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/trail-link-share/{id}:
* get:
* summary: Get trail link share
* description: Retrieves a trail link share by ID
* tags:
* - Trail Link Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: TrailLinkShare
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<TrailLinkShare>(event, Collection.trail_link_share)
@@ -12,6 +38,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-link-share/{id}:
* post:
* summary: Update trail link share
* tags:
* - Trail Link Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLinkShareUpdateInput'
* responses:
* 200:
* description: Trail link share updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailLinkShare'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<TrailLinkShare>(event, TrailLinkShareUpdateSchema, Collection.trail_link_share)
@@ -21,6 +80,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-link-share/{id}:
* delete:
* summary: Delete trail link share
* tags:
* - Trail Link Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Trail link share deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.trail_link_share)

View File

@@ -4,6 +4,43 @@ import type { TrailShare } from '$lib/models/trail_share';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail-share:
* get:
* summary: List trail shares
* description: Retrieves a paginated list of trail shares with optional ActivityPub actor resolution
* tags:
* - Trail Shares
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: ListResult<TrailShare>
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<TrailShare>(event, Collection.trail_share);
@@ -13,6 +50,32 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-share:
* put:
* summary: Create trail share
* description: Creates a new trail share. Converts ActivityPub actor IRI to ID
* tags:
* - Trail Shares
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailShareInput'
* responses:
* 201:
* description: Trail share created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailShare'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const data = await event.request.json();

View File

@@ -3,6 +3,32 @@ import type { TrailShare } from "$lib/models/trail_share";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/trail-share/{id}:
* get:
* summary: Get trail share
* description: Retrieves a trail share by ID
* tags:
* - Trail Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: TrailShare
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<TrailShare>(event, Collection.trail_share)
@@ -12,6 +38,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-share/{id}:
* post:
* summary: Update trail share
* tags:
* - Trail Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailShareUpdateInput'
* responses:
* 200:
* description: Trail share updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailShare'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<TrailShare>(event, TrailShareUpdateSchema, Collection.trail_share)
@@ -21,6 +80,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail-share/{id}:
* delete:
* summary: Delete trail share
* tags:
* - Trail Shares
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Trail share deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.trail_share)

View File

@@ -3,6 +3,43 @@ import type { Trail } from '$lib/models/trail';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail:
* get:
* summary: List trails
* description: Retrieves a paginated list of trails with optional filtering and sorting
* tags:
* - Trails
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: ListResult<Trail>
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<Trail>(event, Collection.trails);
@@ -23,6 +60,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/trail:
* put:
* summary: Create trail
* tags:
* - Trails
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TrailCreateInput'
* responses:
* 201:
* description: Trail created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Trail'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<Trail>(event, TrailCreateSchema, Collection.trails)

View File

@@ -7,6 +7,40 @@ import { json, type RequestEvent } from "@sveltejs/kit";
import type PocketBase from "pocketbase";
import { ClientResponseError } from "pocketbase";
/**
* @swagger
* /api/v1/trail/{id}:
* get:
* summary: Get trail
* description: Retrieves a trail by ID. Supports federated queries via handle parameter, fetching from remote instances and remapping file URLs
* tags:
* - Trails
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* - in: query
* name: handle
* schema:
* type: string
* - in: query
* name: share
* schema:
* type: string
* responses:
* 200:
* description: Trail with optional federated data
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
// try to get the trail simply via the

View File

@@ -2,6 +2,44 @@ import type { Trail } from "$lib/models/trail";
import { Collection, upload } from "$lib/util/api_util";
import { error, json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/trail/{id}/file:
* post:
* summary: Upload trail file
* description: Uploads a file for a trail
* tags:
* - Trails
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* responses:
* 200:
* description: File uploaded
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Trail'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await upload<Trail>(event, Collection.trails);

View File

@@ -2,6 +2,35 @@ import { type TrailBoundingBox, type TrailFilterValues } from '$lib/models/trail
import { handleError } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail/bounding-box:
* get:
* summary: Get trail bounding box
* description: Retrieves geographic bounding box (lat/lon bounds) for user's trails
* tags:
* - Trails
* responses:
* 200:
* description: Bounding box coordinates
* content:
* application/json:
* schema:
* type: object
* properties:
* max_lat:
* type: number
* min_lat:
* type: number
* max_lon:
* type: number
* min_lon:
* type: number
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
if (!event.locals.pb.authStore.record) {
return json({

View File

@@ -1,6 +1,39 @@
import { handleError } from "$lib/util/api_util";
import { type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/trail/download:
* post:
* summary: Download file from URL
* description: Downloads a file from a URL and returns it as a blob
* tags:
* - Trails
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - url
* properties:
* url:
* type: string
* format: uri
* responses:
* 200:
* description: File blob
* content:
* application/octet-stream:
* schema:
* type: string
* format: binary
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const data = await event.request.json();

View File

@@ -2,6 +2,39 @@ import { type TrailFilterValues } from '$lib/models/trail';
import { handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail/filter:
* get:
* summary: Get trail filter values
* description: Retrieves min/max values for trail filtering (distance, elevation gain/loss)
* tags:
* - Trails
* responses:
* 200:
* description: Trail filter values (min/max for distance, elevation)
* content:
* application/json:
* schema:
* type: object
* properties:
* min_distance:
* type: number
* max_distance:
* type: number
* min_elevation_gain:
* type: number
* max_elevation_gain:
* type: number
* min_elevation_loss:
* type: number
* max_elevation_loss:
* type: number
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
if (!event.locals.pb.authStore.record) {
return json({

View File

@@ -2,6 +2,32 @@ import type { Trail } from '$lib/models/trail';
import { Collection, handleError, uploadCreate } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail/form:
* put:
* summary: Create trail with file upload
* description: Creates a new trail with file upload (GPX/photos) and date normalization
* tags:
* - Trails
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* $ref: '#/components/schemas/TrailCreateInput'
* responses:
* 201:
* description: Trail created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Trail'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await uploadCreate<Trail>(event, Collection.trails)

View File

@@ -2,6 +2,40 @@ import type { Trail } from "$lib/models/trail";
import { Collection, handleError, uploadUpdate } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/trail/form/{id}:
* post:
* summary: Update trail with file upload
* description: Updates a trail with file upload (GPX/photos) and date normalization
* tags:
* - Trails
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* $ref: '#/components/schemas/TrailUpdateInput'
* responses:
* 200:
* description: Trail updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Trail'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await uploadUpdate<Trail>(event, Collection.trails)

View File

@@ -2,6 +2,34 @@ import { TrailRecommendSchema } from '$lib/models/api/trail_schema';
import { handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/trail/recommend:
* get:
* summary: Get trail recommendations
* description: Retrieves random trail recommendations from Meilisearch
* tags:
* - Trails
* parameters:
* - in: query
* name: size
* schema:
* type: integer
* default: 10
* responses:
* 200:
* description: Array of recommended trails
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/Trail'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const searchParams = Object.fromEntries(event.url.searchParams);

View File

@@ -9,6 +9,40 @@ import { json, type RequestEvent } from "@sveltejs/kit";
import type { Hits, MeiliSearch } from "meilisearch";
import { ClientResponseError } from "pocketbase";
/**
* @swagger
* /api/v1/trail/upload:
* put:
* summary: Upload and parse GPX file as trail
* description: Uploads a GPX file, parses it to extract trail data, performs duplicate detection, and indexes in search
* tags:
* - Trails
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* name:
* type: string
* ignoreDuplicates:
* type: boolean
* responses:
* 201:
* description: Trail created from GPX
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Trail'
* 400:
* description: Bad Request - Invalid or empty GPX file
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const data = await event.request.formData();

View File

@@ -5,6 +5,31 @@ import { Collection, handleError } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
import { ClientResponseError } from 'pocketbase';
/**
* @swagger
* /api/v1/user:
* put:
* summary: Create user (sign up)
* tags:
* - Users
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/UserCreateInput'
* responses:
* 201:
* description: User created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 400:
* description: Bad Request - Signup disabled or invalid data
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
if (env.PUBLIC_DISABLE_SIGNUP === "true") {
throw new ClientResponseError({ status: 401, response: { messgage: "Forbidden" } })

View File

@@ -4,6 +4,35 @@ import type { User } from '$lib/models/user';
import { Collection, handleError, remove, show } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/user/{id}:
* get:
* summary: Get user by ID
* tags:
* - Users
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: User details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<User>(event, Collection.users)
@@ -14,6 +43,40 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/user/{id}:
* post:
* summary: Update user
* description: Updates a user. Handles password changes and email change requests
* tags:
* - Users
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/UserUpdateInput'
* responses:
* 200:
* description: User updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
@@ -38,6 +101,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/user/{id}:
* delete:
* summary: Delete user
* tags:
* - Users
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: User deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.users)

View File

@@ -2,6 +2,44 @@ import type { User } from "$lib/models/user";
import { Collection, upload } from "$lib/util/api_util";
import { error, json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/user/{id}/file:
* post:
* summary: Upload user file
* description: Uploads a file (avatar) for a user
* tags:
* - Users
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* responses:
* 200:
* description: File uploaded, user updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await upload<User>(event, Collection.users);

View File

@@ -1,23 +0,0 @@
import type { UserAnonymous } from '$lib/models/user';
import { Collection, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
export async function GET(event: RequestEvent) {
let filter = event.url.searchParams.get("filter");
if (!filter) {
filter = "private=false"
} else {
filter += "&&private=false"
}
event.url.searchParams.set("filter", filter);
try {
const r = await list<UserAnonymous>(event, Collection.users_anonymous);
return json(r)
} catch (e: any) {
return handleError(e);
}
}

View File

@@ -1,13 +0,0 @@
import type { UserAnonymous } from "$lib/models/user";
import { Collection, handleError, show } from '$lib/util/api_util';
import { error, json, type RequestEvent } from '@sveltejs/kit';
export async function GET(event: RequestEvent) {
try {
const r = await show<UserAnonymous>(event, Collection.users_anonymous)
return json(r)
} catch (e: any) {
return handleError(e);
}
}

View File

@@ -2,6 +2,32 @@ import { env } from '$env/dynamic/public';
import { error, json, type NumericRange, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/valhalla/height:
* post:
* summary: Get elevation data
* description: Queries Valhalla service for elevation data at coordinates
* tags:
* - Valhalla
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* responses:
* 200:
* description: Elevation data from Valhalla
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const data = await event.request.json()
if (!env.PUBLIC_VALHALLA_URL) {

View File

@@ -2,6 +2,32 @@ import { env } from '$env/dynamic/public';
import { error, json, type NumericRange, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/valhalla/route:
* post:
* summary: Get route data
* description: Queries Valhalla service for routing data
* tags:
* - Valhalla
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* responses:
* 200:
* description: Route data from Valhalla
* content:
* application/json:
* schema:
* type: object
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
const data = await event.request.json()
if (!env.PUBLIC_VALHALLA_URL) {

View File

@@ -3,6 +3,46 @@ import type { Waypoint } from '$lib/models/waypoint';
import { Collection, create, handleError, list } from '$lib/util/api_util';
import { json, type RequestEvent } from '@sveltejs/kit';
/**
* @swagger
* /api/v1/waypoint:
* get:
* summary: List waypoints
* tags:
* - Waypoints
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: perPage
* schema:
* type: integer
* - in: query
* name: sort
* schema:
* type: string
* - in: query
* name: filter
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: List of waypoints
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ListResult'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await list<Waypoint>(event, Collection.waypoints);
@@ -12,6 +52,31 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/waypoint:
* put:
* summary: Create waypoint
* tags:
* - Waypoints
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/WaypointInput'
* responses:
* 201:
* description: Waypoint created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Waypoint'
* 400:
* description: Bad Request
* 500:
* description: Internal Server Error
*/
export async function PUT(event: RequestEvent) {
try {
const r = await create<Waypoint>(event, WaypointCreateSchema, Collection.waypoints)

View File

@@ -3,6 +3,35 @@ import type { Waypoint } from "$lib/models/waypoint";
import { Collection, handleError, remove, show, update } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/waypoint/{id}:
* get:
* summary: Get waypoint
* tags:
* - Waypoints
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* - in: query
* name: expand
* schema:
* type: string
* responses:
* 200:
* description: Waypoint details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Waypoint'
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function GET(event: RequestEvent) {
try {
const r = await show<Waypoint>(event, Collection.waypoints)
@@ -12,6 +41,39 @@ export async function GET(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/waypoint/{id}:
* post:
* summary: Update waypoint
* tags:
* - Waypoints
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/WaypointUpdateInput'
* responses:
* 200:
* description: Waypoint updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Waypoint'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await update<Waypoint>(event, WaypointUpdateSchema, Collection.waypoints)
@@ -21,6 +83,27 @@ export async function POST(event: RequestEvent) {
}
}
/**
* @swagger
* /api/v1/waypoint/{id}:
* delete:
* summary: Delete waypoint
* tags:
* - Waypoints
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Waypoint deleted
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function DELETE(event: RequestEvent) {
try {
const r = await remove(event, Collection.waypoints)

View File

@@ -2,6 +2,44 @@ import type { Waypoint } from "$lib/models/waypoint";
import { Collection, handleError, upload } from "$lib/util/api_util";
import { json, type RequestEvent } from "@sveltejs/kit";
/**
* @swagger
* /api/v1/waypoint/{id}/file:
* post:
* summary: Upload waypoint file
* description: Uploads a file (photo) for a waypoint
* tags:
* - Waypoints
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* responses:
* 200:
* description: File uploaded, waypoint updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Waypoint'
* 400:
* description: Bad Request
* 404:
* description: Not Found
* 500:
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
try {
const r = await upload<Waypoint>(event, Collection.waypoints);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,22 @@
import tailwindcss from '@tailwindcss/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
import openapiPlugin from 'sveltekit-openapi-generator';
import fs from 'fs';
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
export default defineConfig({
plugins: [tailwindcss(), sveltekit()],
plugins: [openapiPlugin({
info: {
title: 'Wanderer API',
version: `${packageJson.version}`,
description: 'API documentation for wanderer backend',
},
outputPath: 'static/docs/api/wanderer.openapi.json',
include: ['src/routes/api/v1/**/*.{js,ts}'],
baseSchemasPath: 'src/lib/models/api/openapi_schemas.ts',
}),tailwindcss(), sveltekit()],
test: { include: ['src/**/*.{test,spec}.{js,ts}'] },
ssr: { noExternal: ['three'] },
...(process.env.WANDERER_ENV == "dev" ? {