From 1c266a5d2e619c82e5f226db1c95a4c8156b899d Mon Sep 17 00:00:00 2001 From: juli0n21 Date: Mon, 3 Feb 2025 03:06:35 +0100 Subject: [PATCH] add swagger docs --- go-backend/database.go | 7 +- go-backend/docs/docs.go | 687 +++++++++++++++++++++++++++++++++++ go-backend/docs/swagger.json | 663 +++++++++++++++++++++++++++++++++ go-backend/docs/swagger.yaml | 449 +++++++++++++++++++++++ go-backend/handlers.go | 536 +++++++++++++++++---------- go-backend/main.go | 1 - go-backend/models.go | 24 +- 7 files changed, 2155 insertions(+), 212 deletions(-) create mode 100644 go-backend/docs/docs.go create mode 100644 go-backend/docs/swagger.json create mode 100644 go-backend/docs/swagger.yaml diff --git a/go-backend/database.go b/go-backend/database.go index 8090398..4e1b7aa 100644 --- a/go-backend/database.go +++ b/go-backend/database.go @@ -7,6 +7,7 @@ import ( "log" "os" "path/filepath" + "strings" "github.com/juli0n21/go-osu-parser/parser" _ "modernc.org/sqlite" @@ -168,7 +169,7 @@ func rebuildDb(db *sql.DB, osuDb *parser.OsuDB) error { stmt = tx.Stmt(stmt) for i, beatmap := range osuDb.Beatmaps { - fmt.Println(i, beatmap.Artist, beatmap.SongTitle, beatmap.MD5Hash) + //fmt.Println(i, beatmap.Artist, beatmap.SongTitle, beatmap.MD5Hash) _, err := stmt.Exec( beatmap.DifficultyID, beatmap.Artist, beatmap.ArtistUnicode, beatmap.SongTitle, beatmap.SongTitleUnicode, beatmap.Creator, @@ -294,7 +295,7 @@ func scanSongs(rows *sql.Rows) ([]Song, error) { s.Image = fmt.Sprintf("404.png") } else { if len(bm.Events) > 1 && len(bm.Events[0].EventParams) > 1 { - s.Image = fmt.Sprintf("%s/%s", s.Folder, bm.Events[0].EventParams[0]) + s.Image = fmt.Sprintf("%s/%s", s.Folder, strings.Trim(bm.Events[0].EventParams[0], "\"")) } } @@ -319,7 +320,7 @@ func scanSong(row *sql.Row) (Song, error) { } if len(bm.Events) > 1 && len(bm.Events[0].EventParams) > 1 { - s.Image = fmt.Sprintf("%s/%s", s.Folder, bm.Events[0].EventParams[0]) + s.Image = fmt.Sprintf("%s/%s", s.Folder, strings.Trim(bm.Events[0].EventParams[0], "\"")) } return s, nil diff --git a/go-backend/docs/docs.go b/go-backend/docs/docs.go new file mode 100644 index 0000000..50aef29 --- /dev/null +++ b/go-backend/docs/docs.go @@ -0,0 +1,687 @@ +// 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": { + "/audio/{filepath}": { + "get": { + "description": "Retrieves a song file from the server based on the provided encoded filepath", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "files" + ], + "summary": "Retrieves a song file by its encoded path", + "parameters": [ + { + "type": "string", + "description": "Base64 encoded file path", + "name": "filepath", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "The requested song file", + "schema": { + "type": "file" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "File Not Found", + "schema": { + "type": "string" + } + } + } + } + }, + "/collection": { + "get": { + "description": "Retrieves a collection of songs using the provided index.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a collection of songs by index", + "parameters": [ + { + "type": "integer", + "description": "Index", + "name": "index", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + }, + "400": { + "description": "Invalid parameter", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/collections": { + "get": { + "description": "Retrieves collections from the database with pagination based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Retrieves collections based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of collections", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Collection" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/image/{filepath}": { + "get": { + "description": "Retrieves an image file from the server based on the provided encoded filepath", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "files" + ], + "summary": "Retrieves an image file by its encoded path", + "parameters": [ + { + "type": "string", + "description": "Base64 encoded file path", + "name": "filepath", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "The requested image file", + "schema": { + "type": "file" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "File Not Found", + "schema": { + "type": "string" + } + } + } + } + }, + "/login": { + "get": { + "description": "Redirects users to an external authentication page", + "tags": [ + "auth" + ], + "summary": "Redirect to login page", + "responses": { + "307": { + "description": "Temporary Redirect", + "schema": { + "type": "string" + } + } + } + } + }, + "/ping": { + "get": { + "description": "Returns a pong response if the server is running", + "tags": [ + "health" + ], + "summary": "Check server health", + "responses": { + "200": { + "description": "pong", + "schema": { + "type": "string" + } + } + } + } + }, + "/search/active": { + "get": { + "description": "Searches active records in the database based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Searches active records based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Active search result", + "schema": { + "$ref": "#/definitions/main.ActiveSearch" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/search/artist": { + "get": { + "description": "Searches for artists in the database based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Searches for artists based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of artists", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/search/collections": { + "get": { + "description": "Searches collections in the database based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Searches collections based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of collections", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Collection" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/song/{hash}": { + "get": { + "description": "Retrieves a song using its unique hash identifier.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a song by its hash", + "parameters": [ + { + "type": "string", + "description": "Song hash", + "name": "hash", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.Song" + } + }, + "400": { + "description": "Invalid parameter", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Song not found", + "schema": { + "type": "string" + } + } + } + } + }, + "/songs/favorites": { + "get": { + "description": "Retrieves favorite songs filtered by a query with pagination support.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a list of favorite songs based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + }, + "400": { + "description": "Invalid parameter", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/songs/recents": { + "get": { + "description": "Retrieves recent songs with pagination support.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a list of recent songs", + "parameters": [ + { + "type": "integer", + "default": 10, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "main.ActiveSearch": { + "description": "ActiveSearch holds search results for a given artist", + "type": "object", + "properties": { + "artist": { + "type": "string", + "example": "Ed Sheeran" + }, + "songs": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + } + }, + "main.Collection": { + "description": "Collection holds a list of songs", + "type": "object", + "properties": { + "items": { + "type": "integer", + "example": 15 + }, + "name": { + "type": "string", + "example": "Best of 2023" + }, + "songs": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + } + }, + "main.Song": { + "description": "Song represents a song with metadata", + "type": "object", + "properties": { + "artist": { + "type": "string", + "example": "Ed Sheeran" + }, + "audio": { + "type": "string", + "example": "audio.mp3" + }, + "beatmap_id": { + "type": "integer", + "example": 123456 + }, + "creator": { + "type": "string", + "example": "JohnDoe" + }, + "file": { + "type": "string", + "example": "beatmap.osu" + }, + "folder": { + "type": "string", + "example": "osu/Songs/123456" + }, + "image": { + "type": "string", + "example": "cover.jpg" + }, + "md5_hash": { + "type": "string", + "example": "abcd1234efgh5678" + }, + "title": { + "type": "string", + "example": "Shape of You" + }, + "total_time": { + "type": "integer", + "example": 240 + }, + "url": { + "type": "string", + "example": "https://osu.ppy.sh/beatmaps/123456" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "localhost:8080", + BasePath: "/api/v1/", + Schemes: []string{}, + Title: "go-osu-music-hoster", + Description: "Server Hosting ur own osu files over a simple Api", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/go-backend/docs/swagger.json b/go-backend/docs/swagger.json new file mode 100644 index 0000000..910074c --- /dev/null +++ b/go-backend/docs/swagger.json @@ -0,0 +1,663 @@ +{ + "swagger": "2.0", + "info": { + "description": "Server Hosting ur own osu files over a simple Api", + "title": "go-osu-music-hoster", + "contact": {}, + "version": "1.0" + }, + "host": "localhost:8080", + "basePath": "/api/v1/", + "paths": { + "/audio/{filepath}": { + "get": { + "description": "Retrieves a song file from the server based on the provided encoded filepath", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "files" + ], + "summary": "Retrieves a song file by its encoded path", + "parameters": [ + { + "type": "string", + "description": "Base64 encoded file path", + "name": "filepath", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "The requested song file", + "schema": { + "type": "file" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "File Not Found", + "schema": { + "type": "string" + } + } + } + } + }, + "/collection": { + "get": { + "description": "Retrieves a collection of songs using the provided index.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a collection of songs by index", + "parameters": [ + { + "type": "integer", + "description": "Index", + "name": "index", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + }, + "400": { + "description": "Invalid parameter", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/collections": { + "get": { + "description": "Retrieves collections from the database with pagination based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Retrieves collections based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of collections", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Collection" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/image/{filepath}": { + "get": { + "description": "Retrieves an image file from the server based on the provided encoded filepath", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "files" + ], + "summary": "Retrieves an image file by its encoded path", + "parameters": [ + { + "type": "string", + "description": "Base64 encoded file path", + "name": "filepath", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "The requested image file", + "schema": { + "type": "file" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "File Not Found", + "schema": { + "type": "string" + } + } + } + } + }, + "/login": { + "get": { + "description": "Redirects users to an external authentication page", + "tags": [ + "auth" + ], + "summary": "Redirect to login page", + "responses": { + "307": { + "description": "Temporary Redirect", + "schema": { + "type": "string" + } + } + } + } + }, + "/ping": { + "get": { + "description": "Returns a pong response if the server is running", + "tags": [ + "health" + ], + "summary": "Check server health", + "responses": { + "200": { + "description": "pong", + "schema": { + "type": "string" + } + } + } + } + }, + "/search/active": { + "get": { + "description": "Searches active records in the database based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Searches active records based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Active search result", + "schema": { + "$ref": "#/definitions/main.ActiveSearch" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/search/artist": { + "get": { + "description": "Searches for artists in the database based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Searches for artists based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of artists", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/search/collections": { + "get": { + "description": "Searches collections in the database based on the query parameter", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Searches collections based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit the number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of collections", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Collection" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "string" + } + } + } + } + }, + "/song/{hash}": { + "get": { + "description": "Retrieves a song using its unique hash identifier.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a song by its hash", + "parameters": [ + { + "type": "string", + "description": "Song hash", + "name": "hash", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.Song" + } + }, + "400": { + "description": "Invalid parameter", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Song not found", + "schema": { + "type": "string" + } + } + } + } + }, + "/songs/favorites": { + "get": { + "description": "Retrieves favorite songs filtered by a query with pagination support.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a list of favorite songs based on a query", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query", + "required": true + }, + { + "type": "integer", + "default": 10, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + }, + "400": { + "description": "Invalid parameter", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/songs/recents": { + "get": { + "description": "Retrieves recent songs with pagination support.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "songs" + ], + "summary": "Get a list of recent songs", + "parameters": [ + { + "type": "integer", + "default": 10, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "main.ActiveSearch": { + "description": "ActiveSearch holds search results for a given artist", + "type": "object", + "properties": { + "artist": { + "type": "string", + "example": "Ed Sheeran" + }, + "songs": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + } + }, + "main.Collection": { + "description": "Collection holds a list of songs", + "type": "object", + "properties": { + "items": { + "type": "integer", + "example": 15 + }, + "name": { + "type": "string", + "example": "Best of 2023" + }, + "songs": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Song" + } + } + } + }, + "main.Song": { + "description": "Song represents a song with metadata", + "type": "object", + "properties": { + "artist": { + "type": "string", + "example": "Ed Sheeran" + }, + "audio": { + "type": "string", + "example": "audio.mp3" + }, + "beatmap_id": { + "type": "integer", + "example": 123456 + }, + "creator": { + "type": "string", + "example": "JohnDoe" + }, + "file": { + "type": "string", + "example": "beatmap.osu" + }, + "folder": { + "type": "string", + "example": "osu/Songs/123456" + }, + "image": { + "type": "string", + "example": "cover.jpg" + }, + "md5_hash": { + "type": "string", + "example": "abcd1234efgh5678" + }, + "title": { + "type": "string", + "example": "Shape of You" + }, + "total_time": { + "type": "integer", + "example": 240 + }, + "url": { + "type": "string", + "example": "https://osu.ppy.sh/beatmaps/123456" + } + } + } + } +} \ No newline at end of file diff --git a/go-backend/docs/swagger.yaml b/go-backend/docs/swagger.yaml new file mode 100644 index 0000000..f5ea579 --- /dev/null +++ b/go-backend/docs/swagger.yaml @@ -0,0 +1,449 @@ +basePath: /api/v1/ +definitions: + main.ActiveSearch: + description: ActiveSearch holds search results for a given artist + properties: + artist: + example: Ed Sheeran + type: string + songs: + items: + $ref: '#/definitions/main.Song' + type: array + type: object + main.Collection: + description: Collection holds a list of songs + properties: + items: + example: 15 + type: integer + name: + example: Best of 2023 + type: string + songs: + items: + $ref: '#/definitions/main.Song' + type: array + type: object + main.Song: + description: Song represents a song with metadata + properties: + artist: + example: Ed Sheeran + type: string + audio: + example: audio.mp3 + type: string + beatmap_id: + example: 123456 + type: integer + creator: + example: JohnDoe + type: string + file: + example: beatmap.osu + type: string + folder: + example: osu/Songs/123456 + type: string + image: + example: cover.jpg + type: string + md5_hash: + example: abcd1234efgh5678 + type: string + title: + example: Shape of You + type: string + total_time: + example: 240 + type: integer + url: + example: https://osu.ppy.sh/beatmaps/123456 + type: string + type: object +host: localhost:8080 +info: + contact: {} + description: Server Hosting ur own osu files over a simple Api + title: go-osu-music-hoster + version: "1.0" +paths: + /audio/{filepath}: + get: + consumes: + - application/json + description: Retrieves a song file from the server based on the provided encoded + filepath + parameters: + - description: Base64 encoded file path + in: path + name: filepath + required: true + type: string + produces: + - application/json + responses: + "200": + description: The requested song file + schema: + type: file + "400": + description: Bad Request + schema: + type: string + "404": + description: File Not Found + schema: + type: string + summary: Retrieves a song file by its encoded path + tags: + - files + /collection: + get: + consumes: + - application/json + description: Retrieves a collection of songs using the provided index. + parameters: + - description: Index + in: query + name: index + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/main.Song' + type: array + "400": + description: Invalid parameter + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Get a collection of songs by index + tags: + - songs + /collections: + get: + consumes: + - application/json + description: Retrieves collections from the database with pagination based on + the query parameter + parameters: + - description: Search query + in: query + name: query + type: string + - default: 10 + description: Limit the number of results + in: query + name: limit + type: integer + - default: 0 + description: Offset for pagination + in: query + name: offset + type: integer + produces: + - application/json + responses: + "200": + description: List of collections + schema: + items: + $ref: '#/definitions/main.Collection' + type: array + "400": + description: Bad Request + schema: + type: string + "500": + description: Internal Server Error + schema: + type: string + summary: Retrieves collections based on a query + tags: + - search + /image/{filepath}: + get: + consumes: + - application/json + description: Retrieves an image file from the server based on the provided encoded + filepath + parameters: + - description: Base64 encoded file path + in: path + name: filepath + required: true + type: string + produces: + - application/json + responses: + "200": + description: The requested image file + schema: + type: file + "400": + description: Bad Request + schema: + type: string + "404": + description: File Not Found + schema: + type: string + summary: Retrieves an image file by its encoded path + tags: + - files + /login: + get: + description: Redirects users to an external authentication page + responses: + "307": + description: Temporary Redirect + schema: + type: string + summary: Redirect to login page + tags: + - auth + /ping: + get: + description: Returns a pong response if the server is running + responses: + "200": + description: pong + schema: + type: string + summary: Check server health + tags: + - health + /search/active: + get: + consumes: + - application/json + description: Searches active records in the database based on the query parameter + parameters: + - description: Search query + in: query + name: query + required: true + type: string + - default: 10 + description: Limit the number of results + in: query + name: limit + type: integer + - default: 0 + description: Offset for pagination + in: query + name: offset + type: integer + produces: + - application/json + responses: + "200": + description: Active search result + schema: + $ref: '#/definitions/main.ActiveSearch' + "400": + description: Bad Request + schema: + type: string + "500": + description: Internal Server Error + schema: + type: string + summary: Searches active records based on a query + tags: + - search + /search/artist: + get: + consumes: + - application/json + description: Searches for artists in the database based on the query parameter + parameters: + - description: Search query + in: query + name: query + required: true + type: string + - default: 10 + description: Limit the number of results + in: query + name: limit + type: integer + - default: 0 + description: Offset for pagination + in: query + name: offset + type: integer + produces: + - application/json + responses: + "200": + description: List of artists + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + type: string + "500": + description: Internal Server Error + schema: + type: string + summary: Searches for artists based on a query + tags: + - search + /search/collections: + get: + consumes: + - application/json + description: Searches collections in the database based on the query parameter + parameters: + - description: Search query + in: query + name: query + required: true + type: string + - default: 10 + description: Limit the number of results + in: query + name: limit + type: integer + - default: 0 + description: Offset for pagination + in: query + name: offset + type: integer + produces: + - application/json + responses: + "200": + description: List of collections + schema: + items: + $ref: '#/definitions/main.Collection' + type: array + "400": + description: Bad Request + schema: + type: string + "500": + description: Internal Server Error + schema: + type: string + summary: Searches collections based on a query + tags: + - search + /song/{hash}: + get: + consumes: + - application/json + description: Retrieves a song using its unique hash identifier. + parameters: + - description: Song hash + in: path + name: hash + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/main.Song' + "400": + description: Invalid parameter + schema: + type: string + "404": + description: Song not found + schema: + type: string + summary: Get a song by its hash + tags: + - songs + /songs/favorites: + get: + consumes: + - application/json + description: Retrieves favorite songs filtered by a query with pagination support. + parameters: + - description: Search query + in: query + name: query + required: true + type: string + - default: 10 + description: Limit + in: query + name: limit + type: integer + - default: 0 + description: Offset + in: query + name: offset + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/main.Song' + type: array + "400": + description: Invalid parameter + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Get a list of favorite songs based on a query + tags: + - songs + /songs/recents: + get: + consumes: + - application/json + description: Retrieves recent songs with pagination support. + parameters: + - default: 10 + description: Limit + in: query + name: limit + type: integer + - default: 0 + description: Offset + in: query + name: offset + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/main.Song' + type: array + "500": + description: Internal server error + schema: + type: string + summary: Get a list of recent songs + tags: + - songs +swagger: "2.0" diff --git a/go-backend/handlers.go b/go-backend/handlers.go index 68d9c96..aafaced 100644 --- a/go-backend/handlers.go +++ b/go-backend/handlers.go @@ -11,6 +11,8 @@ import ( "os" "strconv" + _ "backend/docs" + httpSwagger "github.com/swaggo/http-swagger" "github.com/juli0n21/go-osu-parser/parser" @@ -27,206 +29,350 @@ type Server struct { OsuDb *parser.OsuDB } +func (s *Server) registerRoutes() { + http.HandleFunc("/api/v1/ping", s.ping) + http.HandleFunc("/api/v1/login", s.login) + + http.HandleFunc("/api/v1/song/{hash}/", s.song) + http.HandleFunc("/api/v1/songs/recents", s.recents) + http.HandleFunc("/api/v1/songs/favorites", s.favorites) + + http.HandleFunc("/api/v1/collection", s.collection) + http.HandleFunc("/api/v1/collections", s.collections) + http.HandleFunc("/api/v1/search/collections", s.collectionSearch) + + http.HandleFunc("/api/v1/search/active", s.activeSearch) + http.HandleFunc("/api/v1/search/artist", s.artistSearch) + + http.HandleFunc("/api/v1/audio/{filepath}", s.songFile) + http.HandleFunc("/api/v1/image/{filepath}", s.imageFile) + http.Handle("/swagger-ui", httpSwagger.WrapHandler) +} + func run(s *Server) { - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "pong") - }) - - http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "https://proxy.illegalesachen.download/login", http.StatusTemporaryRedirect) - }) - - http.HandleFunc("/api/v1/songs/{hash}/", func(w http.ResponseWriter, r *http.Request) { - - hash := r.PathValue("hash") - if hash == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - song, err := getSong(s.Db, hash) - if err != nil { - fmt.Println(err) - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusNotFound) - return - } - - writeJSON(w, song, http.StatusOK) - }) - - http.HandleFunc("/api/v1/songs/recent", func(w http.ResponseWriter, r *http.Request) { - - limit, offset := pagination(r) - recent, err := getRecent(s.Db, limit, offset) - if err != nil { - fmt.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, http.StatusOK) - }) - - http.HandleFunc("/api/v1/songs/favorite", func(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query().Get("query") - if query == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - limit, offset := pagination(r) - - favorites, err := getFavorites(s.Db, query, limit, offset) - if err != nil { - fmt.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, favorites, http.StatusOK) - }) - - http.HandleFunc("/api/v1/collections", func(w http.ResponseWriter, r *http.Request) { - index, err := strconv.Atoi(r.URL.Query().Get("index")) - if err != nil { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - recent, err := getCollection(s.Db, index) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, http.StatusOK) - }) - - http.HandleFunc("/api/v1/collections/", func(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query().Get("query") - if query == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - limit, offset := pagination(r) - - recent, err := getCollections(s.Db, query, limit, offset) - if err != nil { - fmt.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, http.StatusOK) - }) - - http.HandleFunc("/api/v1/audio/{filepath}", func(w http.ResponseWriter, r *http.Request) { - f := r.PathValue("filepath") - if f == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - filename, err := base64.RawStdEncoding.DecodeString(f) - if err != nil { - fmt.Println(err) - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - file, err := os.Open(s.OsuDir + "Songs/" + string(filename)) - if err != nil { - fmt.Println(err) - http.Error(w, ErrFileNotFound.Error(), http.StatusNotFound) - return - } - defer file.Close() - - stat, err := file.Stat() - if err != nil { - fmt.Println(err) - http.Error(w, ErrFileNotFound.Error(), http.StatusNotFound) - return - } - - http.ServeContent(w, r, stat.Name(), stat.ModTime(), file) - }) - - http.HandleFunc("/api/v1/search/active", func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query().Get("query") - if q == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - limit, offset := pagination(r) - - recent, err := getSearch(s.Db, q, limit, offset) - if err != nil { - fmt.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, http.StatusOK) - }) - - http.HandleFunc("/api/v1/search/artist", func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query().Get("query") - if q == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - limit, offset := pagination(r) - - recent, err := getArtists(s.Db, q, limit, offset) - if err != nil { - fmt.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, http.StatusOK) - }) - - http.HandleFunc("/api/v1/search/collections", func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query().Get("query") - if q == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - limit, offset := pagination(r) - - recent, err := getCollections(s.Db, q, limit, offset) - if err != nil { - fmt.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, http.StatusOK) - }) - - http.HandleFunc("/api/v1/images/{filename}", func(w http.ResponseWriter, r *http.Request) { - f := r.PathValue("filename") - if f == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - filename, err := base64.RawStdEncoding.DecodeString(f) - if err != nil { - fmt.Println(err) - http.Error(w, ErrFailedToParseEncoded.Error(), http.StatusBadRequest) - return - } - - http.ServeFile(w, r, s.OsuDir+"Songs/"+string(filename)) - }) - - http.Handle("/swagger/", httpSwagger.WrapHandler) - + s.registerRoutes() fmt.Println("starting server on http://localhost" + s.Port) log.Fatal(http.ListenAndServe(s.Port, nil)) } +// ping godoc +// @Summary Check server health +// @Description Returns a pong response if the server is running +// @Tags health +// @Success 200 {string} string "pong" +// @Router /ping [get] +func (s *Server) ping(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "pong") +} + +// login godoc +// @Summary Redirect to login page +// @Description Redirects users to an external authentication page +// @Tags auth +// @Success 307 {string} string "Temporary Redirect" +// @Router /login [get] +func (s *Server) login(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "https://proxy.illegalesachen.download/login", http.StatusTemporaryRedirect) +} + +// song godoc +// @Summary Get a song by its hash +// @Description Retrieves a song using its unique hash identifier. +// @Tags songs +// @Accept json +// @Produce json +// @Param hash path string true "Song hash" +// @Success 200 {object} Song +// @Failure 400 {string} string "Invalid parameter" +// @Failure 404 {string} string "Song not found" +// @Router /song/{hash} [get] +func (s *Server) song(w http.ResponseWriter, r *http.Request) { + + hash := r.PathValue("hash") + if hash == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + song, err := getSong(s.Db, hash) + if err != nil { + fmt.Println(err) + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusNotFound) + return + } + + writeJSON(w, song, http.StatusOK) +} + +// recents godoc +// @Summary Get a list of recent songs +// @Description Retrieves recent songs with pagination support. +// @Tags songs +// @Accept json +// @Produce json +// @Param limit query int false "Limit" default(10) +// @Param offset query int false "Offset" default(0) +// @Success 200 {array} Song +// @Failure 500 {string} string "Internal server error" +// @Router /songs/recents [get] +func (s *Server) recents(w http.ResponseWriter, r *http.Request) { + + limit, offset := pagination(r) + recent, err := getRecent(s.Db, limit, offset) + if err != nil { + fmt.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, recent, http.StatusOK) +} + +// favorites godoc +// @Summary Get a list of favorite songs based on a query +// @Description Retrieves favorite songs filtered by a query with pagination support. +// @Tags songs +// @Accept json +// @Produce json +// @Param query query string true "Search query" +// @Param limit query int false "Limit" default(10) +// @Param offset query int false "Offset" default(0) +// @Success 200 {array} Song +// @Failure 400 {string} string "Invalid parameter" +// @Failure 500 {string} string "Internal server error" +// @Router /songs/favorites [get] +func (s *Server) favorites(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query().Get("query") + if query == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + limit, offset := pagination(r) + + favorites, err := getFavorites(s.Db, query, limit, offset) + if err != nil { + fmt.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, favorites, http.StatusOK) +} + +// collection godoc +// @Summary Get a collection of songs by index +// @Description Retrieves a collection of songs using the provided index. +// @Tags songs +// @Accept json +// @Produce json +// @Param index query int true "Index" +// @Success 200 {array} Song +// @Failure 400 {string} string "Invalid parameter" +// @Failure 500 {string} string "Internal server error" +// @Router /collection [get] +func (s *Server) collection(w http.ResponseWriter, r *http.Request) { + index, err := strconv.Atoi(r.URL.Query().Get("index")) + if err != nil { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + recent, err := getCollection(s.Db, index) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, recent, http.StatusOK) +} + +// @Summary Retrieves collections based on a query +// @Description Retrieves collections from the database with pagination based on the query parameter +// @Tags search +// @Accept json +// @Produce json +// @Param query query string false "Search query" +// @Param limit query int false "Limit the number of results" default(10) +// @Param offset query int false "Offset for pagination" default(0) +// @Success 200 {array} Collection "List of collections" +// @Failure 400 {object} string "Bad Request" +// @Failure 500 {object} string "Internal Server Error" +// @Router /collections [get] +func (s *Server) collections(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query().Get("query") + if query == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + limit, offset := pagination(r) + + recent, err := getCollections(s.Db, query, limit, offset) + if err != nil { + fmt.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, recent, http.StatusOK) +} + +// @Summary Searches collections based on a query +// @Description Searches collections in the database based on the query parameter +// @Tags search +// @Accept json +// @Produce json +// @Param query query string true "Search query" +// @Param limit query int false "Limit the number of results" default(10) +// @Param offset query int false "Offset for pagination" default(0) +// @Success 200 {array} Collection "List of collections" +// @Failure 400 {object} string "Bad Request" +// @Failure 500 {object} string "Internal Server Error" +// @Router /search/collections [get] +func (s *Server) collectionSearch(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query().Get("query") + if q == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + limit, offset := pagination(r) + + recent, err := getCollections(s.Db, q, limit, offset) + if err != nil { + fmt.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, recent, http.StatusOK) +} + +// @Summary Searches active records based on a query +// @Description Searches active records in the database based on the query parameter +// @Tags search +// @Accept json +// @Produce json +// @Param query query string true "Search query" +// @Param limit query int false "Limit the number of results" default(10) +// @Param offset query int false "Offset for pagination" default(0) +// @Success 200 {object} ActiveSearch "Active search result" +// @Failure 400 {object} string "Bad Request" +// @Failure 500 {object} string "Internal Server Error" +// @Router /search/active [get] +func (s *Server) activeSearch(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query().Get("query") + if q == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + limit, offset := pagination(r) + + recent, err := getSearch(s.Db, q, limit, offset) + if err != nil { + fmt.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, recent, http.StatusOK) +} + +// @Summary Searches for artists based on a query +// @Description Searches for artists in the database based on the query parameter +// @Tags search +// @Accept json +// @Produce json +// @Param query query string true "Search query" +// @Param limit query int false "Limit the number of results" default(10) +// @Param offset query int false "Offset for pagination" default(0) +// @Success 200 {array} string "List of artists" +// @Failure 400 {object} string "Bad Request" +// @Failure 500 {object} string "Internal Server Error" +// @Router /search/artist [get] +func (s *Server) artistSearch(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query().Get("query") + if q == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + limit, offset := pagination(r) + + recent, err := getArtists(s.Db, q, limit, offset) + if err != nil { + fmt.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, recent, http.StatusOK) +} + +// @Summary Retrieves a song file by its encoded path +// @Description Retrieves a song file from the server based on the provided encoded filepath +// @Tags files +// @Accept json +// @Produce json +// @Param filepath path string true "Base64 encoded file path" +// @Success 200 {file} File "The requested song file" +// @Failure 400 {object} string "Bad Request" +// @Failure 404 {object} string "File Not Found" +// @Router /audio/{filepath} [get] +func (s *Server) songFile(w http.ResponseWriter, r *http.Request) { + f := r.PathValue("filepath") + if f == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + filename, err := base64.RawStdEncoding.DecodeString(f) + if err != nil { + fmt.Println(err) + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + file, err := os.Open(s.OsuDir + "Songs/" + string(filename)) + if err != nil { + fmt.Println(err) + http.Error(w, ErrFileNotFound.Error(), http.StatusNotFound) + return + } + defer file.Close() + + stat, err := file.Stat() + if err != nil { + fmt.Println(err) + http.Error(w, ErrFileNotFound.Error(), http.StatusNotFound) + return + } + + http.ServeContent(w, r, stat.Name(), stat.ModTime(), file) +} + +// @Summary Retrieves an image file by its encoded path +// @Description Retrieves an image file from the server based on the provided encoded filepath +// @Tags files +// @Accept json +// @Produce json +// @Param filepath path string true "Base64 encoded file path" +// @Success 200 {file} File "The requested image file" +// @Failure 400 {object} string "Bad Request" +// @Failure 404 {object} string "File Not Found" +// @Router /image/{filepath} [get] +func (s *Server) imageFile(w http.ResponseWriter, r *http.Request) { + f := r.PathValue("filepath") + if f == "" { + http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) + return + } + + filename, err := base64.RawStdEncoding.DecodeString(f) + if err != nil { + fmt.Println(err) + http.Error(w, ErrFailedToParseEncoded.Error(), http.StatusBadRequest) + return + } + + http.ServeFile(w, r, s.OsuDir+"Songs/"+string(filename)) +} + func writeJSON(w http.ResponseWriter, v any, status int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) diff --git a/go-backend/main.go b/go-backend/main.go index 8621c54..ad7d095 100644 --- a/go-backend/main.go +++ b/go-backend/main.go @@ -36,7 +36,6 @@ func main() { s := &Server{ Port: ":8080", Db: db, - OsuDb: osuDb, OsuDir: osuRoot, } diff --git a/go-backend/models.go b/go-backend/models.go index db7adc8..ffae985 100644 --- a/go-backend/models.go +++ b/go-backend/models.go @@ -1,21 +1,19 @@ package main -import "time" - // Song represents a song entity // @Description Song represents a song with metadata type Song struct { - BeatmapID int `json:"beatmap_id" example:"123456"` - MD5Hash string `json:"md5_hash" example:"abcd1234efgh5678"` - Title string `json:"title" example:"Shape of You"` - Artist string `json:"artist" example:"Ed Sheeran"` - Creator string `json:"creator" example:"JohnDoe"` - Folder string `json:"folder" example:"osu/Songs/123456"` - File string `json:"file" example:"beatmap.osu"` - Audio string `json:"audio" example:"audio.mp3"` - TotalTime time.Duration `json:"total_time" example:"240"` - Url string `json:"url" example:"https://osu.ppy.sh/beatmaps/123456"` - Image string `json:"image" example:"cover.jpg"` + BeatmapID int `json:"beatmap_id" example:"123456"` + MD5Hash string `json:"md5_hash" example:"abcd1234efgh5678"` + Title string `json:"title" example:"Shape of You"` + Artist string `json:"artist" example:"Ed Sheeran"` + Creator string `json:"creator" example:"JohnDoe"` + Folder string `json:"folder" example:"osu/Songs/123456"` + File string `json:"file" example:"beatmap.osu"` + Audio string `json:"audio" example:"audio.mp3"` + TotalTime int64 `json:"total_time" example:"240"` + Url string `json:"url" example:"https://osu.ppy.sh/beatmaps/123456"` + Image string `json:"image" example:"cover.jpg"` } // CollectionPreview represents a preview of a song collection