Compare commits
18 Commits
main
..
f0653489d6
| Author | SHA1 | Date | |
|---|---|---|---|
| f0653489d6 | |||
| d0fbba86f1 | |||
| bd0e7f4a8d | |||
| b5926e3b31 | |||
| 37909139de | |||
| 82252ce1ff | |||
| 1dab9d6e7c | |||
| b80ad90eab | |||
| 2cff8d16d7 | |||
| 12f18ba12c | |||
| 6e2c381d90 | |||
| efca22834b | |||
| e57609725e | |||
| fabd6a6931 | |||
| f03e001bdd | |||
| 1d77ae491c | |||
| c0d1aaa4d1 | |||
| 76aaa884fa |
@@ -22,19 +22,19 @@ jobs:
|
||||
uses: https://github.com/docker/setup-buildx-action@v3
|
||||
with:
|
||||
config-inline: |
|
||||
[registry."gitea.sanplex.tech/sansan"]
|
||||
[registry."gitea.sanplex.xyz/sansan"]
|
||||
http = true
|
||||
insecure = true
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.sanplex.tech
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.TOKEN }}
|
||||
- name: Build
|
||||
uses: https://github.com/docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: false
|
||||
#tags: "gitea.sanplex.tech/sansan/musicserver:${{gitea.ref_name}}, gitea.sanplex.tech/sansan/musicserver:latest"
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.sanplex.xyz
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.TOKEN }}
|
||||
- name: Build
|
||||
uses: https://github.com/docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: false
|
||||
#tags: "gitea.sanplex.xyz/sansan/musicserver:${{gitea.ref_name}}, gitea.sanplex.xyz/sansan/musicserver:latest"
|
||||
|
||||
@@ -23,19 +23,19 @@ jobs:
|
||||
uses: https://github.com/docker/setup-buildx-action@v3
|
||||
with:
|
||||
config-inline: |
|
||||
[registry."gitea.sanplex.tech/sansan"]
|
||||
[registry."gitea.sanplex.xyz/sansan"]
|
||||
http = true
|
||||
insecure = true
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.sanplex.tech
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.TOKEN }}
|
||||
- name: Build and push Docker image
|
||||
uses: https://github.com/docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: "gitea.sanplex.tech/sansan/musicserver:${{gitea.ref_name}}, gitea.sanplex.tech/sansan/musicserver:latest"
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.sanplex.xyz
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.TOKEN }}
|
||||
- name: Build and push Docker image
|
||||
uses: https://github.com/docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: "gitea.sanplex.xyz/sansan/musicserver:${{gitea.ref_name}}, gitea.sanplex.xyz/sansan/musicserver:latest"
|
||||
|
||||
@@ -23,6 +23,779 @@ var doc = `{
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/character": {
|
||||
"get": {
|
||||
"description": "Returns the image for a specific character",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"image/png"
|
||||
],
|
||||
"tags": [
|
||||
"characters"
|
||||
],
|
||||
"summary": "Get character image",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Character name",
|
||||
"name": "name",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/characters": {
|
||||
"get": {
|
||||
"description": "Returns a list of all available characters",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"characters"
|
||||
],
|
||||
"summary": "Get list of characters",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/dbtest": {
|
||||
"get": {
|
||||
"description": "Tests the database connection",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"database"
|
||||
],
|
||||
"summary": "Test database connection",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "TestedDB",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download": {
|
||||
"get": {
|
||||
"description": "Checks for the latest version of the application",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "Check for latest version",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download/linux": {
|
||||
"get": {
|
||||
"description": "Redirects to download the latest Linux version",
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "Download latest Linux version",
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download/list": {
|
||||
"get": {
|
||||
"description": "Lists all assets available for the latest version",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "List assets of latest version",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download/windows": {
|
||||
"get": {
|
||||
"description": "Redirects to download the latest Windows version",
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "Download latest Windows version",
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"description": "Returns the health status of the server",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"health"
|
||||
],
|
||||
"summary": "Check server health",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music": {
|
||||
"get": {
|
||||
"description": "Returns a specific song by name",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get a specific song",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Song name",
|
||||
"name": "song",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "song can't be empty",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/addPlayed": {
|
||||
"get": {
|
||||
"description": "Adds the latest song to the played list",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Add latest to played",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/addQue": {
|
||||
"get": {
|
||||
"description": "Adds the latest song to the queue",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Add latest to queue",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/all/order": {
|
||||
"get": {
|
||||
"description": "Returns a list of all games in order",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get all games",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/all/random": {
|
||||
"get": {
|
||||
"description": "Returns a list of all games in random order",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get all games random",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/info": {
|
||||
"get": {
|
||||
"description": "Returns information about the current song",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get current song info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/list": {
|
||||
"get": {
|
||||
"description": "Returns a list of played songs",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get played songs list",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/next": {
|
||||
"get": {
|
||||
"description": "Returns the next song in the queue",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get next song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/played": {
|
||||
"put": {
|
||||
"description": "Marks a song as played by its ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Mark song as played",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Song ID",
|
||||
"name": "song",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/previous": {
|
||||
"get": {
|
||||
"description": "Returns the previous song in the queue",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get previous song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/rand": {
|
||||
"get": {
|
||||
"description": "Returns a random song",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get random song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/rand/classic": {
|
||||
"get": {
|
||||
"description": "Returns a random song from the classic selection",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get random classic song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/rand/low": {
|
||||
"get": {
|
||||
"description": "Returns a random song with low chance selection",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get random song with low chance",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/reset": {
|
||||
"get": {
|
||||
"description": "Resets the music state",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Reset music state",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/soundTest": {
|
||||
"get": {
|
||||
"description": "Returns the sound check song",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get sound check song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync": {
|
||||
"get": {
|
||||
"description": "Starts syncing games with only new changes",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Sync games with only changes",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Start syncing games",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync/full": {
|
||||
"get": {
|
||||
"description": "Starts a full sync of all games",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Sync all games fully",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Start syncing games full",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync/progress": {
|
||||
"get": {
|
||||
"description": "Returns the current sync progress or result",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Get sync progress",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync/reset": {
|
||||
"get": {
|
||||
"description": "Resets the games database by deleting all games and songs",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Reset games database",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Games and songs are deleted from the database",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/version": {
|
||||
"get": {
|
||||
"description": "get string by ID",
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
|
||||
def load_openapi_spec(path: str) -> dict:
|
||||
"""Load OpenAPI spec from YAML or JSON file."""
|
||||
try:
|
||||
import yaml
|
||||
with open(path, "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
except ImportError:
|
||||
# Fallback to JSON if PyYAML is not installed
|
||||
with open(path, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
def map_type(openapi_type: str) -> str:
|
||||
"""Map OpenAPI types to GDScript types."""
|
||||
type_mapping = {
|
||||
"integer": "int",
|
||||
"number": "float",
|
||||
"boolean": "bool",
|
||||
"array": "Array",
|
||||
"object": "Dictionary",
|
||||
}
|
||||
return type_mapping.get(openapi_type, "String")
|
||||
|
||||
def default_value(openapi_type: str):
|
||||
"""Return default values for GDScript types."""
|
||||
default_mapping = {
|
||||
"integer": "0",
|
||||
"number": "0.0",
|
||||
"boolean": "false",
|
||||
"array": "[]",
|
||||
"object": "{}",
|
||||
}
|
||||
return default_mapping.get(openapi_type, '""')
|
||||
|
||||
def sanitize_class_name(name: str) -> str:
|
||||
"""Convert a name to a valid GDScript class name."""
|
||||
# Replace invalid characters with underscores
|
||||
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
||||
# Capitalize first letter
|
||||
return name[0].upper() + name[1:] if name else "Model"
|
||||
|
||||
def generate_model_class(class_name: str, schema: dict) -> str:
|
||||
"""Generate a GDScript class for a model."""
|
||||
lines = [
|
||||
f"class_name {class_name}",
|
||||
"extends RefCounted",
|
||||
"",
|
||||
]
|
||||
|
||||
# Add properties
|
||||
properties = schema.get("properties", {})
|
||||
for prop_name, prop_schema in properties.items():
|
||||
prop_type = map_type(prop_schema.get("type", "string"))
|
||||
lines.append(f"var {prop_name}: {prop_type}")
|
||||
|
||||
# Add _init method
|
||||
lines.extend([
|
||||
"",
|
||||
"func _init(data: Dictionary):",
|
||||
])
|
||||
for prop_name in properties:
|
||||
prop_type = map_type(properties[prop_name].get("type", "string"))
|
||||
default = default_value(properties[prop_name].get("type", "string"))
|
||||
lines.append(f' {prop_name} = data.get("{prop_name}", {default})')
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def generate_api_client(path: str, method: str, endpoint: dict) -> str:
|
||||
"""Generate a GDScript API client for an endpoint."""
|
||||
# Sanitize path for class name
|
||||
class_name = sanitize_class_name(path.replace("/", "_").replace("{", "").replace("}", "")) + method.capitalize()
|
||||
# Format URL (replace {param} with %s for Godot's string formatting)
|
||||
url = path.replace("{", "%").replace("}", "s")
|
||||
full_url = f'"{BASE_URL}{url}"'
|
||||
|
||||
lines = [
|
||||
f"class_name {class_name}",
|
||||
"extends RefCounted",
|
||||
"",
|
||||
"var http_request: HTTPRequest",
|
||||
"",
|
||||
"func _init(node: Node):",
|
||||
" http_request = HTTPRequest.new()",
|
||||
" node.add_child(http_request)",
|
||||
' http_request.connect("request_completed", self, "_on_request_completed")',
|
||||
"",
|
||||
f"func call(params: Dictionary, callback: Callable):",
|
||||
f" var url := {full_url}",
|
||||
' var headers = ["User-Agent: MyGodotApp"]',
|
||||
" var error := http_request.request(url, headers)",
|
||||
" if error != OK:",
|
||||
' push_error("HTTP request failed.")',
|
||||
" return",
|
||||
" http_request.set_meta(\"callback\", callback)",
|
||||
"",
|
||||
"func _on_request_completed(result: int, response_code: int, headers: PoolStringArray, body: PoolByteArray):",
|
||||
" var callback := http_request.get_meta(\"callback\")",
|
||||
" if callback:",
|
||||
" var response_body = body.get_string_from_utf8()",
|
||||
" var json = JSON.new()",
|
||||
" if json.parse(response_body) == OK:",
|
||||
" callback.call(json.get_data())",
|
||||
" else:",
|
||||
" callback.call(null)",
|
||||
]
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def generate_tag_client(tag: str, endpoints: list) -> str:
|
||||
"""Generate a GDScript API client for all endpoints with a given tag."""
|
||||
class_name = sanitize_class_name(tag) + "API"
|
||||
|
||||
lines = [
|
||||
f"class_name {class_name}",
|
||||
"extends RefCounted",
|
||||
"",
|
||||
"var http_request: HTTPRequest",
|
||||
"var base_url: String",
|
||||
"",
|
||||
"func _init(node: Node, base_url_param: String):",
|
||||
" http_request = HTTPRequest.new()",
|
||||
" node.add_child(http_request)",
|
||||
' http_request.connect("request_completed", self, "_on_request_completed")',
|
||||
" base_url = base_url_param",
|
||||
"",
|
||||
]
|
||||
|
||||
# Generate a method for each endpoint
|
||||
for path, method, endpoint in endpoints:
|
||||
# Sanitize method name for GDScript
|
||||
method_name = method.lower()
|
||||
# Create a valid function name from the path
|
||||
func_name = "call_" + path.replace("/", "_").replace("{", "").replace("}", "").replace("-", "_")
|
||||
|
||||
# Format URL (replace {param} with %s for Godot's string formatting)
|
||||
url = path.replace("{", "%").replace("}", "s")
|
||||
|
||||
lines.extend([
|
||||
f"func {func_name}(params: Dictionary = {{}}, callback: Callable):",
|
||||
f' var url := base_url + "{url}"',
|
||||
' var headers = ["User-Agent: MyGodotApp"]',
|
||||
f" var error := http_request.request(url, headers, false, HTTPClient.METHOD_{method.upper()})",
|
||||
" if error != OK:",
|
||||
' push_error("HTTP request failed.")',
|
||||
" return",
|
||||
" http_request.set_meta(\"callback\", callback)",
|
||||
"",
|
||||
])
|
||||
|
||||
# Add the completion handler
|
||||
lines.extend([
|
||||
"func _on_request_completed(result: int, response_code: int, headers: PoolStringArray, body: PoolByteArray):",
|
||||
" var callback := http_request.get_meta(\"callback\")",
|
||||
" if callback:",
|
||||
" var response_body = body.get_string_from_utf8()",
|
||||
" var json = JSON.new()",
|
||||
" if json.parse(response_body) == OK:",
|
||||
" callback.call(json.get_data())",
|
||||
" else:",
|
||||
" callback.call(null)",
|
||||
])
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generate_code(spec: dict, output_dir: str):
|
||||
"""Generate all GDScript files from the OpenAPI spec."""
|
||||
# Create output directory
|
||||
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Generate models
|
||||
schemas = spec.get("definitions", {}) # Swagger 2.0 uses "definitions"
|
||||
if not schemas:
|
||||
schemas = spec.get("components", {}).get("schemas", {})
|
||||
|
||||
for schema_name, schema in schemas.items():
|
||||
class_name = sanitize_class_name(schema_name)
|
||||
code = generate_model_class(class_name, schema)
|
||||
output_path = Path(output_dir) / f"{class_name}.gd"
|
||||
with open(output_path, "w") as f:
|
||||
f.write(code)
|
||||
print(f"Generated model: {output_path}")
|
||||
|
||||
# Group endpoints by tag
|
||||
paths = spec.get("paths", {})
|
||||
tag_endpoints = defaultdict(list)
|
||||
|
||||
for path, methods in paths.items():
|
||||
for method, endpoint in methods.items():
|
||||
tags = endpoint.get("tags", ["default"])
|
||||
for tag in tags:
|
||||
tag_endpoints[tag].append((path, method, endpoint))
|
||||
|
||||
# Generate one file per tag
|
||||
for tag, endpoints in tag_endpoints.items():
|
||||
code = generate_tag_client(tag, endpoints)
|
||||
class_name = sanitize_class_name(tag) + "API"
|
||||
output_path = Path(output_dir) / f"{class_name}.gd"
|
||||
with open(output_path, "w") as f:
|
||||
f.write(code)
|
||||
print(f"Generated API client for tag '{tag}': {output_path}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate Godot API clients from OpenAPI spec")
|
||||
parser.add_argument("input", help="Path to the OpenAPI JSON/YAML file")
|
||||
parser.add_argument("-o", "--output", default="godot_generated", help="Output directory for GDScript files")
|
||||
args = parser.parse_args()
|
||||
|
||||
spec = load_openapi_spec(args.input)
|
||||
generate_code(spec, args.output)
|
||||
print("Done!")
|
||||
print("Note: When initializing the API classes, pass the base URL as a parameter:")
|
||||
print(" var music_api = MusicAPI.new()")
|
||||
print(" music_api._init(get_node('/root'), 'http://localhost:8080')")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -4,6 +4,779 @@
|
||||
"contact": {}
|
||||
},
|
||||
"paths": {
|
||||
"/character": {
|
||||
"get": {
|
||||
"description": "Returns the image for a specific character",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"image/png"
|
||||
],
|
||||
"tags": [
|
||||
"characters"
|
||||
],
|
||||
"summary": "Get character image",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Character name",
|
||||
"name": "name",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/characters": {
|
||||
"get": {
|
||||
"description": "Returns a list of all available characters",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"characters"
|
||||
],
|
||||
"summary": "Get list of characters",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/dbtest": {
|
||||
"get": {
|
||||
"description": "Tests the database connection",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"database"
|
||||
],
|
||||
"summary": "Test database connection",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "TestedDB",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download": {
|
||||
"get": {
|
||||
"description": "Checks for the latest version of the application",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "Check for latest version",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download/linux": {
|
||||
"get": {
|
||||
"description": "Redirects to download the latest Linux version",
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "Download latest Linux version",
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download/list": {
|
||||
"get": {
|
||||
"description": "Lists all assets available for the latest version",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "List assets of latest version",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/download/windows": {
|
||||
"get": {
|
||||
"description": "Redirects to download the latest Windows version",
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
],
|
||||
"tags": [
|
||||
"download"
|
||||
],
|
||||
"summary": "Download latest Windows version",
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"description": "Returns the health status of the server",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"health"
|
||||
],
|
||||
"summary": "Check server health",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music": {
|
||||
"get": {
|
||||
"description": "Returns a specific song by name",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get a specific song",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Song name",
|
||||
"name": "song",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "song can't be empty",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/addPlayed": {
|
||||
"get": {
|
||||
"description": "Adds the latest song to the played list",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Add latest to played",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/addQue": {
|
||||
"get": {
|
||||
"description": "Adds the latest song to the queue",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Add latest to queue",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/all/order": {
|
||||
"get": {
|
||||
"description": "Returns a list of all games in order",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get all games",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/all/random": {
|
||||
"get": {
|
||||
"description": "Returns a list of all games in random order",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get all games random",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/info": {
|
||||
"get": {
|
||||
"description": "Returns information about the current song",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get current song info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/list": {
|
||||
"get": {
|
||||
"description": "Returns a list of played songs",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get played songs list",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/next": {
|
||||
"get": {
|
||||
"description": "Returns the next song in the queue",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get next song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/played": {
|
||||
"put": {
|
||||
"description": "Marks a song as played by its ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Mark song as played",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Song ID",
|
||||
"name": "song",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/previous": {
|
||||
"get": {
|
||||
"description": "Returns the previous song in the queue",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get previous song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/rand": {
|
||||
"get": {
|
||||
"description": "Returns a random song",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get random song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/rand/classic": {
|
||||
"get": {
|
||||
"description": "Returns a random song from the classic selection",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get random classic song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/rand/low": {
|
||||
"get": {
|
||||
"description": "Returns a random song with low chance selection",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get random song with low chance",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/reset": {
|
||||
"get": {
|
||||
"description": "Resets the music state",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Reset music state",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/music/soundTest": {
|
||||
"get": {
|
||||
"description": "Returns the sound check song",
|
||||
"produces": [
|
||||
"audio/mpeg"
|
||||
],
|
||||
"tags": [
|
||||
"music"
|
||||
],
|
||||
"summary": "Get sound check song",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync": {
|
||||
"get": {
|
||||
"description": "Starts syncing games with only new changes",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Sync games with only changes",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Start syncing games",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync/full": {
|
||||
"get": {
|
||||
"description": "Starts a full sync of all games",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Sync all games fully",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Start syncing games full",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync/progress": {
|
||||
"get": {
|
||||
"description": "Returns the current sync progress or result",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Get sync progress",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sync/reset": {
|
||||
"get": {
|
||||
"description": "Resets the games database by deleting all games and songs",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"summary": "Reset games database",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Games and songs are deleted from the database",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"423": {
|
||||
"description": "Syncing is in progress",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/version": {
|
||||
"get": {
|
||||
"description": "get string by ID",
|
||||
|
||||
@@ -15,6 +15,514 @@ definitions:
|
||||
info:
|
||||
contact: {}
|
||||
paths:
|
||||
/character:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns the image for a specific character
|
||||
parameters:
|
||||
- description: Character name
|
||||
in: query
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- image/png
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
summary: Get character image
|
||||
tags:
|
||||
- characters
|
||||
/characters:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns a list of all available characters
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
summary: Get list of characters
|
||||
tags:
|
||||
- characters
|
||||
/dbtest:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Tests the database connection
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: TestedDB
|
||||
schema:
|
||||
type: string
|
||||
summary: Test database connection
|
||||
tags:
|
||||
- database
|
||||
/download:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Checks for the latest version of the application
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
summary: Check for latest version
|
||||
tags:
|
||||
- download
|
||||
/download/linux:
|
||||
get:
|
||||
description: Redirects to download the latest Linux version
|
||||
produces:
|
||||
- application/octet-stream
|
||||
responses:
|
||||
"302":
|
||||
description: Found
|
||||
schema:
|
||||
type: string
|
||||
summary: Download latest Linux version
|
||||
tags:
|
||||
- download
|
||||
/download/list:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Lists all assets available for the latest version
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
summary: List assets of latest version
|
||||
tags:
|
||||
- download
|
||||
/download/windows:
|
||||
get:
|
||||
description: Redirects to download the latest Windows version
|
||||
produces:
|
||||
- application/octet-stream
|
||||
responses:
|
||||
"302":
|
||||
description: Found
|
||||
schema:
|
||||
type: string
|
||||
summary: Download latest Windows version
|
||||
tags:
|
||||
- download
|
||||
/health:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns the health status of the server
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
summary: Check server health
|
||||
tags:
|
||||
- health
|
||||
/music:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns a specific song by name
|
||||
parameters:
|
||||
- description: Song name
|
||||
in: query
|
||||
name: song
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"400":
|
||||
description: song can't be empty
|
||||
schema:
|
||||
type: string
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get a specific song
|
||||
tags:
|
||||
- music
|
||||
/music/addPlayed:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Adds the latest song to the played list
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Add latest to played
|
||||
tags:
|
||||
- music
|
||||
/music/addQue:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Adds the latest song to the queue
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Add latest to queue
|
||||
tags:
|
||||
- music
|
||||
/music/all/order:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns a list of all games in order
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get all games
|
||||
tags:
|
||||
- music
|
||||
/music/all/random:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns a list of all games in random order
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get all games random
|
||||
tags:
|
||||
- music
|
||||
/music/info:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns information about the current song
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Get current song info
|
||||
tags:
|
||||
- music
|
||||
/music/list:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns a list of played songs
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
summary: Get played songs list
|
||||
tags:
|
||||
- music
|
||||
/music/next:
|
||||
get:
|
||||
description: Returns the next song in the queue
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get next song
|
||||
tags:
|
||||
- music
|
||||
/music/played:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Marks a song as played by its ID
|
||||
parameters:
|
||||
- description: Song ID
|
||||
in: query
|
||||
name: song
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Mark song as played
|
||||
tags:
|
||||
- music
|
||||
/music/previous:
|
||||
get:
|
||||
description: Returns the previous song in the queue
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get previous song
|
||||
tags:
|
||||
- music
|
||||
/music/rand:
|
||||
get:
|
||||
description: Returns a random song
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get random song
|
||||
tags:
|
||||
- music
|
||||
/music/rand/classic:
|
||||
get:
|
||||
description: Returns a random song from the classic selection
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get random classic song
|
||||
tags:
|
||||
- music
|
||||
/music/rand/low:
|
||||
get:
|
||||
description: Returns a random song with low chance selection
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get random song with low chance
|
||||
tags:
|
||||
- music
|
||||
/music/reset:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Resets the music state
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Reset music state
|
||||
tags:
|
||||
- music
|
||||
/music/soundTest:
|
||||
get:
|
||||
description: Returns the sound check song
|
||||
produces:
|
||||
- audio/mpeg
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: file
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Get sound check song
|
||||
tags:
|
||||
- music
|
||||
/sync:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Starts syncing games with only new changes
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Start syncing games
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Sync games with only changes
|
||||
tags:
|
||||
- sync
|
||||
/sync/full:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Starts a full sync of all games
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Start syncing games full
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Sync all games fully
|
||||
tags:
|
||||
- sync
|
||||
/sync/progress:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns the current sync progress or result
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Get sync progress
|
||||
tags:
|
||||
- sync
|
||||
/sync/reset:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Resets the games database by deleting all games and songs
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Games and songs are deleted from the database
|
||||
schema:
|
||||
type: string
|
||||
"423":
|
||||
description: Syncing is in progress
|
||||
schema:
|
||||
type: string
|
||||
summary: Reset games database
|
||||
tags:
|
||||
- sync
|
||||
/version:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
Generated
+10916
-27824
File diff suppressed because it is too large
Load Diff
+45
-36
@@ -7,42 +7,51 @@
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"config.js": "^0.1.0",
|
||||
"core-js": "^3.8.2",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"nodemon": "^2.0.7",
|
||||
"vue": "^3.0.5",
|
||||
"vue-axios": "^3.2.2",
|
||||
"vuex": "^4.0.0-rc.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.5.10",
|
||||
"@vue/cli-plugin-eslint": "^4.5.10",
|
||||
"@vue/cli-service": "^4.5.10",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^7.4.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {
|
||||
"no-debugger": 1
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"core-js": "^3.37.1",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"vue": "^3.4.31",
|
||||
"vue-axios": "^3.5.2",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/compiler-sfc": "^3.4.31",
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.27.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser",
|
||||
"requireConfigFile": false,
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"globalReturn": false,
|
||||
"impliedStrict": true,
|
||||
"jsx": true
|
||||
},
|
||||
"babelOptions": {
|
||||
"presets": ["@babel/preset-env"]
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"no-debugger": 1
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
#search-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#search_term {
|
||||
width: 60vw;
|
||||
font-size: 2vh;
|
||||
}
|
||||
|
||||
#clear {
|
||||
font-size: 2vh;
|
||||
}
|
||||
|
||||
#games-container{
|
||||
font-size: 2vh;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* Pure CSS styles for Music Search */
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
line-height: 1.5;
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#search-container {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#search_term {
|
||||
width: 60vw;
|
||||
max-width: 600px;
|
||||
font-size: 1.5rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #9ca3af;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #e5e7eb;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#search_term:focus {
|
||||
outline: none;
|
||||
border-color: #6b7280;
|
||||
}
|
||||
|
||||
#clear {
|
||||
font-size: 1.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #f97316;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
#clear:hover {
|
||||
background-color: #ea580c;
|
||||
}
|
||||
|
||||
#games-container {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
/* Game result cards */
|
||||
.bg-green-100 {
|
||||
background-color: #dcfce7;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.shadow-md {
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
#search_term {
|
||||
width: 80vw;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
#clear {
|
||||
font-size: 1.2rem;
|
||||
padding: 0.4rem 0.8rem;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -2,15 +2,15 @@ package web
|
||||
|
||||
templ Base() {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-screen">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Music Search</title>
|
||||
<link href="assets/css/output.css" rel="stylesheet"/>
|
||||
<link href="assets/css/styles.css" rel="stylesheet"/>
|
||||
<script src="assets/js/htmx.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<main class="mx-auto p-4">
|
||||
<body>
|
||||
<main>
|
||||
{ children... }
|
||||
</main>
|
||||
</body>
|
||||
|
||||
+3
-3
@@ -3,12 +3,12 @@ package web
|
||||
templ HelloForm() {
|
||||
@Base() {
|
||||
<div id="search-container">
|
||||
<input class="bg-gray-200 text-black p-2 border border-gray-400 rounded-lg" id="search_term" name="search_term" type="text" hx-post="/find" hx-trigger="keyup changed delay:0.25s" hx-target="#games-container"/>
|
||||
<button type="button" class="bg-orange-500 hover:bg-orange-700 text-white py-2 px-4 rounded" id="clear" name="clear">Clear</button>
|
||||
<input id="search_term" name="search_term" type="text" hx-post="/find" hx-trigger="keyup changed delay:0.25s" hx-target="#games-container"/>
|
||||
<button type="button" id="clear" name="clear">Clear</button>
|
||||
</div>
|
||||
<div id="games-container"></div>
|
||||
<script>
|
||||
document.addEventListener('readystatechange', () => {
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (document.readyState == 'complete') {
|
||||
htmx.ajax('POST', '/find', '#games-container');
|
||||
document.getElementById("search_term").focus();
|
||||
|
||||
@@ -1,56 +1,51 @@
|
||||
module music-server
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.2
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/MShekow/directory-checksum v1.4.9
|
||||
github.com/a-h/templ v0.3.937
|
||||
github.com/golang-migrate/migrate/v4 v4.18.3
|
||||
github.com/jackc/pgx/v5 v5.7.5
|
||||
github.com/labstack/echo/v4 v4.13.4
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/panjf2000/ants/v2 v2.11.3
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/swaggo/echo-swagger v1.4.1
|
||||
github.com/MShekow/directory-checksum v1.4.18
|
||||
github.com/a-h/templ v0.3.1020
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1
|
||||
github.com/jackc/pgx/v5 v5.9.2
|
||||
github.com/labstack/echo/v5 v5.1.1
|
||||
github.com/lib/pq v1.12.3
|
||||
github.com/panjf2000/ants/v2 v2.12.0
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/swaggo/echo-swagger/v2 v2.0.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/docker/docker v27.3.1+incompatible // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/spec v0.22.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/loading v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/sv-tools/openapi v0.4.0 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.uber.org/zap v1.28.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,173 +2,160 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/MShekow/directory-checksum v1.4.9 h1:olzWbrq9ylwfi7afuoivzHM8AV2z2KOaT7FJ6Ri2ppU=
|
||||
github.com/MShekow/directory-checksum v1.4.9/go.mod h1:LhNeWmPftlKTlc3TNurdihPK/whw9j76VnLaTRu2SkU=
|
||||
github.com/MShekow/directory-checksum v1.4.18 h1:1nPPVl7uREa6WMTAPKoWW/GylhnASs0C9C+GPiwLwXA=
|
||||
github.com/MShekow/directory-checksum v1.4.18/go.mod h1:iUupsPb0X0BumQQymLrpD5Pkqe/CbV13OSgosw1oFc4=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/a-h/templ v0.3.937 h1:Ta+0Tf9YuZplUyKTUxReV36FCRKtK6FRMWpmXERHDnM=
|
||||
github.com/a-h/templ v0.3.937/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
||||
github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dhui/dktest v0.4.5 h1:uUfYBIVREmj/Rw6MvgmqNAYzTiKOHJak+enB5Di73MM=
|
||||
github.com/dhui/dktest v0.4.5/go.mod h1:tmcyeHDKagvlDrz7gDKq4UAJOLIfVZYkfD5OnHDwcCo=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4=
|
||||
github.com/dhui/dktest v0.4.6/go.mod h1:JHTSYDtKkvFNFHJKqCzVzqXecyv+tKt8EzceOmQOgbU=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
||||
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
|
||||
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
|
||||
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
|
||||
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=
|
||||
github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=
|
||||
github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w=
|
||||
github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y=
|
||||
github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko=
|
||||
github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg=
|
||||
github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg=
|
||||
github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE=
|
||||
github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4=
|
||||
github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE=
|
||||
github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4=
|
||||
github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
|
||||
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
||||
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
||||
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/labstack/echo/v5 v5.1.1 h1:4QkvKoS8ps5ch49t8b72QS9Z581ytgxhTzxuB/CBA2I=
|
||||
github.com/labstack/echo/v5 v5.1.1/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
|
||||
github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
||||
github.com/panjf2000/ants/v2 v2.12.0 h1:u9JhESo83i/GkZnhfTNuFMMWcNt7mnV1bGJ6FT4wXH8=
|
||||
github.com/panjf2000/ants/v2 v2.12.0/go.mod h1:tSQuaNQ6r6NRhPt+IZVUevvDyFMTs+eS4ztZc52uJTY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk=
|
||||
github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc=
|
||||
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
|
||||
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/sv-tools/openapi v0.4.0 h1:UhD9DVnGox1hfTePNclpUzUFgos57FvzT2jmcAuTOJ4=
|
||||
github.com/sv-tools/openapi v0.4.0/go.mod h1:kD/dG+KP0+Fom1r6nvcj/ORtLus8d8enXT6dyRZDirE=
|
||||
github.com/swaggo/echo-swagger/v2 v2.0.1 h1:jKR3QiK+ciGjxE0+7qZ/azjtlx/pTVls7pJFJqdJoJI=
|
||||
github.com/swaggo/echo-swagger/v2 v2.0.1/go.mod h1:BbgiO9XKX6yYU5Rq4ejqVlQI0mVRv6ziFKd0XgdztnQ=
|
||||
github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
|
||||
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5 h1:fK7d6ET9rrEsdB8IyuwXREWMcyQN3N7gawGFbbrjgHk=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5/go.mod h1:kCL8Fu4Zl8d5tB2Bgj96b8wRowwrwk175bZHXfuGVFI=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
|
||||
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@@ -21,7 +21,7 @@ type assetResponse struct {
|
||||
}
|
||||
|
||||
func CheckLatest() string {
|
||||
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
resp, err := http.Get("https://gitea.sanplex.xyz/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func CheckLatest() string {
|
||||
}
|
||||
|
||||
func ListAssetsOfLatest() []string {
|
||||
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
resp, err := http.Get("https://gitea.sanplex.xyz/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func ListAssetsOfLatest() []string {
|
||||
}
|
||||
|
||||
func DownloadLatestWindows() string {
|
||||
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
resp, err := http.Get("https://gitea.sanplex.xyz/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func DownloadLatestWindows() string {
|
||||
}
|
||||
|
||||
func DownloadLatestLinux() string {
|
||||
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
resp, err := http.Get("https://gitea.sanplex.xyz/api/v1/repos/sansan/MusicPlayer/releases/latest")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.31.1
|
||||
|
||||
package repository
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.31.1
|
||||
// source: game.sql
|
||||
|
||||
package repository
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.31.1
|
||||
|
||||
package repository
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.31.1
|
||||
// source: song.sql
|
||||
|
||||
package repository
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.31.1
|
||||
// source: song_list.sql
|
||||
|
||||
package repository
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// RequestLogger is an Echo middleware that logs HTTP requests using Zap
|
||||
func RequestLogger() echo.MiddlewareFunc {
|
||||
return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||
LogStatus: true,
|
||||
LogURI: true,
|
||||
LogMethod: true,
|
||||
HandleError: true,
|
||||
LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error {
|
||||
logger := GetLogger()
|
||||
|
||||
fields := []zap.Field{
|
||||
zap.String("method", v.Method),
|
||||
zap.String("uri", v.URI),
|
||||
zap.Int("status", v.Status),
|
||||
}
|
||||
|
||||
if v.Error != nil {
|
||||
fields = append(fields, zap.String("error", v.Error.Error()))
|
||||
logger.Error("Request error", fields...)
|
||||
} else {
|
||||
logger.Info("Request completed", fields...)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorHandler is a custom error handler that logs errors
|
||||
func ErrorHandler(err error, c *echo.Context) {
|
||||
logger := GetLogger()
|
||||
logger.Error("Error occurred",
|
||||
zap.String("method", c.Request().Method),
|
||||
zap.String("path", c.Request().URL.Path),
|
||||
zap.String("error", err.Error()),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logger is the global logger instance
|
||||
Logger *zap.Logger
|
||||
|
||||
// SugaredLogger is the global sugared logger instance
|
||||
SugaredLogger *zap.SugaredLogger
|
||||
)
|
||||
|
||||
// Init initializes the logger with the specified level and config
|
||||
func Init(level string, jsonOutput bool) {
|
||||
var config zap.Config
|
||||
|
||||
// Set the log level
|
||||
logLevel := zap.NewAtomicLevel()
|
||||
err := logLevel.UnmarshalText([]byte(level))
|
||||
if err != nil {
|
||||
logLevel.SetLevel(zap.InfoLevel)
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
// JSON output for Grafana Loki
|
||||
config = zap.Config{
|
||||
Level: logLevel,
|
||||
Development: false,
|
||||
Sampling: nil,
|
||||
Encoding: "json",
|
||||
EncoderConfig: zapcore.EncoderConfig{
|
||||
MessageKey: "msg",
|
||||
LevelKey: "level",
|
||||
TimeKey: "time",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
FunctionKey: zapcore.OmitKey,
|
||||
StacktraceKey: "stacktrace",
|
||||
SkipLineEnding: false,
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
},
|
||||
OutputPaths: []string{"stdout"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
InitialFields: map[string]interface{}{"service": "music-server"},
|
||||
}
|
||||
} else {
|
||||
// Human-readable output for development
|
||||
config = zap.Config{
|
||||
Level: logLevel,
|
||||
Development: true,
|
||||
Sampling: nil,
|
||||
Encoding: "console",
|
||||
EncoderConfig: zapcore.EncoderConfig{
|
||||
MessageKey: "msg",
|
||||
LevelKey: "level",
|
||||
TimeKey: "time",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
FunctionKey: zapcore.OmitKey,
|
||||
StacktraceKey: "stacktrace",
|
||||
SkipLineEnding: false,
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
},
|
||||
OutputPaths: []string{"stdout"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
InitialFields: map[string]interface{}{"service": "music-server"},
|
||||
}
|
||||
}
|
||||
|
||||
logger, err := config.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Logger = logger
|
||||
SugaredLogger = logger.Sugar()
|
||||
}
|
||||
|
||||
// GetLogger returns the global logger
|
||||
func GetLogger() *zap.Logger {
|
||||
if Logger == nil {
|
||||
Init("info", false)
|
||||
}
|
||||
return Logger
|
||||
}
|
||||
|
||||
// GetSugaredLogger returns the global sugared logger
|
||||
func GetSugaredLogger() *zap.SugaredLogger {
|
||||
if SugaredLogger == nil {
|
||||
Init("info", false)
|
||||
}
|
||||
return SugaredLogger
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"log"
|
||||
"github.com/labstack/echo/v5"
|
||||
"music-server/internal/backend"
|
||||
"music-server/internal/logging"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -14,27 +14,57 @@ func NewDownloadHandler() *DownloadHandler {
|
||||
return &DownloadHandler{}
|
||||
}
|
||||
|
||||
func (d *DownloadHandler) checkLatest(ctx echo.Context) error {
|
||||
log.Println("Checking latest version")
|
||||
// CheckLatest godoc
|
||||
// @Summary Check for latest version
|
||||
// @Description Checks for the latest version of the application
|
||||
// @Tags download
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string
|
||||
// @Router /download [get]
|
||||
func (d *DownloadHandler) checkLatest(ctx *echo.Context) error {
|
||||
logging.GetLogger().Info("Checking latest version")
|
||||
latest := backend.CheckLatest()
|
||||
return ctx.JSON(http.StatusOK, latest)
|
||||
}
|
||||
|
||||
func (d *DownloadHandler) listAssetsOfLatest(ctx echo.Context) error {
|
||||
log.Println("Listing assets")
|
||||
// ListAssetsOfLatest godoc
|
||||
// @Summary List assets of latest version
|
||||
// @Description Lists all assets available for the latest version
|
||||
// @Tags download
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} string
|
||||
// @Router /download/list [get]
|
||||
func (d *DownloadHandler) listAssetsOfLatest(ctx *echo.Context) error {
|
||||
logging.GetLogger().Info("Listing assets")
|
||||
assets := backend.ListAssetsOfLatest()
|
||||
return ctx.JSON(http.StatusOK, assets)
|
||||
}
|
||||
|
||||
func (d *DownloadHandler) downloadLatestWindows(ctx echo.Context) error {
|
||||
log.Println("Downloading latest windows")
|
||||
// DownloadLatestWindows godoc
|
||||
// @Summary Download latest Windows version
|
||||
// @Description Redirects to download the latest Windows version
|
||||
// @Tags download
|
||||
// @Produce octet-stream
|
||||
// @Success 302 {string} string
|
||||
// @Router /download/windows [get]
|
||||
func (d *DownloadHandler) downloadLatestWindows(ctx *echo.Context) error {
|
||||
logging.GetLogger().Info("Downloading latest windows")
|
||||
asset := backend.DownloadLatestWindows()
|
||||
ctx.Response().Header().Set("Content-Type", "application/octet-stream")
|
||||
return ctx.Redirect(http.StatusFound, asset)
|
||||
}
|
||||
|
||||
func (d *DownloadHandler) downloadLatestLinux(ctx echo.Context) error {
|
||||
log.Println("Downloading latest linux")
|
||||
// DownloadLatestLinux godoc
|
||||
// @Summary Download latest Linux version
|
||||
// @Description Redirects to download the latest Linux version
|
||||
// @Tags download
|
||||
// @Produce octet-stream
|
||||
// @Success 302 {string} string
|
||||
// @Router /download/linux [get]
|
||||
func (d *DownloadHandler) downloadLatestLinux(ctx *echo.Context) error {
|
||||
logging.GetLogger().Info("Downloading latest linux")
|
||||
asset := backend.DownloadLatestLinux()
|
||||
ctx.Response().Header().Set("Content-Type", "application/octet-stream")
|
||||
return ctx.Redirect(http.StatusFound, asset)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"music-server/internal/db"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
type IndexHandler struct {
|
||||
@@ -25,7 +25,7 @@ func NewIndexHandler() *IndexHandler {
|
||||
// @Success 200 {object} backend.VersionData
|
||||
// @Failure 404 {object} string
|
||||
// @Router /version [get]
|
||||
func (i *IndexHandler) GetVersion(ctx echo.Context) error {
|
||||
func (i *IndexHandler) GetVersion(ctx *echo.Context) error {
|
||||
versionHistory := backend.GetVersionHistory()
|
||||
if versionHistory.Version == "" {
|
||||
return ctx.JSON(http.StatusNotFound, "version not found")
|
||||
@@ -33,21 +33,54 @@ func (i *IndexHandler) GetVersion(ctx echo.Context) error {
|
||||
return ctx.JSON(http.StatusOK, versionHistory)
|
||||
}
|
||||
|
||||
func (i *IndexHandler) GetDBTest(ctx echo.Context) error {
|
||||
// GetDBTest godoc
|
||||
// @Summary Test database connection
|
||||
// @Description Tests the database connection
|
||||
// @Tags database
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string "TestedDB"
|
||||
// @Router /dbtest [get]
|
||||
func (i *IndexHandler) GetDBTest(ctx *echo.Context) error {
|
||||
backend.TestDB()
|
||||
return ctx.JSON(http.StatusOK, "TestedDB")
|
||||
}
|
||||
|
||||
func (i *IndexHandler) HealthCheck(ctx echo.Context) error {
|
||||
// HealthCheck godoc
|
||||
// @Summary Check server health
|
||||
// @Description Returns the health status of the server
|
||||
// @Tags health
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string "OK"
|
||||
// @Router /health [get]
|
||||
func (i *IndexHandler) HealthCheck(ctx *echo.Context) error {
|
||||
return ctx.JSON(http.StatusOK, db.Health())
|
||||
}
|
||||
|
||||
func (i *IndexHandler) GetCharacterList(ctx echo.Context) error {
|
||||
// GetCharacterList godoc
|
||||
// @Summary Get list of characters
|
||||
// @Description Returns a list of all available characters
|
||||
// @Tags characters
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} string
|
||||
// @Router /characters [get]
|
||||
func (i *IndexHandler) GetCharacterList(ctx *echo.Context) error {
|
||||
characters := backend.GetCharacterList()
|
||||
return ctx.JSON(http.StatusOK, characters)
|
||||
}
|
||||
|
||||
func (i *IndexHandler) GetCharacter(ctx echo.Context) error {
|
||||
// GetCharacter godoc
|
||||
// @Summary Get character image
|
||||
// @Description Returns the image for a specific character
|
||||
// @Tags characters
|
||||
// @Accept json
|
||||
// @Produce image/png
|
||||
// @Param name query string true "Character name"
|
||||
// @Success 200 {file} file
|
||||
// @Router /character [get]
|
||||
func (i *IndexHandler) GetCharacter(ctx *echo.Context) error {
|
||||
character := ctx.QueryParam("name")
|
||||
return ctx.File(backend.GetCharacter(character))
|
||||
}
|
||||
|
||||
+175
-39
@@ -1,13 +1,14 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"music-server/internal/backend"
|
||||
"music-server/internal/logging"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v5"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MusicHandler struct {
|
||||
@@ -17,9 +18,21 @@ func NewMusicHandler() *MusicHandler {
|
||||
return &MusicHandler{}
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetSong(ctx echo.Context) error {
|
||||
// GetSong godoc
|
||||
// @Summary Get a specific song
|
||||
// @Description Returns a specific song by name
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Produce audio/mpeg
|
||||
// @Param song query string true "Song name"
|
||||
// @Success 200 {file} file
|
||||
// @Failure 400 {string} string "song can't be empty"
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music [get]
|
||||
func (m *MusicHandler) GetSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
song := ctx.QueryParam("song")
|
||||
@@ -29,159 +42,282 @@ func (m *MusicHandler) GetSong(ctx echo.Context) error {
|
||||
songPath := backend.GetSong(song)
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetSoundCheckSong(ctx echo.Context) error {
|
||||
// GetSoundCheckSong godoc
|
||||
// @Summary Get sound check song
|
||||
// @Description Returns the sound check song
|
||||
// @Tags music
|
||||
// @Produce audio/mpeg
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/soundTest [get]
|
||||
func (m *MusicHandler) GetSoundCheckSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetSoundCheckSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) ResetMusic(ctx echo.Context) error {
|
||||
// ResetMusic godoc
|
||||
// @Summary Reset music state
|
||||
// @Description Resets the music state
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Success 204
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/reset [get]
|
||||
func (m *MusicHandler) ResetMusic(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
backend.Reset()
|
||||
return ctx.NoContent(http.StatusOK)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetRandomSong(ctx echo.Context) error {
|
||||
// GetRandomSong godoc
|
||||
// @Summary Get random song
|
||||
// @Description Returns a random song
|
||||
// @Tags music
|
||||
// @Produce audio/mpeg
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/rand [get]
|
||||
func (m *MusicHandler) GetRandomSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetRandomSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetRandomSongLowChance(ctx echo.Context) error {
|
||||
// GetRandomSongLowChance godoc
|
||||
// @Summary Get random song with low chance
|
||||
// @Description Returns a random song with low chance selection
|
||||
// @Tags music
|
||||
// @Produce audio/mpeg
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/rand/low [get]
|
||||
func (m *MusicHandler) GetRandomSongLowChance(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetRandomSongLowChance()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetRandomSongClassic(ctx echo.Context) error {
|
||||
// GetRandomSongClassic godoc
|
||||
// @Summary Get random classic song
|
||||
// @Description Returns a random song from the classic selection
|
||||
// @Tags music
|
||||
// @Produce audio/mpeg
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/rand/classic [get]
|
||||
func (m *MusicHandler) GetRandomSongClassic(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetRandomSongClassic()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetSongInfo(ctx echo.Context) error {
|
||||
// GetSongInfo godoc
|
||||
// @Summary Get current song info
|
||||
// @Description Returns information about the current song
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /music/info [get]
|
||||
func (m *MusicHandler) GetSongInfo(ctx *echo.Context) error {
|
||||
song := backend.GetSongInfo()
|
||||
return ctx.JSON(http.StatusOK, song)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetPlayedSongs(ctx echo.Context) error {
|
||||
// GetPlayedSongs godoc
|
||||
// @Summary Get played songs list
|
||||
// @Description Returns a list of played songs
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} map[string]interface{}
|
||||
// @Router /music/list [get]
|
||||
func (m *MusicHandler) GetPlayedSongs(ctx *echo.Context) error {
|
||||
songList := backend.GetPlayedSongs()
|
||||
return ctx.JSON(http.StatusOK, songList)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetNextSong(ctx echo.Context) error {
|
||||
// GetNextSong godoc
|
||||
// @Summary Get next song
|
||||
// @Description Returns the next song in the queue
|
||||
// @Tags music
|
||||
// @Produce audio/mpeg
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/next [get]
|
||||
func (m *MusicHandler) GetNextSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetNextSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetPreviousSong(ctx echo.Context) error {
|
||||
// GetPreviousSong godoc
|
||||
// @Summary Get previous song
|
||||
// @Description Returns the previous song in the queue
|
||||
// @Tags music
|
||||
// @Produce audio/mpeg
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {string} string "Not Found"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/previous [get]
|
||||
func (m *MusicHandler) GetPreviousSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetPreviousSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetAllGames(ctx echo.Context) error {
|
||||
// GetAllGames godoc
|
||||
// @Summary Get all games
|
||||
// @Description Returns a list of all games in order
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} map[string]interface{}
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/all/order [get]
|
||||
func (m *MusicHandler) GetAllGames(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
gameList := backend.GetAllGames()
|
||||
return ctx.JSON(http.StatusOK, gameList)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) GetAllGamesRandom(ctx echo.Context) error {
|
||||
// GetAllGamesRandom godoc
|
||||
// @Summary Get all games random
|
||||
// @Description Returns a list of all games in random order
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} map[string]interface{}
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/all/random [get]
|
||||
func (m *MusicHandler) GetAllGamesRandom(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
gameList := backend.GetAllGamesRandom()
|
||||
return ctx.JSON(http.StatusOK, gameList)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) PutPlayed(ctx echo.Context) error {
|
||||
// PutPlayed godoc
|
||||
// @Summary Mark song as played
|
||||
// @Description Marks a song as played by its ID
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param song query int true "Song ID"
|
||||
// @Success 204
|
||||
// @Failure 400 {string} string "Bad Request"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/played [put]
|
||||
func (m *MusicHandler) PutPlayed(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
song, err := strconv.Atoi(ctx.QueryParam("song"))
|
||||
if err != nil {
|
||||
return ctx.JSON(http.StatusBadRequest, err)
|
||||
return ctx.JSON(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
log.Println("song", song)
|
||||
logging.GetLogger().Info("Marking song as played", zap.Int("song_id", song))
|
||||
backend.SetPlayed(song)
|
||||
return ctx.NoContent(http.StatusOK)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) AddLatestToQue(ctx echo.Context) error {
|
||||
// AddLatestToQue godoc
|
||||
// @Summary Add latest to queue
|
||||
// @Description Adds the latest song to the queue
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Success 204
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/addQue [get]
|
||||
func (m *MusicHandler) AddLatestToQue(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
backend.AddLatestToQue()
|
||||
return ctx.NoContent(http.StatusOK)
|
||||
}
|
||||
|
||||
func (m *MusicHandler) AddLatestPlayed(ctx echo.Context) error {
|
||||
// AddLatestPlayed godoc
|
||||
// @Summary Add latest to played
|
||||
// @Description Adds the latest song to the played list
|
||||
// @Tags music
|
||||
// @Accept json
|
||||
// @Success 204
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/addPlayed [get]
|
||||
func (m *MusicHandler) AddLatestPlayed(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
backend.AddLatestPlayed()
|
||||
|
||||
+21
-23
@@ -7,30 +7,35 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
_ "music-server/cmd/docs"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/swaggo/echo-swagger" // echo-swagger middleware
|
||||
//_ "github.com/swaggo/echo-swagger/example/docs" // docs is generated by Swag CLI, you have to import it.
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/middleware"
|
||||
echoSwagger "github.com/swaggo/echo-swagger/v2"
|
||||
"music-server/internal/logging"
|
||||
)
|
||||
|
||||
// @Title Swagger Example API
|
||||
// @version 0.5
|
||||
// @description This is a sample server Petstore server.
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
// @Title MusicServer API
|
||||
// @version 1.0
|
||||
// @description API for the MusicServer application
|
||||
// @termsOfService http://sanplex.xyz/terms/
|
||||
|
||||
// @contact.name Sebastian Olsson
|
||||
// @contact.email zarnor91@gmail.com
|
||||
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
// @license.name MIT
|
||||
// @license.url http://opensource.org/licenses/MIT
|
||||
|
||||
// @host localhost:8080
|
||||
// @BasePath /
|
||||
func (s *Server) RegisterRoutes() http.Handler {
|
||||
e := echo.New()
|
||||
e.Use(middleware.Logger())
|
||||
|
||||
// Serve OpenAPI spec at /openapi
|
||||
e.GET("/openapi", echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
http.ServeFile(w, r, "cmd/docs/swagger.json")
|
||||
})))
|
||||
e.Use(logging.RequestLogger())
|
||||
e.Use(middleware.Recover())
|
||||
|
||||
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||
@@ -49,14 +54,7 @@ func (s *Server) RegisterRoutes() http.Handler {
|
||||
|
||||
e.Static("/", "/frontend")
|
||||
|
||||
/*swagger := http.FileServer(http.FS(web.Swagger))
|
||||
e.GET("/swagger/*", echo.WrapHandler(swagger))*/
|
||||
|
||||
swaggerRedirect := func(c echo.Context) error {
|
||||
return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html")
|
||||
}
|
||||
e.GET("/swagger", swaggerRedirect)
|
||||
e.GET("/swagger/", swaggerRedirect)
|
||||
// Swagger UI
|
||||
e.GET("/swagger/*", echoSwagger.WrapHandler)
|
||||
|
||||
index := NewIndexHandler()
|
||||
@@ -101,13 +99,13 @@ func (s *Server) RegisterRoutes() http.Handler {
|
||||
musicGroup.GET("/addQue", music.AddLatestToQue)
|
||||
musicGroup.GET("/addPlayed", music.AddLatestPlayed)
|
||||
|
||||
routes := e.Routes()
|
||||
routes := e.Router().Routes()
|
||||
sort.Slice(routes, func(i, j int) bool {
|
||||
return routes[i].Path < routes[j].Path
|
||||
})
|
||||
for _, r := range routes {
|
||||
if (r.Method == "GET" || r.Method == "POST" || r.Method == "PUT" || r.Method == "DELETE") && !strings.Contains(r.Name, "github") {
|
||||
fmt.Printf(" %s %s\n", r.Method, r.Path)
|
||||
fmt.Printf(" %s\t\t%s\n", r.Method, r.Path)
|
||||
}
|
||||
}
|
||||
return e
|
||||
|
||||
@@ -2,12 +2,15 @@ package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"music-server/internal/db"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"music-server/internal/db"
|
||||
"music-server/internal/logging"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
@@ -22,24 +25,39 @@ var (
|
||||
password = os.Getenv("DB_PASSWORD")
|
||||
musicPath = os.Getenv("MUSIC_PATH")
|
||||
charactersPath = os.Getenv("CHARACTERS_PATH")
|
||||
logLevel = os.Getenv("LOG_LEVEL")
|
||||
logJSON = os.Getenv("LOG_JSON") == "true"
|
||||
)
|
||||
|
||||
func NewServer() *http.Server {
|
||||
// Initialize logger
|
||||
if logLevel == "" {
|
||||
logLevel = "info"
|
||||
}
|
||||
logging.Init(logLevel, logJSON)
|
||||
|
||||
logger := logging.GetLogger()
|
||||
|
||||
port, _ := strconv.Atoi(os.Getenv("PORT"))
|
||||
NewServer := &Server{
|
||||
port: port,
|
||||
}
|
||||
|
||||
fmt.Printf("host: %s, dbPort: %v, username: %s, password: %s, dbName: %s\n",
|
||||
host, dbPort, username, password, dbName)
|
||||
logger.Info("Starting server",
|
||||
zap.String("host", host),
|
||||
zap.String("dbPort", dbPort),
|
||||
zap.String("username", username),
|
||||
zap.String("dbName", dbName),
|
||||
)
|
||||
|
||||
log.Printf("musicPath: %s\n", musicPath)
|
||||
log.Printf("charactersPath: %s\n", charactersPath)
|
||||
logger.Info("Paths",
|
||||
zap.String("musicPath", musicPath),
|
||||
zap.String("charactersPath", charactersPath),
|
||||
)
|
||||
|
||||
//conf.SetupDb()
|
||||
if host == "" || dbPort == "" || username == "" || password == "" || dbName == "" || musicPath == "" || charactersPath == "" {
|
||||
log.Fatal("Invalid settings")
|
||||
logging.GetLogger().Fatal("Invalid settings - missing required environment variables")
|
||||
}
|
||||
|
||||
db.Migrate_db(host, dbPort, username, password, dbName)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"music-server/internal/backend"
|
||||
"music-server/internal/logging"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
type SyncHandler struct {
|
||||
@@ -15,42 +15,78 @@ func NewSyncHandler() *SyncHandler {
|
||||
return &SyncHandler{}
|
||||
}
|
||||
|
||||
func (s *SyncHandler) SyncProgress(ctx echo.Context) error {
|
||||
// SyncProgress godoc
|
||||
// @Summary Get sync progress
|
||||
// @Description Returns the current sync progress or result
|
||||
// @Tags sync
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /sync/progress [get]
|
||||
func (s *SyncHandler) SyncProgress(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Getting progress")
|
||||
logging.GetLogger().Info("Getting sync progress")
|
||||
response := backend.SyncProgress()
|
||||
return ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
log.Println("Getting result")
|
||||
logging.GetLogger().Info("Getting sync result")
|
||||
response := backend.SyncResult()
|
||||
return ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func (s *SyncHandler) SyncGamesNewOnlyChanges(ctx echo.Context) error {
|
||||
// SyncGamesNewOnlyChanges godoc
|
||||
// @Summary Sync games with only changes
|
||||
// @Description Starts syncing games with only new changes
|
||||
// @Tags sync
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string "Start syncing games"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /sync [get]
|
||||
func (s *SyncHandler) SyncGamesNewOnlyChanges(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Warn("Syncing is already in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
log.Println("Start syncing games")
|
||||
logging.GetLogger().Info("Starting sync with only changes")
|
||||
go backend.SyncGamesNewOnlyChanges()
|
||||
return ctx.JSON(http.StatusOK, "Start syncing games")
|
||||
}
|
||||
|
||||
func (s *SyncHandler) SyncGamesNewFull(ctx echo.Context) error {
|
||||
// SyncGamesNewFull godoc
|
||||
// @Summary Sync all games fully
|
||||
// @Description Starts a full sync of all games
|
||||
// @Tags sync
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string "Start syncing games full"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /sync/full [get]
|
||||
func (s *SyncHandler) SyncGamesNewFull(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Warn("Syncing is already in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
log.Println("Start syncing games full")
|
||||
logging.GetLogger().Info("Starting full sync")
|
||||
go backend.SyncGamesNewFull()
|
||||
return ctx.JSON(http.StatusOK, "Start syncing games full")
|
||||
}
|
||||
|
||||
func (s *SyncHandler) ResetGames(ctx echo.Context) error {
|
||||
// ResetGames godoc
|
||||
// @Summary Reset games database
|
||||
// @Description Resets the games database by deleting all games and songs
|
||||
// @Tags sync
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string "Games and songs are deleted from the database"
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /sync/reset [get]
|
||||
func (s *SyncHandler) ResetGames(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
log.Println("Syncing is in progress")
|
||||
logging.GetLogger().Warn("Cannot reset - syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
logging.GetLogger().Info("Resetting games database")
|
||||
backend.ResetDB()
|
||||
return ctx.JSON(http.StatusOK, "Games and songs are deleted from the database")
|
||||
}
|
||||
|
||||
@@ -41,10 +41,39 @@ sqlc-generate:
|
||||
migrate-create name:
|
||||
@migrate create -ext sql -dir internal/db/migrations -seq {{name}}
|
||||
|
||||
swag-install:
|
||||
@if ! command -v swag > /dev/null; then \
|
||||
read -p "Swag is not installed on your machine. Do you want to install it? [Y/n] " choice; \
|
||||
if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \
|
||||
go install github.com/swaggo/swag/cmd/swag@latest; \
|
||||
if [ ! -x "$$(command -v swag)" ]; then \
|
||||
echo "swag installation failed. Exiting..."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
else \
|
||||
echo "You chose not to install swag. Exiting..."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi
|
||||
|
||||
swag-generate: swag-install
|
||||
@echo "Generating OpenAPI docs..."
|
||||
@swag init -g internal/server/routes.go -o cmd/docs
|
||||
|
||||
frontend-install:
|
||||
@if ! command -v npm > /dev/null; then \
|
||||
echo "npm is not installed on your machine. Please install Node.js first."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@cd cmd/frontend && npm install
|
||||
|
||||
frontend-build: frontend-install
|
||||
@echo "Building frontend..."
|
||||
@cd cmd/frontend && npm run build
|
||||
|
||||
[no-cd]
|
||||
build: sqlc-generate templ-build tailwind-build
|
||||
build: sqlc-generate templ-build swag-generate
|
||||
@echo "Building..."
|
||||
@swag init -g routes.go -d ./internal/server/,./internal/backend/ -o ./cmd/docs
|
||||
@go build -o main cmd/main.go
|
||||
|
||||
run:
|
||||
|
||||
Reference in New Issue
Block a user