1
0

feat(db): add swagger doc

Assisted-by: Junie:gemini-3-flash
Signed-off-by: Klagarge <remi@heredero.ch>
This commit is contained in:
2026-05-03 16:55:25 +02:00
parent c2a67684ed
commit 9163fd494b
8 changed files with 462 additions and 0 deletions

View File

@@ -18,5 +18,13 @@ cp .env.template .env
docker compose up -d
```
## Development
### Swagger Documentation
```bash
swag init
```
## Traefik
A traefik config file is available on [traefik.yml](./traefik.yml). It can be used with an OIDC provider ([Authentik](https://github.com/goauthentik/authentik) in our case) to control access to the database explorer.

159
db/src/docs/docs.go Normal file
View File

@@ -0,0 +1,159 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/rooms": {
"get": {
"description": "Get a list of all unique rooms from the measurement",
"produces": [
"application/json"
],
"tags": [
"rooms"
],
"summary": "Get all unique rooms",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/rooms/{room-id}/current": {
"get": {
"description": "Get the latest record for a specific room",
"produces": [
"application/json"
],
"tags": [
"rooms"
],
"summary": "Get current data for a room",
"parameters": [
{
"type": "string",
"description": "Room ID",
"name": "room-id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/rooms/{room-id}/history": {
"get": {
"description": "Get history for a specific room",
"produces": [
"application/json"
],
"tags": [
"rooms"
],
"summary": "Get history for a room",
"parameters": [
{
"type": "string",
"description": "Room ID",
"name": "room-id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "doc.db.e.kb28.ch",
BasePath: "/api/v1",
Schemes: []string{},
Title: "Gateway API",
Description: "This is a gateway API for IoT data.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

135
db/src/docs/swagger.json Normal file
View File

@@ -0,0 +1,135 @@
{
"swagger": "2.0",
"info": {
"description": "This is a gateway API for IoT data.",
"title": "Gateway API",
"contact": {},
"version": "1.0"
},
"host": "doc.db.e.kb28.ch",
"basePath": "/api/v1",
"paths": {
"/rooms": {
"get": {
"description": "Get a list of all unique rooms from the measurement",
"produces": [
"application/json"
],
"tags": [
"rooms"
],
"summary": "Get all unique rooms",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/rooms/{room-id}/current": {
"get": {
"description": "Get the latest record for a specific room",
"produces": [
"application/json"
],
"tags": [
"rooms"
],
"summary": "Get current data for a room",
"parameters": [
{
"type": "string",
"description": "Room ID",
"name": "room-id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/rooms/{room-id}/history": {
"get": {
"description": "Get history for a specific room",
"produces": [
"application/json"
],
"tags": [
"rooms"
],
"summary": "Get history for a room",
"parameters": [
{
"type": "string",
"description": "Room ID",
"name": "room-id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
}
}

90
db/src/docs/swagger.yaml Normal file
View File

@@ -0,0 +1,90 @@
basePath: /api/v1
host: doc.db.e.kb28.ch
info:
contact: {}
description: This is a gateway API for IoT data.
title: Gateway API
version: "1.0"
paths:
/rooms:
get:
description: Get a list of all unique rooms from the measurement
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
type: string
type: array
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Get all unique rooms
tags:
- rooms
/rooms/{room-id}/current:
get:
description: Get the latest record for a specific room
parameters:
- description: Room ID
in: path
name: room-id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
"404":
description: Not Found
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Get current data for a room
tags:
- rooms
/rooms/{room-id}/history:
get:
description: Get history for a specific room
parameters:
- description: Room ID
in: path
name: room-id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
additionalProperties: true
type: object
type: array
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Get history for a room
tags:
- rooms
swagger: "2.0"

View File

@@ -11,6 +11,9 @@ require (
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/apache/arrow-go/v18 v18.5.1 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
@@ -19,6 +22,10 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
@@ -27,10 +34,12 @@ require (
github.com/google/flatbuffers v25.12.19+incompatible // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/influxdata/line-protocol/v2 v2.2.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -40,6 +49,9 @@ require (
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/swaggo/gin-swagger v1.6.1 // indirect
github.com/swaggo/swag v1.16.4 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
@@ -58,5 +70,6 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/grpc v1.79.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -93,6 +93,11 @@ func influxConnection() *influx.InfluxGateway {
return gateway
}
// @title Gateway API
// @version 1.0
// @description This is a gateway API for IoT data.
// @host doc.db.e.kb28.ch
// @BasePath /api/v1
func main() {
// Load mapping configuration
mappingPath := os.Getenv("MAPPING_CONFIG_PATH")

View File

@@ -6,7 +6,11 @@ import (
"gateway/influx"
"net/http"
_ "gateway/docs"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
type RestGateway struct {
@@ -33,6 +37,8 @@ func (g *RestGateway) setupRoutes() {
v1.GET("/rooms/:room-id/current", g.getRoomCurrent)
v1.GET("/rooms/:room-id/history", g.getRoomHistory)
}
g.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
func (g *RestGateway) Run(addr string) error {
@@ -40,6 +46,14 @@ func (g *RestGateway) Run(addr string) error {
}
// GET /api/v1/rooms
// getRooms godoc
// @Summary Get all unique rooms
// @Description Get a list of all unique rooms from the measurement
// @Tags rooms
// @Produce json
// @Success 200 {array} string
// @Failure 500 {object} map[string]string
// @Router /rooms [get]
func (g *RestGateway) getRooms(c *gin.Context) {
// Query unique rooms from the measurement
query := fmt.Sprintf(`SELECT DISTINCT("room") FROM "%s"`, g.measurementName)
@@ -68,6 +82,16 @@ func (g *RestGateway) getRooms(c *gin.Context) {
}
// GET /api/v1/rooms/{room-id}/current
// getRoomCurrent godoc
// @Summary Get current data for a room
// @Description Get the latest record for a specific room
// @Tags rooms
// @Produce json
// @Param room-id path string true "Room ID"
// @Success 200 {object} map[string]any
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /rooms/{room-id}/current [get]
func (g *RestGateway) getRoomCurrent(c *gin.Context) {
roomID := c.Param("room-id")
@@ -95,6 +119,15 @@ func (g *RestGateway) getRoomCurrent(c *gin.Context) {
}
// GET /api/v1/rooms/{room-id}/history
// getRoomHistory godoc
// @Summary Get history for a room
// @Description Get history for a specific room
// @Tags rooms
// @Produce json
// @Param room-id path string true "Room ID"
// @Success 200 {array} map[string]any
// @Failure 500 {object} map[string]string
// @Router /rooms/{room-id}/history [get]
func (g *RestGateway) getRoomHistory(c *gin.Context) {
roomID := c.Param("room-id")

View File

@@ -1,4 +1,9 @@
http:
middlewares:
pi-db-doc-redirect:
redirectRegex:
regex: "^https://doc.db.e.kb28.ch/$"
replacement: "https://doc.db.e.kb28.ch/swagger/index.html"
# middlewares:
# oidc-auth-pi-db:
# plugin:
@@ -33,6 +38,15 @@ http:
service: pi-mqtt-management
tls:
certResolver: letsencrypt
pi-db-doc:
rule: "Host(`doc.db.e.kb28.ch`)"
entryPoints:
- websecure
service: pi-db-doc
tls:
certResolver: letsencrypt
middlewares:
- pi-db-doc-redirect
services:
pi-db-ui:
@@ -50,6 +64,11 @@ http:
servers:
- url: "http://192.168.42.211:15672"
passHostHeader: true
pi-db-doc:
loadBalancer:
servers:
- url: "http://192.168.42.211:8080"
passHostHeader: true
tcp:
routers: