From 90efa3619337cda7e290d585c28627ed7103eb63 Mon Sep 17 00:00:00 2001 From: yusing Date: Sat, 16 Aug 2025 19:57:14 +0800 Subject: [PATCH] refactor(api): better type safety, split homepage override apis, fixed favicon api docs --- internal/api/handler.go | 5 +- internal/api/v1/docker/containers.go | 12 +- internal/api/v1/docs/docs.go | 459 ++++++++++------ internal/api/v1/docs/swagger.json | 501 +++++++++++------- internal/api/v1/docs/swagger.yaml | 341 ++++++++---- internal/api/v1/favicon.go | 17 +- internal/api/v1/homepage/overrides.go | 155 +++--- internal/api/v1/metrics/system_info.go | 9 +- internal/homepage/homepage.go | 4 +- internal/homepage/list_icons.go | 14 +- internal/metrics/systeminfo/system_info.go | 66 +-- .../metrics/systeminfo/system_info_test.go | 12 +- 12 files changed, 1013 insertions(+), 582 deletions(-) diff --git a/internal/api/handler.go b/internal/api/handler.go index 6b287127..d251812a 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -77,7 +77,10 @@ func NewHandler() *gin.Engine { { homepage.GET("/categories", homepageApi.Categories) homepage.GET("/items", homepageApi.Items) - homepage.POST("/set", homepageApi.Set) + homepage.POST("/set/item", homepageApi.SetItem) + homepage.POST("/set/items_batch", homepageApi.SetItemsBatch) + homepage.POST("/set/item_visible", homepageApi.SetItemVisible) + homepage.POST("/set/category_order", homepageApi.SetCategoryOrder) } cert := v1.Group("/cert") diff --git a/internal/api/v1/docker/containers.go b/internal/api/v1/docker/containers.go index e1838f53..6d8c657b 100644 --- a/internal/api/v1/docker/containers.go +++ b/internal/api/v1/docker/containers.go @@ -9,12 +9,14 @@ import ( "github.com/yusing/go-proxy/internal/gperr" ) +type ContainerState = container.ContainerState // @name ContainerState + type Container struct { - Server string `json:"server"` - Name string `json:"name"` - ID string `json:"id"` - Image string `json:"image"` - State string `json:"state"` + Server string `json:"server"` + Name string `json:"name"` + ID string `json:"id"` + Image string `json:"image"` + State ContainerState `json:"state"` } // @name ContainerResponse // @BasePath /api/v1 diff --git a/internal/api/v1/docs/docs.go b/internal/api/v1/docs/docs.go index 2513d0f0..3cfb5800 100644 --- a/internal/api/v1/docs/docs.go +++ b/internal/api/v1/docs/docs.go @@ -582,25 +582,25 @@ const docTemplate = `{ } }, "400": { - "description": "Bad Request", + "description": "Bad Request: alias is empty or route is not HTTPRoute", "schema": { "$ref": "#/definitions/ErrorResponse" } }, "403": { - "description": "Forbidden", + "description": "Forbidden: unauthorized", "schema": { "$ref": "#/definitions/ErrorResponse" } }, "404": { - "description": "Not Found", + "description": "Not Found: route or icon not found", "schema": { "$ref": "#/definitions/ErrorResponse" } }, "500": { - "description": "Internal Server Error", + "description": "Internal Server Error: internal error", "schema": { "$ref": "#/definitions/ErrorResponse" } @@ -979,9 +979,9 @@ const docTemplate = `{ "x-id": "items" } }, - "/homepage/set": { + "/homepage/set/category_order": { "post": { - "description": "Set homepage overrides", + "description": "Set homepage category order.", "consumes": [ "application/json" ], @@ -991,90 +991,15 @@ const docTemplate = `{ "tags": [ "homepage" ], - "summary": "Set homepage overrides", + "summary": "Set homepage category order", "parameters": [ - { - "description": "Override single item", - "name": "request", - "in": "body", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideItemParams" - } - } - } - ] - } - }, - { - "description": "Override multiple items", - "name": "request", - "in": "body", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideItemsBatchParams" - } - } - } - ] - } - }, { "description": "Override category order", "name": "request", "in": "body", "required": true, "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideCategoryOrderParams" - } - } - } - ] - } - }, - { - "description": "Override item visibility", - "name": "request", - "in": "body", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideItemVisibleParams" - } - } - } - ] + "$ref": "#/definitions/HomepageOverrideCategoryOrderParams" } } ], @@ -1098,7 +1023,148 @@ const docTemplate = `{ } } }, - "x-id": "set" + "x-id": "set-category-order" + } + }, + "/homepage/set/item": { + "post": { + "description": "Override single homepage item.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "homepage" + ], + "summary": "Override single homepage item", + "parameters": [ + { + "description": "Override single item", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/HomepageOverrideItemParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "set-item" + } + }, + "/homepage/set/item_visible": { + "post": { + "description": "POST list of item ids and visibility value.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "homepage" + ], + "summary": "Set homepage item visibility", + "parameters": [ + { + "description": "Set item visibility", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/HomepageOverrideItemVisibleParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "set-item-visible" + } + }, + "/homepage/set/items_batch": { + "post": { + "description": "Override multiple homepage items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "homepage" + ], + "summary": "Override multiple homepage items", + "parameters": [ + { + "description": "Override multiple items", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/HomepageOverrideItemsBatchParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "set-items-batch" } }, "/icons": { @@ -1168,13 +1234,73 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "description": "Agent address", - "name": "agent_addr", + "name": "agentAddr", "in": "query" }, { + "enum": [ + "cpu_average", + "memory_usage", + "memory_usage_percent", + "disks_read_speed", + "disks_write_speed", + "disks_iops", + "disk_usage", + "network_speed", + "network_transfer", + "sensor_temperature" + ], "type": "string", - "description": "Period", + "x-enum-comments": { + "SystemInfoAggregateModeCPUAverage": "@name SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeDiskUsage": "@name SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeDisksIOPS": "@name SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDisksReadSpeed": "@name SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed": "@name SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeMemoryUsage": "@name SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent": "@name SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeNetworkSpeed": "@name SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer": "@name SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature": "@name SystemInfoAggregateModeSensorTemperature" + }, + "x-enum-varnames": [ + "SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature" + ], + "name": "aggregate", + "in": "query" + }, + { + "enum": [ + "5m", + "15m", + "1h", + "1d", + "1mo" + ], + "type": "string", + "x-enum-comments": { + "MetricsPeriod15m": "@name MetricsPeriod15m", + "MetricsPeriod1d": "@name MetricsPeriod1d", + "MetricsPeriod1h": "@name MetricsPeriod1h", + "MetricsPeriod1mo": "@name MetricsPeriod1mo", + "MetricsPeriod5m": "@name MetricsPeriod5m" + }, + "x-enum-varnames": [ + "MetricsPeriod5m", + "MetricsPeriod15m", + "MetricsPeriod1h", + "MetricsPeriod1d", + "MetricsPeriod1mo" + ], "name": "period", "in": "query" } @@ -1742,10 +1868,40 @@ const docTemplate = `{ "type": "string" }, "state": { - "type": "string" + "$ref": "#/definitions/ContainerState" } } }, + "ContainerState": { + "type": "string", + "enum": [ + "created", + "running", + "paused", + "restarting", + "removing", + "exited", + "dead" + ], + "x-enum-comments": { + "StateCreated": "StateCreated indicates the container is created, but not (yet) started.", + "StateDead": "StateDead indicates that the container failed to be deleted. Containers in this state are attempted to be cleaned up when the daemon restarts.", + "StateExited": "StateExited indicates that the container exited.", + "StatePaused": "StatePaused indicates that the container's current state is paused.", + "StateRemoving": "StateRemoving indicates that the container is being removed.", + "StateRestarting": "StateRestarting indicates that the container is currently restarting.", + "StateRunning": "StateRunning indicates that the container is running." + }, + "x-enum-varnames": [ + "StateCreated", + "StateRunning", + "StatePaused", + "StateRestarting", + "StateRemoving", + "StateExited", + "StateDead" + ] + }, "ContainerStats": { "type": "object", "properties": { @@ -2675,6 +2831,45 @@ const docTemplate = `{ } } }, + "SystemInfoAggregateMode": { + "type": "string", + "enum": [ + "cpu_average", + "memory_usage", + "memory_usage_percent", + "disks_read_speed", + "disks_write_speed", + "disks_iops", + "disk_usage", + "network_speed", + "network_transfer", + "sensor_temperature" + ], + "x-enum-comments": { + "SystemInfoAggregateModeCPUAverage": "@name SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeDiskUsage": "@name SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeDisksIOPS": "@name SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDisksReadSpeed": "@name SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed": "@name SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeMemoryUsage": "@name SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent": "@name SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeNetworkSpeed": "@name SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer": "@name SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature": "@name SystemInfoAggregateModeSensorTemperature" + }, + "x-enum-varnames": [ + "SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature" + ] + }, "UptimeAggregate": { "type": "object", "properties": { @@ -2884,48 +3079,28 @@ const docTemplate = `{ } } }, - "homepage.IconExtra": { - "type": "object", - "properties": { - "file_type": { - "type": "string" - }, - "is_dark": { - "type": "boolean" - }, - "is_light": { - "type": "boolean" - }, - "key": { - "type": "string" - }, - "ref": { - "type": "string" - } - } - }, "homepage.IconMetaSearch": { "type": "object", "properties": { - "dark": { + "Dark": { "type": "boolean" }, - "light": { + "Light": { "type": "boolean" }, - "png": { + "PNG": { "type": "boolean" }, - "ref": { + "Ref": { "type": "string" }, - "source": { - "$ref": "#/definitions/homepage.IconSource" - }, - "svg": { + "SVG": { "type": "boolean" }, - "webP": { + "Source": { + "$ref": "#/definitions/homepage.IconSource" + }, + "WebP": { "type": "boolean" } } @@ -2945,26 +3120,6 @@ const docTemplate = `{ "IconSourceSelfhSt" ] }, - "homepage.IconURL": { - "type": "object", - "properties": { - "extra": { - "description": "only for walkxcode/selfhst icons", - "allOf": [ - { - "$ref": "#/definitions/homepage.IconExtra" - } - ] - }, - "source": { - "$ref": "#/definitions/homepage.IconSource" - }, - "value": { - "description": "only for absolute/relative icons", - "type": "string" - } - } - }, "homepage.Item": { "type": "object", "properties": { @@ -2978,7 +3133,7 @@ const docTemplate = `{ "type": "string" }, "icon": { - "$ref": "#/definitions/homepage.IconURL" + "type": "string" }, "name": { "description": "display name", @@ -3000,7 +3155,12 @@ const docTemplate = `{ "type": "string" }, "widget_config": { - "$ref": "#/definitions/widgets.Config" + "allOf": [ + { + "$ref": "#/definitions/widgets.Config" + } + ], + "x-nullable": true } } }, @@ -3014,7 +3174,7 @@ const docTemplate = `{ "type": "string" }, "icon": { - "$ref": "#/definitions/homepage.IconURL" + "type": "string" }, "name": { "description": "display name", @@ -3031,25 +3191,6 @@ const docTemplate = `{ } } }, - "homepageapi.SetHomePageOverridesRequest": { - "type": "object", - "required": [ - "value", - "what" - ], - "properties": { - "value": {}, - "what": { - "type": "string", - "enum": [ - "item", - "items_batch", - "category_order", - "item_visible" - ] - } - } - }, "mem.VirtualMemoryStat": { "type": "object", "properties": { diff --git a/internal/api/v1/docs/swagger.json b/internal/api/v1/docs/swagger.json index b69d864a..49d2bf33 100644 --- a/internal/api/v1/docs/swagger.json +++ b/internal/api/v1/docs/swagger.json @@ -578,25 +578,25 @@ } }, "400": { - "description": "Bad Request", + "description": "Bad Request: alias is empty or route is not HTTPRoute", "schema": { "$ref": "#/definitions/ErrorResponse" } }, "403": { - "description": "Forbidden", + "description": "Forbidden: unauthorized", "schema": { "$ref": "#/definitions/ErrorResponse" } }, "404": { - "description": "Not Found", + "description": "Not Found: route or icon not found", "schema": { "$ref": "#/definitions/ErrorResponse" } }, "500": { - "description": "Internal Server Error", + "description": "Internal Server Error: internal error", "schema": { "$ref": "#/definitions/ErrorResponse" } @@ -983,9 +983,9 @@ "operationId": "items" } }, - "/homepage/set": { + "/homepage/set/category_order": { "post": { - "description": "Set homepage overrides", + "description": "Set homepage category order.", "consumes": [ "application/json" ], @@ -995,90 +995,15 @@ "tags": [ "homepage" ], - "summary": "Set homepage overrides", + "summary": "Set homepage category order", "parameters": [ - { - "description": "Override single item", - "name": "request", - "in": "body", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideItemParams" - } - } - } - ] - } - }, - { - "description": "Override multiple items", - "name": "request", - "in": "body", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideItemsBatchParams" - } - } - } - ] - } - }, { "description": "Override category order", "name": "request", "in": "body", "required": true, "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideCategoryOrderParams" - } - } - } - ] - } - }, - { - "description": "Override item visibility", - "name": "request", - "in": "body", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/definitions/homepageapi.SetHomePageOverridesRequest" - }, - { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/HomepageOverrideItemVisibleParams" - } - } - } - ] + "$ref": "#/definitions/HomepageOverrideCategoryOrderParams" } } ], @@ -1102,8 +1027,152 @@ } } }, - "x-id": "set", - "operationId": "set" + "x-id": "set-category-order", + "operationId": "set-category-order" + } + }, + "/homepage/set/item": { + "post": { + "description": "Override single homepage item.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "homepage" + ], + "summary": "Override single homepage item", + "parameters": [ + { + "description": "Override single item", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/HomepageOverrideItemParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "set-item", + "operationId": "set-item" + } + }, + "/homepage/set/item_visible": { + "post": { + "description": "POST list of item ids and visibility value.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "homepage" + ], + "summary": "Set homepage item visibility", + "parameters": [ + { + "description": "Set item visibility", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/HomepageOverrideItemVisibleParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "set-item-visible", + "operationId": "set-item-visible" + } + }, + "/homepage/set/items_batch": { + "post": { + "description": "Override multiple homepage items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "homepage" + ], + "summary": "Override multiple homepage items", + "parameters": [ + { + "description": "Override multiple items", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/HomepageOverrideItemsBatchParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "set-items-batch", + "operationId": "set-items-batch" } }, "/icons": { @@ -1174,13 +1243,73 @@ "parameters": [ { "type": "string", - "description": "Agent address", - "name": "agent_addr", + "name": "agentAddr", "in": "query" }, { + "enum": [ + "cpu_average", + "memory_usage", + "memory_usage_percent", + "disks_read_speed", + "disks_write_speed", + "disks_iops", + "disk_usage", + "network_speed", + "network_transfer", + "sensor_temperature" + ], "type": "string", - "description": "Period", + "x-enum-comments": { + "SystemInfoAggregateModeCPUAverage": "@name SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeDiskUsage": "@name SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeDisksIOPS": "@name SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDisksReadSpeed": "@name SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed": "@name SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeMemoryUsage": "@name SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent": "@name SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeNetworkSpeed": "@name SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer": "@name SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature": "@name SystemInfoAggregateModeSensorTemperature" + }, + "x-enum-varnames": [ + "SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature" + ], + "name": "aggregate", + "in": "query" + }, + { + "enum": [ + "5m", + "15m", + "1h", + "1d", + "1mo" + ], + "type": "string", + "x-enum-comments": { + "MetricsPeriod15m": "@name MetricsPeriod15m", + "MetricsPeriod1d": "@name MetricsPeriod1d", + "MetricsPeriod1h": "@name MetricsPeriod1h", + "MetricsPeriod1mo": "@name MetricsPeriod1mo", + "MetricsPeriod5m": "@name MetricsPeriod5m" + }, + "x-enum-varnames": [ + "MetricsPeriod5m", + "MetricsPeriod15m", + "MetricsPeriod1h", + "MetricsPeriod1d", + "MetricsPeriod1mo" + ], "name": "period", "in": "query" } @@ -1839,7 +1968,7 @@ "x-omitempty": false }, "state": { - "type": "string", + "$ref": "#/definitions/ContainerState", "x-nullable": false, "x-omitempty": false } @@ -1847,6 +1976,38 @@ "x-nullable": false, "x-omitempty": false }, + "ContainerState": { + "type": "string", + "enum": [ + "created", + "running", + "paused", + "restarting", + "removing", + "exited", + "dead" + ], + "x-enum-comments": { + "StateCreated": "StateCreated indicates the container is created, but not (yet) started.", + "StateDead": "StateDead indicates that the container failed to be deleted. Containers in this state are attempted to be cleaned up when the daemon restarts.", + "StateExited": "StateExited indicates that the container exited.", + "StatePaused": "StatePaused indicates that the container's current state is paused.", + "StateRemoving": "StateRemoving indicates that the container is being removed.", + "StateRestarting": "StateRestarting indicates that the container is currently restarting.", + "StateRunning": "StateRunning indicates that the container is running." + }, + "x-enum-varnames": [ + "StateCreated", + "StateRunning", + "StatePaused", + "StateRestarting", + "StateRemoving", + "StateExited", + "StateDead" + ], + "x-nullable": false, + "x-omitempty": false + }, "ContainerStats": { "type": "object", "properties": { @@ -3168,6 +3329,47 @@ "x-nullable": false, "x-omitempty": false }, + "SystemInfoAggregateMode": { + "type": "string", + "enum": [ + "cpu_average", + "memory_usage", + "memory_usage_percent", + "disks_read_speed", + "disks_write_speed", + "disks_iops", + "disk_usage", + "network_speed", + "network_transfer", + "sensor_temperature" + ], + "x-enum-comments": { + "SystemInfoAggregateModeCPUAverage": "@name SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeDiskUsage": "@name SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeDisksIOPS": "@name SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDisksReadSpeed": "@name SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed": "@name SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeMemoryUsage": "@name SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent": "@name SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeNetworkSpeed": "@name SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer": "@name SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature": "@name SystemInfoAggregateModeSensorTemperature" + }, + "x-enum-varnames": [ + "SystemInfoAggregateModeCPUAverage", + "SystemInfoAggregateModeMemoryUsage", + "SystemInfoAggregateModeMemoryUsagePercent", + "SystemInfoAggregateModeDisksReadSpeed", + "SystemInfoAggregateModeDisksWriteSpeed", + "SystemInfoAggregateModeDisksIOPS", + "SystemInfoAggregateModeDiskUsage", + "SystemInfoAggregateModeNetworkSpeed", + "SystemInfoAggregateModeNetworkTransfer", + "SystemInfoAggregateModeSensorTemperature" + ], + "x-nullable": false, + "x-omitempty": false + }, "UptimeAggregate": { "type": "object", "properties": { @@ -3475,72 +3677,40 @@ "x-nullable": false, "x-omitempty": false }, - "homepage.IconExtra": { - "type": "object", - "properties": { - "file_type": { - "type": "string", - "x-nullable": false, - "x-omitempty": false - }, - "is_dark": { - "type": "boolean", - "x-nullable": false, - "x-omitempty": false - }, - "is_light": { - "type": "boolean", - "x-nullable": false, - "x-omitempty": false - }, - "key": { - "type": "string", - "x-nullable": false, - "x-omitempty": false - }, - "ref": { - "type": "string", - "x-nullable": false, - "x-omitempty": false - } - }, - "x-nullable": false, - "x-omitempty": false - }, "homepage.IconMetaSearch": { "type": "object", "properties": { - "dark": { + "Dark": { "type": "boolean", "x-nullable": false, "x-omitempty": false }, - "light": { + "Light": { "type": "boolean", "x-nullable": false, "x-omitempty": false }, - "png": { + "PNG": { "type": "boolean", "x-nullable": false, "x-omitempty": false }, - "ref": { + "Ref": { "type": "string", "x-nullable": false, "x-omitempty": false }, - "source": { + "SVG": { + "type": "boolean", + "x-nullable": false, + "x-omitempty": false + }, + "Source": { "$ref": "#/definitions/homepage.IconSource", "x-nullable": false, "x-omitempty": false }, - "svg": { - "type": "boolean", - "x-nullable": false, - "x-omitempty": false - }, - "webP": { + "WebP": { "type": "boolean", "x-nullable": false, "x-omitempty": false @@ -3566,34 +3736,6 @@ "x-nullable": false, "x-omitempty": false }, - "homepage.IconURL": { - "type": "object", - "properties": { - "extra": { - "description": "only for walkxcode/selfhst icons", - "allOf": [ - { - "$ref": "#/definitions/homepage.IconExtra" - } - ], - "x-nullable": false, - "x-omitempty": false - }, - "source": { - "$ref": "#/definitions/homepage.IconSource", - "x-nullable": false, - "x-omitempty": false - }, - "value": { - "description": "only for absolute/relative icons", - "type": "string", - "x-nullable": false, - "x-omitempty": false - } - }, - "x-nullable": false, - "x-omitempty": false - }, "homepage.Item": { "type": "object", "properties": { @@ -3613,7 +3755,7 @@ "x-omitempty": false }, "icon": { - "$ref": "#/definitions/homepage.IconURL", + "type": "string", "x-nullable": false, "x-omitempty": false }, @@ -3649,9 +3791,12 @@ "x-omitempty": false }, "widget_config": { - "$ref": "#/definitions/widgets.Config", - "x-nullable": false, - "x-omitempty": false + "allOf": [ + { + "$ref": "#/definitions/widgets.Config" + } + ], + "x-nullable": true } }, "x-nullable": false, @@ -3671,7 +3816,7 @@ "x-omitempty": false }, "icon": { - "$ref": "#/definitions/homepage.IconURL", + "type": "string", "x-nullable": false, "x-omitempty": false }, @@ -3700,32 +3845,6 @@ "x-nullable": false, "x-omitempty": false }, - "homepageapi.SetHomePageOverridesRequest": { - "type": "object", - "required": [ - "value", - "what" - ], - "properties": { - "value": { - "x-nullable": false, - "x-omitempty": false - }, - "what": { - "type": "string", - "enum": [ - "item", - "items_batch", - "category_order", - "item_visible" - ], - "x-nullable": false, - "x-omitempty": false - } - }, - "x-nullable": false, - "x-omitempty": false - }, "mem.VirtualMemoryStat": { "type": "object", "properties": { diff --git a/internal/api/v1/docs/swagger.yaml b/internal/api/v1/docs/swagger.yaml index 44fb9d95..4806d0d2 100644 --- a/internal/api/v1/docs/swagger.yaml +++ b/internal/api/v1/docs/swagger.yaml @@ -107,8 +107,36 @@ definitions: server: type: string state: - type: string + $ref: '#/definitions/ContainerState' type: object + ContainerState: + enum: + - created + - running + - paused + - restarting + - removing + - exited + - dead + type: string + x-enum-comments: + StateCreated: StateCreated indicates the container is created, but not (yet) + started. + StateDead: StateDead indicates that the container failed to be deleted. Containers + in this state are attempted to be cleaned up when the daemon restarts. + StateExited: StateExited indicates that the container exited. + StatePaused: StatePaused indicates that the container's current state is paused. + StateRemoving: StateRemoving indicates that the container is being removed. + StateRestarting: StateRestarting indicates that the container is currently restarting. + StateRunning: StateRunning indicates that the container is running. + x-enum-varnames: + - StateCreated + - StateRunning + - StatePaused + - StateRestarting + - StateRemoving + - StateExited + - StateDead ContainerStats: properties: paused: @@ -731,6 +759,41 @@ definitions: total: type: integer type: object + SystemInfoAggregateMode: + enum: + - cpu_average + - memory_usage + - memory_usage_percent + - disks_read_speed + - disks_write_speed + - disks_iops + - disk_usage + - network_speed + - network_transfer + - sensor_temperature + type: string + x-enum-comments: + SystemInfoAggregateModeCPUAverage: '@name SystemInfoAggregateModeCPUAverage' + SystemInfoAggregateModeDiskUsage: '@name SystemInfoAggregateModeDiskUsage' + SystemInfoAggregateModeDisksIOPS: '@name SystemInfoAggregateModeDisksIOPS' + SystemInfoAggregateModeDisksReadSpeed: '@name SystemInfoAggregateModeDisksReadSpeed' + SystemInfoAggregateModeDisksWriteSpeed: '@name SystemInfoAggregateModeDisksWriteSpeed' + SystemInfoAggregateModeMemoryUsage: '@name SystemInfoAggregateModeMemoryUsage' + SystemInfoAggregateModeMemoryUsagePercent: '@name SystemInfoAggregateModeMemoryUsagePercent' + SystemInfoAggregateModeNetworkSpeed: '@name SystemInfoAggregateModeNetworkSpeed' + SystemInfoAggregateModeNetworkTransfer: '@name SystemInfoAggregateModeNetworkTransfer' + SystemInfoAggregateModeSensorTemperature: '@name SystemInfoAggregateModeSensorTemperature' + x-enum-varnames: + - SystemInfoAggregateModeCPUAverage + - SystemInfoAggregateModeMemoryUsage + - SystemInfoAggregateModeMemoryUsagePercent + - SystemInfoAggregateModeDisksReadSpeed + - SystemInfoAggregateModeDisksWriteSpeed + - SystemInfoAggregateModeDisksIOPS + - SystemInfoAggregateModeDiskUsage + - SystemInfoAggregateModeNetworkSpeed + - SystemInfoAggregateModeNetworkTransfer + - SystemInfoAggregateModeSensorTemperature UptimeAggregate: properties: data: @@ -886,34 +949,21 @@ definitions: statusCode: type: integer type: object - homepage.IconExtra: - properties: - file_type: - type: string - is_dark: - type: boolean - is_light: - type: boolean - key: - type: string - ref: - type: string - type: object homepage.IconMetaSearch: properties: - dark: + Dark: type: boolean - light: + Light: type: boolean - png: + PNG: type: boolean - ref: + Ref: type: string - source: - $ref: '#/definitions/homepage.IconSource' - svg: + SVG: type: boolean - webP: + Source: + $ref: '#/definitions/homepage.IconSource' + WebP: type: boolean type: object homepage.IconSource: @@ -928,18 +978,6 @@ definitions: - IconSourceRelative - IconSourceWalkXCode - IconSourceSelfhSt - homepage.IconURL: - properties: - extra: - allOf: - - $ref: '#/definitions/homepage.IconExtra' - description: only for walkxcode/selfhst icons - source: - $ref: '#/definitions/homepage.IconSource' - value: - description: only for absolute/relative icons - type: string - type: object homepage.Item: properties: alias: @@ -949,7 +987,7 @@ definitions: description: type: string icon: - $ref: '#/definitions/homepage.IconURL' + type: string name: description: display name type: string @@ -964,7 +1002,9 @@ definitions: url: type: string widget_config: - $ref: '#/definitions/widgets.Config' + allOf: + - $ref: '#/definitions/widgets.Config' + x-nullable: true type: object homepage.ItemConfig: properties: @@ -973,7 +1013,7 @@ definitions: description: type: string icon: - $ref: '#/definitions/homepage.IconURL' + type: string name: description: display name type: string @@ -984,20 +1024,6 @@ definitions: url: type: string type: object - homepageapi.SetHomePageOverridesRequest: - properties: - value: {} - what: - enum: - - item - - items_batch - - category_order - - item_visible - type: string - required: - - value - - what - type: object mem.VirtualMemoryStat: properties: available: @@ -1602,19 +1628,19 @@ paths: $ref: '#/definitions/homepage.FetchResult' type: array "400": - description: Bad Request + description: 'Bad Request: alias is empty or route is not HTTPRoute' schema: $ref: '#/definitions/ErrorResponse' "403": - description: Forbidden + description: 'Forbidden: unauthorized' schema: $ref: '#/definitions/ErrorResponse' "404": - description: Not Found + description: 'Not Found: route or icon not found' schema: $ref: '#/definitions/ErrorResponse' "500": - description: Internal Server Error + description: 'Internal Server Error: internal error' schema: $ref: '#/definitions/ErrorResponse' summary: Get favicon @@ -1872,56 +1898,18 @@ paths: tags: - homepage x-id: items - /homepage/set: + /homepage/set/category_order: post: consumes: - application/json - description: Set homepage overrides + description: Set homepage category order. parameters: - - description: Override single item - in: body - name: request - required: true - schema: - allOf: - - $ref: '#/definitions/homepageapi.SetHomePageOverridesRequest' - - properties: - value: - $ref: '#/definitions/HomepageOverrideItemParams' - type: object - - description: Override multiple items - in: body - name: request - required: true - schema: - allOf: - - $ref: '#/definitions/homepageapi.SetHomePageOverridesRequest' - - properties: - value: - $ref: '#/definitions/HomepageOverrideItemsBatchParams' - type: object - description: Override category order in: body name: request required: true schema: - allOf: - - $ref: '#/definitions/homepageapi.SetHomePageOverridesRequest' - - properties: - value: - $ref: '#/definitions/HomepageOverrideCategoryOrderParams' - type: object - - description: Override item visibility - in: body - name: request - required: true - schema: - allOf: - - $ref: '#/definitions/homepageapi.SetHomePageOverridesRequest' - - properties: - value: - $ref: '#/definitions/HomepageOverrideItemVisibleParams' - type: object + $ref: '#/definitions/HomepageOverrideCategoryOrderParams' produces: - application/json responses: @@ -1937,10 +1925,103 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/ErrorResponse' - summary: Set homepage overrides + summary: Set homepage category order tags: - homepage - x-id: set + x-id: set-category-order + /homepage/set/item: + post: + consumes: + - application/json + description: Override single homepage item. + parameters: + - description: Override single item + in: body + name: request + required: true + schema: + $ref: '#/definitions/HomepageOverrideItemParams' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/SuccessResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorResponse' + summary: Override single homepage item + tags: + - homepage + x-id: set-item + /homepage/set/item_visible: + post: + consumes: + - application/json + description: POST list of item ids and visibility value. + parameters: + - description: Set item visibility + in: body + name: request + required: true + schema: + $ref: '#/definitions/HomepageOverrideItemVisibleParams' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/SuccessResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorResponse' + summary: Set homepage item visibility + tags: + - homepage + x-id: set-item-visible + /homepage/set/items_batch: + post: + consumes: + - application/json + description: Override multiple homepage items. + parameters: + - description: Override multiple items + in: body + name: request + required: true + schema: + $ref: '#/definitions/HomepageOverrideItemsBatchParams' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/SuccessResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorResponse' + summary: Override multiple homepage items + tags: + - homepage + x-id: set-items-batch /icons: get: consumes: @@ -1980,14 +2061,66 @@ paths: get: description: Get system info parameters: - - description: Agent address - in: query - name: agent_addr + - in: query + name: agentAddr type: string - - description: Period + - enum: + - cpu_average + - memory_usage + - memory_usage_percent + - disks_read_speed + - disks_write_speed + - disks_iops + - disk_usage + - network_speed + - network_transfer + - sensor_temperature + in: query + name: aggregate + type: string + x-enum-comments: + SystemInfoAggregateModeCPUAverage: '@name SystemInfoAggregateModeCPUAverage' + SystemInfoAggregateModeDiskUsage: '@name SystemInfoAggregateModeDiskUsage' + SystemInfoAggregateModeDisksIOPS: '@name SystemInfoAggregateModeDisksIOPS' + SystemInfoAggregateModeDisksReadSpeed: '@name SystemInfoAggregateModeDisksReadSpeed' + SystemInfoAggregateModeDisksWriteSpeed: '@name SystemInfoAggregateModeDisksWriteSpeed' + SystemInfoAggregateModeMemoryUsage: '@name SystemInfoAggregateModeMemoryUsage' + SystemInfoAggregateModeMemoryUsagePercent: '@name SystemInfoAggregateModeMemoryUsagePercent' + SystemInfoAggregateModeNetworkSpeed: '@name SystemInfoAggregateModeNetworkSpeed' + SystemInfoAggregateModeNetworkTransfer: '@name SystemInfoAggregateModeNetworkTransfer' + SystemInfoAggregateModeSensorTemperature: '@name SystemInfoAggregateModeSensorTemperature' + x-enum-varnames: + - SystemInfoAggregateModeCPUAverage + - SystemInfoAggregateModeMemoryUsage + - SystemInfoAggregateModeMemoryUsagePercent + - SystemInfoAggregateModeDisksReadSpeed + - SystemInfoAggregateModeDisksWriteSpeed + - SystemInfoAggregateModeDisksIOPS + - SystemInfoAggregateModeDiskUsage + - SystemInfoAggregateModeNetworkSpeed + - SystemInfoAggregateModeNetworkTransfer + - SystemInfoAggregateModeSensorTemperature + - enum: + - 5m + - 15m + - 1h + - 1d + - 1mo in: query name: period type: string + x-enum-comments: + MetricsPeriod15m: '@name MetricsPeriod15m' + MetricsPeriod1d: '@name MetricsPeriod1d' + MetricsPeriod1h: '@name MetricsPeriod1h' + MetricsPeriod1mo: '@name MetricsPeriod1mo' + MetricsPeriod5m: '@name MetricsPeriod5m' + x-enum-varnames: + - MetricsPeriod5m + - MetricsPeriod15m + - MetricsPeriod1h + - MetricsPeriod1d + - MetricsPeriod1mo produces: - application/json responses: @@ -2031,11 +2164,11 @@ paths: name: interval type: string x-enum-comments: + MetricsPeriod15m: '@name MetricsPeriod15m' + MetricsPeriod1d: '@name MetricsPeriod1d' MetricsPeriod1h: '@name MetricsPeriod1h' MetricsPeriod1mo: '@name MetricsPeriod1mo' MetricsPeriod5m: '@name MetricsPeriod5m' - MetricsPeriod15m: '@name MetricsPeriod15m' - MetricsPeriod1d: '@name MetricsPeriod1d' x-enum-varnames: - MetricsPeriod5m - MetricsPeriod15m diff --git a/internal/api/v1/favicon.go b/internal/api/v1/favicon.go index 7a1f6cac..07eeea45 100644 --- a/internal/api/v1/favicon.go +++ b/internal/api/v1/favicon.go @@ -15,15 +15,6 @@ type GetFavIconRequest struct { Alias string `form:"alias" binding:"required_without=URL"` } // @name GetFavIconRequest -// GetFavIcon returns the favicon of the route -// -// Returns: -// - 200 OK: if icon found -// - 400 Bad Request: if alias is empty or route is not HTTPRoute -// - 404 Not Found: if route or icon not found -// - 500 Internal Server Error: if internal error -// - others: depends on route handler response - // @x-id "favicon" // @BasePath /api/v1 // @Summary Get favicon @@ -34,10 +25,10 @@ type GetFavIconRequest struct { // @Param url query string false "URL of the route" // @Param alias query string false "Alias of the route" // @Success 200 {array} homepage.FetchResult -// @Failure 400 {object} apitypes.ErrorResponse -// @Failure 403 {object} apitypes.ErrorResponse -// @Failure 404 {object} apitypes.ErrorResponse -// @Failure 500 {object} apitypes.ErrorResponse +// @Failure 400 {object} apitypes.ErrorResponse "Bad Request: alias is empty or route is not HTTPRoute" +// @Failure 403 {object} apitypes.ErrorResponse "Forbidden: unauthorized" +// @Failure 404 {object} apitypes.ErrorResponse "Not Found: route or icon not found" +// @Failure 500 {object} apitypes.ErrorResponse "Internal Server Error: internal error" // @Router /favicon [get] func FavIcon(c *gin.Context) { var request GetFavIconRequest diff --git a/internal/api/v1/homepage/overrides.go b/internal/api/v1/homepage/overrides.go index d8ded137..b67dc4af 100644 --- a/internal/api/v1/homepage/overrides.go +++ b/internal/api/v1/homepage/overrides.go @@ -2,7 +2,6 @@ package homepageapi import ( "encoding/json" - "errors" "net/http" "github.com/gin-gonic/gin" @@ -10,13 +9,6 @@ import ( "github.com/yusing/go-proxy/internal/homepage" ) -const ( - HomepageOverrideItem = "item" - HomepageOverrideItemsBatch = "items_batch" - HomepageOverrideCategoryOrder = "category_order" - HomepageOverrideItemVisible = "item_visible" -) - type ( HomepageOverrideItemParams struct { Which string `json:"which"` @@ -35,76 +27,119 @@ type ( } // @name HomepageOverrideItemVisibleParams ) -type SetHomePageOverridesRequest struct { - What string `json:"what" validate:"required,oneof=item items_batch category_order item_visible"` - Value any `json:"value" validate:"required" swaggerType:"object"` -} - -// @x-id "set" +// @x-id "set-item" // @BasePath /api/v1 -// @Summary Set homepage overrides -// @Description Set homepage overrides +// @Summary Override single homepage item +// @Description Override single homepage item. // @Tags homepage -// @Accept json +// @Accept json // @Produce json -// @Param request body SetHomePageOverridesRequest{value=HomepageOverrideItemParams} true "Override single item" -// @Param request body SetHomePageOverridesRequest{value=HomepageOverrideItemsBatchParams} true "Override multiple items" -// @Param request body SetHomePageOverridesRequest{value=HomepageOverrideCategoryOrderParams} true "Override category order" -// @Param request body SetHomePageOverridesRequest{value=HomepageOverrideItemVisibleParams} true "Override item visibility" +// @Param request body HomepageOverrideItemParams true "Override single item" // @Success 200 {object} apitypes.SuccessResponse // @Failure 400 {object} apitypes.ErrorResponse // @Failure 500 {object} apitypes.ErrorResponse -// @Router /homepage/set [post] -func Set(c *gin.Context) { - var request SetHomePageOverridesRequest - if err := c.ShouldBindJSON(&request); err != nil { +// @Router /homepage/set/item [post] +func SetItem(c *gin.Context) { + var params HomepageOverrideItemParams + if err := c.ShouldBindJSON(¶ms); err != nil { c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err)) return } - - data, err := c.GetRawData() - if err != nil { - c.Error(apitypes.InternalServerError(err, "failed to get raw data")) - return - } - overrides := homepage.GetOverrideConfig() - switch request.What { - case HomepageOverrideItem: - var params HomepageOverrideItemParams - if err := json.Unmarshal(data, ¶ms); err != nil { - c.Error(apitypes.InternalServerError(err, "failed to unmarshal data")) + overrides.OverrideItem(params.Which, ¶ms.Value) + c.JSON(http.StatusOK, apitypes.Success("success")) +} + +// @x-id "set-items-batch" +// @BasePath /api/v1 +// @Summary Override multiple homepage items +// @Description Override multiple homepage items. +// @Tags homepage +// @Accept json +// @Produce json +// @Param request body HomepageOverrideItemsBatchParams true "Override multiple items" +// @Success 200 {object} apitypes.SuccessResponse +// @Failure 400 {object} apitypes.ErrorResponse +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /homepage/set/items_batch [post] +func SetItemsBatch(c *gin.Context) { + var params HomepageOverrideItemsBatchParams + if err := c.ShouldBindJSON(¶ms); err != nil { + data, derr := c.GetRawData() + if derr != nil { + c.Error(apitypes.InternalServerError(derr, "failed to get raw data")) return } - overrides.OverrideItem(params.Which, ¶ms.Value) - case HomepageOverrideItemsBatch: - var params HomepageOverrideItemsBatchParams - if err := json.Unmarshal(data, ¶ms); err != nil { - c.Error(apitypes.InternalServerError(err, "failed to unmarshal data")) + if uerr := json.Unmarshal(data, ¶ms); uerr != nil { + c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", uerr)) return } - overrides.OverrideItems(params.Value) - case HomepageOverrideItemVisible: // POST /v1/item_visible [a,b,c], false => hide a, b, c - var params HomepageOverrideItemVisibleParams - if err := json.Unmarshal(data, ¶ms); err != nil { - c.Error(apitypes.InternalServerError(err, "failed to unmarshal data")) + } + overrides := homepage.GetOverrideConfig() + overrides.OverrideItems(params.Value) + c.JSON(http.StatusOK, apitypes.Success("success")) +} + +// @x-id "set-item-visible" +// @BasePath /api/v1 +// @Summary Set homepage item visibility +// @Description POST list of item ids and visibility value. +// @Tags homepage +// @Accept json +// @Produce json +// @Param request body HomepageOverrideItemVisibleParams true "Set item visibility" +// @Success 200 {object} apitypes.SuccessResponse +// @Failure 400 {object} apitypes.ErrorResponse +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /homepage/set/item_visible [post] +func SetItemVisible(c *gin.Context) { + var params HomepageOverrideItemVisibleParams + if err := c.ShouldBindJSON(¶ms); err != nil { + data, derr := c.GetRawData() + if derr != nil { + c.Error(apitypes.InternalServerError(derr, "failed to get raw data")) return } - if params.Value { - overrides.UnhideItems(params.Which) - } else { - overrides.HideItems(params.Which) - } - case HomepageOverrideCategoryOrder: - var params HomepageOverrideCategoryOrderParams - if err := json.Unmarshal(data, ¶ms); err != nil { - c.Error(apitypes.InternalServerError(err, "failed to unmarshal data")) + if uerr := json.Unmarshal(data, ¶ms); uerr != nil { + c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", uerr)) return } - overrides.SetCategoryOrder(params.Which, params.Value) - default: // won't happen - c.JSON(http.StatusBadRequest, apitypes.Error("invalid what", errors.New("invalid what"))) - return + } + overrides := homepage.GetOverrideConfig() + if params.Value { + overrides.UnhideItems(params.Which) + } else { + overrides.HideItems(params.Which) } c.JSON(http.StatusOK, apitypes.Success("success")) } + +// @x-id "set-category-order" +// @BasePath /api/v1 +// @Summary Set homepage category order +// @Description Set homepage category order. +// @Tags homepage +// @Accept json +// @Produce json +// @Param request body HomepageOverrideCategoryOrderParams true "Override category order" +// @Success 200 {object} apitypes.SuccessResponse +// @Failure 400 {object} apitypes.ErrorResponse +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /homepage/set/category_order [post] +func SetCategoryOrder(c *gin.Context) { + var params HomepageOverrideCategoryOrderParams + if err := c.ShouldBindJSON(¶ms); err != nil { + data, derr := c.GetRawData() + if derr != nil { + c.Error(apitypes.InternalServerError(derr, "failed to get raw data")) + return + } + if uerr := json.Unmarshal(data, ¶ms); uerr != nil { + c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", uerr)) + return + } + } + overrides := homepage.GetOverrideConfig() + overrides.SetCategoryOrder(params.Which, params.Value) + c.JSON(http.StatusOK, apitypes.Success("success")) +} diff --git a/internal/api/v1/metrics/system_info.go b/internal/api/v1/metrics/system_info.go index 5b8949b5..4f30e858 100644 --- a/internal/api/v1/metrics/system_info.go +++ b/internal/api/v1/metrics/system_info.go @@ -13,6 +13,12 @@ import ( nettypes "github.com/yusing/go-proxy/internal/net/types" ) +type SystemInfoRequest struct { + AgentAddr string `query:"agent_addr"` + Aggregate systeminfo.SystemInfoAggregateMode `query:"aggregate"` + Period period.Filter `query:"period"` +} // @name SystemInfoRequest + type SystemInfoAggregate period.ResponseType[systeminfo.Aggregated] // @name SystemInfoAggregate // @x-id "system_info" @@ -21,8 +27,7 @@ type SystemInfoAggregate period.ResponseType[systeminfo.Aggregated] // @name Sys // @Description Get system info // @Tags metrics,websocket // @Produce json -// @Param agent_addr query string false "Agent address" -// @Param period query string false "Period" +// @Param request query SystemInfoRequest false "Request" // @Success 200 {object} systeminfo.SystemInfo "no period specified" // @Success 200 {object} SystemInfoAggregate "period specified" // @Failure 400 {object} apitypes.ErrorResponse diff --git a/internal/homepage/homepage.go b/internal/homepage/homepage.go index 75febb0a..c2da470b 100644 --- a/internal/homepage/homepage.go +++ b/internal/homepage/homepage.go @@ -14,7 +14,7 @@ type ( ItemConfig struct { Show bool `json:"show"` Name string `json:"name"` // display name - Icon *IconURL `json:"icon"` + Icon *IconURL `json:"icon" swaggertype:"string"` Category string `json:"category"` Description string `json:"description" aliases:"desc"` URL string `json:"url,omitempty"` @@ -24,7 +24,7 @@ type ( Item struct { *ItemConfig - WidgetConfig *widgets.Config `json:"widget_config,omitempty" aliases:"widget"` + WidgetConfig *widgets.Config `json:"widget_config,omitempty" aliases:"widget" extensions:"x-nullable"` Alias string `json:"alias"` Provider string `json:"provider"` diff --git a/internal/homepage/list_icons.go b/internal/homepage/list_icons.go index 9e9ad514..32184eb0 100644 --- a/internal/homepage/list_icons.go +++ b/internal/homepage/list_icons.go @@ -29,13 +29,13 @@ type ( Tag string } IconMetaSearch struct { - Source IconSource - Ref string - SVG bool - PNG bool - WebP bool - Light bool - Dark bool + Source IconSource `json:"Source"` + Ref string `json:"Ref"` + SVG bool `json:"SVG"` + PNG bool `json:"PNG"` + WebP bool `json:"WebP"` + Light bool `json:"Light"` + Dark bool `json:"Dark"` } Cache struct { Icons IconMap diff --git a/internal/metrics/systeminfo/system_info.go b/internal/metrics/systeminfo/system_info.go index 275181ab..fc1b20a5 100644 --- a/internal/metrics/systeminfo/system_info.go +++ b/internal/metrics/systeminfo/system_info.go @@ -37,30 +37,32 @@ type SystemInfo struct { Sensors Sensors `json:"sensors"` // sensor temperature by key } // @name SystemInfo +type SystemInfoAggregateMode string // @name SystemInfoAggregateMode + const ( - queryCPUAverage = "cpu_average" - queryMemoryUsage = "memory_usage" - queryMemoryUsagePercent = "memory_usage_percent" - queryDisksReadSpeed = "disks_read_speed" - queryDisksWriteSpeed = "disks_write_speed" - queryDisksIOPS = "disks_iops" - queryDiskUsage = "disk_usage" - queryNetworkSpeed = "network_speed" - queryNetworkTransfer = "network_transfer" - querySensorTemperature = "sensor_temperature" + SystemInfoAggregateModeCPUAverage SystemInfoAggregateMode = "cpu_average" // @name SystemInfoAggregateModeCPUAverage + SystemInfoAggregateModeMemoryUsage SystemInfoAggregateMode = "memory_usage" // @name SystemInfoAggregateModeMemoryUsage + SystemInfoAggregateModeMemoryUsagePercent SystemInfoAggregateMode = "memory_usage_percent" // @name SystemInfoAggregateModeMemoryUsagePercent + SystemInfoAggregateModeDisksReadSpeed SystemInfoAggregateMode = "disks_read_speed" // @name SystemInfoAggregateModeDisksReadSpeed + SystemInfoAggregateModeDisksWriteSpeed SystemInfoAggregateMode = "disks_write_speed" // @name SystemInfoAggregateModeDisksWriteSpeed + SystemInfoAggregateModeDisksIOPS SystemInfoAggregateMode = "disks_iops" // @name SystemInfoAggregateModeDisksIOPS + SystemInfoAggregateModeDiskUsage SystemInfoAggregateMode = "disk_usage" // @name SystemInfoAggregateModeDiskUsage + SystemInfoAggregateModeNetworkSpeed SystemInfoAggregateMode = "network_speed" // @name SystemInfoAggregateModeNetworkSpeed + SystemInfoAggregateModeNetworkTransfer SystemInfoAggregateMode = "network_transfer" // @name SystemInfoAggregateModeNetworkTransfer + SystemInfoAggregateModeSensorTemperature SystemInfoAggregateMode = "sensor_temperature" // @name SystemInfoAggregateModeSensorTemperature ) -var allQueries = []string{ - queryCPUAverage, - queryMemoryUsage, - queryMemoryUsagePercent, - queryDisksReadSpeed, - queryDisksWriteSpeed, - queryDisksIOPS, - queryDiskUsage, - queryNetworkSpeed, - queryNetworkTransfer, - querySensorTemperature, +var allQueries = []SystemInfoAggregateMode{ + SystemInfoAggregateModeCPUAverage, + SystemInfoAggregateModeMemoryUsage, + SystemInfoAggregateModeMemoryUsagePercent, + SystemInfoAggregateModeDisksReadSpeed, + SystemInfoAggregateModeDisksWriteSpeed, + SystemInfoAggregateModeDisksIOPS, + SystemInfoAggregateModeDiskUsage, + SystemInfoAggregateModeNetworkSpeed, + SystemInfoAggregateModeNetworkTransfer, + SystemInfoAggregateModeSensorTemperature, } var Poller = period.NewPoller("system_info", getSystemInfo, aggregate) @@ -227,8 +229,8 @@ func (s *SystemInfo) collectSensorsInfo(ctx context.Context) error { func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggregated) { n := len(entries) aggregated := make(Aggregated, 0, n) - switch query.Get("aggregate") { - case queryCPUAverage: + switch SystemInfoAggregateMode(query.Get("aggregate")) { + case SystemInfoAggregateModeCPUAverage: for _, entry := range entries { if entry.CPUAverage != nil { aggregated = append(aggregated, map[string]any{ @@ -237,7 +239,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre }) } } - case queryMemoryUsage: + case SystemInfoAggregateModeMemoryUsage: for _, entry := range entries { if entry.Memory != nil { aggregated = append(aggregated, map[string]any{ @@ -246,7 +248,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre }) } } - case queryMemoryUsagePercent: + case SystemInfoAggregateModeMemoryUsagePercent: for _, entry := range entries { if entry.Memory != nil { aggregated = append(aggregated, map[string]any{ @@ -255,7 +257,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre }) } } - case queryDisksReadSpeed: + case SystemInfoAggregateModeDisksReadSpeed: for _, entry := range entries { if entry.DisksIO == nil { continue @@ -267,7 +269,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre m["timestamp"] = entry.Timestamp aggregated = append(aggregated, m) } - case queryDisksWriteSpeed: + case SystemInfoAggregateModeDisksWriteSpeed: for _, entry := range entries { if entry.DisksIO == nil { continue @@ -279,7 +281,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre m["timestamp"] = entry.Timestamp aggregated = append(aggregated, m) } - case queryDisksIOPS: + case SystemInfoAggregateModeDisksIOPS: for _, entry := range entries { if entry.DisksIO == nil { continue @@ -291,7 +293,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre m["timestamp"] = entry.Timestamp aggregated = append(aggregated, m) } - case queryDiskUsage: + case SystemInfoAggregateModeDiskUsage: for _, entry := range entries { if entry.Disks == nil { continue @@ -303,7 +305,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre m["timestamp"] = entry.Timestamp aggregated = append(aggregated, m) } - case queryNetworkSpeed: + case SystemInfoAggregateModeNetworkSpeed: for _, entry := range entries { if entry.Network == nil { continue @@ -314,7 +316,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre "download": entry.Network.DownloadSpeed, }) } - case queryNetworkTransfer: + case SystemInfoAggregateModeNetworkTransfer: for _, entry := range entries { if entry.Network == nil { continue @@ -325,7 +327,7 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre "download": entry.Network.BytesRecv, }) } - case querySensorTemperature: + case SystemInfoAggregateModeSensorTemperature: for _, entry := range entries { if entry.Sensors == nil { continue diff --git a/internal/metrics/systeminfo/system_info_test.go b/internal/metrics/systeminfo/system_info_test.go index ec1c15a6..73457a4e 100644 --- a/internal/metrics/systeminfo/system_info_test.go +++ b/internal/metrics/systeminfo/system_info_test.go @@ -127,8 +127,8 @@ func TestSerialize(t *testing.T) { entries[i] = testInfo } for _, query := range allQueries { - t.Run(query, func(t *testing.T) { - _, result := aggregate(entries, url.Values{"aggregate": []string{query}}) + t.Run(string(query), func(t *testing.T) { + _, result := aggregate(entries, url.Values{"aggregate": []string{string(query)}}) s, err := result.MarshalJSON() ExpectNoError(t, err) var v []map[string]any @@ -152,8 +152,8 @@ func BenchmarkSerialize(b *testing.B) { } queries := map[string]Aggregated{} for _, query := range allQueries { - _, result := aggregate(entries, url.Values{"aggregate": []string{query}}) - queries[query] = result + _, result := aggregate(entries, url.Values{"aggregate": []string{string(query)}}) + queries[string(query)] = result } b.ReportAllocs() b.ResetTimer() @@ -170,14 +170,14 @@ func BenchmarkSerialize(b *testing.B) { b.Run("optimized", func(b *testing.B) { for b.Loop() { for _, query := range allQueries { - _, _ = queries[query].MarshalJSON() + _, _ = queries[string(query)].MarshalJSON() } } }) b.Run("json", func(b *testing.B) { for b.Loop() { for _, query := range allQueries { - _, _ = json.Marshal([]map[string]any(queries[query])) + _, _ = json.Marshal([]map[string]any(queries[string(query)])) } } })