mirror of
https://github.com/Dvorinka/Dash.git
synced 2026-10-28 15:51:27 +00:00
Backend: migration 0002 (sections/items/urls/settings), CRUD + fractional reorder, icon upload, settings, export/import, cached status pings. Handler tests cover reorder, URL scheme validation, settings round-trip. Frontend: shared board store over the generated client, dnd-kit layer (same-section, cross-section, section reorder persisted), URL chooser popover, add/edit service dialog, settings dialog, Bento + Cards renderers behind the shared seam, dark/light theme. Generated with [Devin](https://devin.ai) Authored By: TDvorak <[email protected]> Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
483 lines
12 KiB
YAML
483 lines
12 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Dash API
|
|
version: 0.1.0
|
|
description: >
|
|
Single source of truth for the Dash API. The TypeScript client in
|
|
packages/api-client is generated from this file — do not hand-write
|
|
API types in the frontend.
|
|
servers:
|
|
- url: /
|
|
paths:
|
|
/api/healthz:
|
|
get:
|
|
operationId: healthz
|
|
summary: Liveness + database reachability probe
|
|
responses:
|
|
"200":
|
|
description: Service healthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Health"
|
|
"503":
|
|
description: Database unreachable
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Health"
|
|
|
|
/api/sections:
|
|
get:
|
|
operationId: listSections
|
|
summary: Full board — sections with nested items and urls, ordered
|
|
responses:
|
|
"200":
|
|
description: Ordered board tree
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Section"
|
|
post:
|
|
operationId: createSection
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SectionInput"
|
|
responses:
|
|
"201":
|
|
description: Created section
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Section"
|
|
|
|
/api/sections/{id}:
|
|
patch:
|
|
operationId: updateSection
|
|
parameters:
|
|
- $ref: "#/components/parameters/id"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SectionPatch"
|
|
responses:
|
|
"200":
|
|
description: Updated section
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Section"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
delete:
|
|
operationId: deleteSection
|
|
parameters:
|
|
- $ref: "#/components/parameters/id"
|
|
responses:
|
|
"204":
|
|
description: Section and its items deleted
|
|
|
|
/api/sections/reorder:
|
|
post:
|
|
operationId: reorderSection
|
|
summary: Move a section between two neighbors (fractional position)
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Reorder"
|
|
responses:
|
|
"200":
|
|
description: Reordered
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Section"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
|
|
/api/items:
|
|
post:
|
|
operationId: createItem
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ItemInput"
|
|
responses:
|
|
"201":
|
|
description: Created item
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Item"
|
|
"400":
|
|
$ref: "#/components/responses/BadRequest"
|
|
|
|
/api/items/{id}:
|
|
patch:
|
|
operationId: updateItem
|
|
parameters:
|
|
- $ref: "#/components/parameters/id"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ItemPatch"
|
|
responses:
|
|
"200":
|
|
description: Updated item
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Item"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
delete:
|
|
operationId: deleteItem
|
|
parameters:
|
|
- $ref: "#/components/parameters/id"
|
|
responses:
|
|
"204":
|
|
description: Item deleted
|
|
|
|
/api/items/{id}/icon:
|
|
post:
|
|
operationId: uploadIcon
|
|
summary: Upload an icon file; returns the stored icon reference
|
|
parameters:
|
|
- $ref: "#/components/parameters/id"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required: [file]
|
|
properties:
|
|
file:
|
|
type: string
|
|
format: binary
|
|
responses:
|
|
"200":
|
|
description: Icon stored
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Item"
|
|
"400":
|
|
$ref: "#/components/responses/BadRequest"
|
|
|
|
/api/items/reorder:
|
|
post:
|
|
operationId: reorderItem
|
|
summary: Move an item between two neighbors, optionally across sections
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ItemReorder"
|
|
responses:
|
|
"200":
|
|
description: Reordered
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Item"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
|
|
/api/icons/{file}:
|
|
get:
|
|
operationId: getIcon
|
|
summary: Serve an uploaded icon file
|
|
parameters:
|
|
- name: file
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: Icon bytes
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
|
|
/api/status:
|
|
get:
|
|
operationId: getStatus
|
|
summary: Reachability of each service item's primary URL (~60s server cache)
|
|
responses:
|
|
"200":
|
|
description: Status map keyed by item id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties:
|
|
$ref: "#/components/schemas/Status"
|
|
|
|
/api/settings:
|
|
get:
|
|
operationId: getSettings
|
|
responses:
|
|
"200":
|
|
description: All settings as key/value map
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: true
|
|
put:
|
|
operationId: putSettings
|
|
summary: Merge settings (keys not sent are preserved)
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: true
|
|
responses:
|
|
"200":
|
|
description: Full settings map after merge
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: true
|
|
|
|
/api/export:
|
|
get:
|
|
operationId: exportBoard
|
|
summary: Full JSON backup (sections, items, urls, settings)
|
|
responses:
|
|
"200":
|
|
description: Board export
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Export"
|
|
|
|
/api/import:
|
|
post:
|
|
operationId: importBoard
|
|
summary: Replace board state from an export payload
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Export"
|
|
responses:
|
|
"200":
|
|
description: Imported board tree
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Section"
|
|
"400":
|
|
$ref: "#/components/responses/BadRequest"
|
|
|
|
components:
|
|
parameters:
|
|
id:
|
|
name: id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
|
|
responses:
|
|
NotFound:
|
|
description: Not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
BadRequest:
|
|
description: Invalid input
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
schemas:
|
|
Health:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: ok
|
|
Error:
|
|
type: object
|
|
required: [error]
|
|
properties:
|
|
error:
|
|
type: string
|
|
Url:
|
|
type: object
|
|
required: [id, url, label]
|
|
properties:
|
|
id:
|
|
type: string
|
|
url:
|
|
type: string
|
|
format: uri
|
|
label:
|
|
type: string
|
|
description: Tag such as "local" / "external" or a custom label
|
|
position:
|
|
type: number
|
|
UrlInput:
|
|
type: object
|
|
required: [url]
|
|
properties:
|
|
url:
|
|
type: string
|
|
format: uri
|
|
label:
|
|
type: string
|
|
default: ""
|
|
Item:
|
|
type: object
|
|
required: [id, sectionId, kind, name, position, urls]
|
|
properties:
|
|
id:
|
|
type: string
|
|
sectionId:
|
|
type: string
|
|
kind:
|
|
type: string
|
|
enum: [service, widget]
|
|
name:
|
|
type: string
|
|
icon:
|
|
type: string
|
|
description: "/api/icons/<file> path or remote URL; empty = letter tile"
|
|
position:
|
|
type: number
|
|
config:
|
|
type: object
|
|
nullable: true
|
|
additionalProperties: true
|
|
description: Widget config (Phase 2); null for services
|
|
urls:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Url"
|
|
ItemInput:
|
|
type: object
|
|
required: [sectionId, name]
|
|
properties:
|
|
sectionId:
|
|
type: string
|
|
name:
|
|
type: string
|
|
icon:
|
|
type: string
|
|
urls:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/UrlInput"
|
|
ItemPatch:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
icon:
|
|
type: string
|
|
urls:
|
|
type: array
|
|
description: Full replacement of the item's URL list
|
|
items:
|
|
$ref: "#/components/schemas/UrlInput"
|
|
Section:
|
|
type: object
|
|
required: [id, name, position, collapsed, items]
|
|
properties:
|
|
id:
|
|
type: string
|
|
name:
|
|
type: string
|
|
position:
|
|
type: number
|
|
collapsed:
|
|
type: boolean
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Item"
|
|
SectionInput:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name:
|
|
type: string
|
|
SectionPatch:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
collapsed:
|
|
type: boolean
|
|
Reorder:
|
|
type: object
|
|
required: [id]
|
|
properties:
|
|
id:
|
|
type: string
|
|
beforeId:
|
|
type: string
|
|
description: Neighbor now before the moved entity; null if first
|
|
afterId:
|
|
type: string
|
|
description: Neighbor now after the moved entity; null if last
|
|
ItemReorder:
|
|
allOf:
|
|
- $ref: "#/components/schemas/Reorder"
|
|
- type: object
|
|
properties:
|
|
sectionId:
|
|
type: string
|
|
description: Target section (defaults to current)
|
|
Status:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum: [up, down]
|
|
latencyMs:
|
|
type: integer
|
|
checkedAt:
|
|
type: string
|
|
format: date-time
|
|
Export:
|
|
type: object
|
|
required: [version, sections]
|
|
properties:
|
|
version:
|
|
type: integer
|
|
settings:
|
|
type: object
|
|
additionalProperties: true
|
|
sections:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Section"
|