mirror of
https://github.com/zoriya/Kyoo.git
synced 2026-03-28 12:27:51 -04:00
Transcoder: Pick Audio Quality (#1353)
This commit is contained in:
commit
7673416cf9
@ -42,6 +42,26 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/info": {
|
||||
"get": {
|
||||
"description": "List keibi's settings (oidc providers, public url...)",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "Auth info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.ServerInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/jwt": {
|
||||
"get": {
|
||||
"security": [
|
||||
@ -199,7 +219,252 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/callback/{provider}": {
|
||||
"get": {
|
||||
"description": "Exchange an opaque OIDC token for a local session.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Opaque token returned by /oidc/logged/:provider",
|
||||
"name": "token",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Optional tenant passthrough for federated setups",
|
||||
"name": "tenant",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Bearer token to link provider to current account",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.SessionWToken"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"410": {
|
||||
"description": "Login token expired or already used",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/logged/{provider}": {
|
||||
"get": {
|
||||
"description": "Callback endpoint called by OIDC providers after login.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC logged callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "State value returned by the provider",
|
||||
"name": "state",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Authorization code",
|
||||
"name": "code",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Provider callback error",
|
||||
"name": "error",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid state",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/login/{provider}": {
|
||||
"get": {
|
||||
"description": "Start an OIDC login with a provider.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC login",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "URL to redirect the browser to after provider callback",
|
||||
"name": "redirectUrl",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Optional tenant passthrough for federated setups",
|
||||
"name": "tenant",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found"
|
||||
},
|
||||
"400": {
|
||||
"description": "Missing redirectUrl",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Remove an OIDC provider from the current account.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC unlink provider",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "List all active sessions for the currently connected user",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "List my sessions",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/main.SessionWCurrent"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Login to your account and open a session",
|
||||
"consumes": [
|
||||
@ -374,7 +639,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Page-main_User"
|
||||
"$ref": "#/definitions/main.Page-models_User"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
@ -410,7 +675,7 @@ const docTemplate = `{
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.RegisterDto"
|
||||
"$ref": "#/definitions/models.RegisterDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -421,6 +686,12 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/main.SessionWToken"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Registrations are disabled",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Duplicated email or username",
|
||||
"schema": {
|
||||
@ -455,7 +726,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
@ -493,7 +764,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -521,7 +792,7 @@ const docTemplate = `{
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.EditUserDto"
|
||||
"$ref": "#/definitions/models.EditUserDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -529,7 +800,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
@ -547,6 +818,137 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me/logo": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Get the current user's logo (manual upload if available, gravatar otherwise)",
|
||||
"produces": [
|
||||
"image/*"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get my logo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No gravatar image found for this user",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Upload a manual profile picture for the current user",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Upload my logo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "file",
|
||||
"description": "Profile picture image (jpeg/png/gif/webp, max 5MB)",
|
||||
"name": "logo",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"413": {
|
||||
"description": "File too large",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Missing or invalid logo file",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete the current user's manually uploaded profile picture",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete my logo",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me/password": {
|
||||
"patch": {
|
||||
"security": [
|
||||
@ -578,7 +980,7 @@ const docTemplate = `{
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.EditPasswordDto"
|
||||
"$ref": "#/definitions/models.EditPasswordDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -595,6 +997,49 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me/{id}": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete the user's manually uploaded profile picture",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete user logo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The id or username of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
@ -626,7 +1071,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -675,7 +1120,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -724,7 +1169,7 @@ const docTemplate = `{
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.EditUserDto"
|
||||
"$ref": "#/definitions/models.EditUserDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -732,7 +1177,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
@ -749,6 +1194,104 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}/logo": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.read"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Get a user's logo (manual upload if available, gravatar otherwise)",
|
||||
"produces": [
|
||||
"image/*"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get user logo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The id or username of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No gravatar image found for this user",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}/sessions": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "List all active sessions for a user. Listing someone else's sessions requires users.read.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "List user sessions",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397",
|
||||
"description": "The id or username of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/main.Session"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Missing permissions: users.read.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No user found with id or username",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@ -834,40 +1377,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.EditPasswordDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.EditUserDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": {
|
||||
"preferOriginal": " true"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.JwkSet": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -949,24 +1458,14 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.OidcHandle": {
|
||||
"main.OidcInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this oidc handle.",
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
|
||||
"logo": {
|
||||
"type": "string"
|
||||
},
|
||||
"profileUrl": {
|
||||
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"example": "https://myanimelist.net/profile/zoriya"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user on the external service.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -989,13 +1488,13 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Page-main_User": {
|
||||
"main.Page-models_User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"next": {
|
||||
@ -1008,29 +1507,20 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.RegisterDto": {
|
||||
"main.ServerInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Valid email that could be used for forgotten password requests. Can be used for login.",
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
"allowRegister": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password to use.",
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
"oidc": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/main.OidcInfo"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the new account, can't contain @ signs. Can be used for login.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
"publicUrl": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1059,6 +1549,34 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.SessionWCurrent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdDate": {
|
||||
"description": "When was the session first opened",
|
||||
"type": "string",
|
||||
"example": "2025-03-29T18:20:05.267Z"
|
||||
},
|
||||
"current": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device": {
|
||||
"description": "Device that created the session.",
|
||||
"type": "string",
|
||||
"example": "Web - Firefox"
|
||||
},
|
||||
"id": {
|
||||
"description": "Unique id of this session. Can be used for calls to DELETE",
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
|
||||
},
|
||||
"lastUsed": {
|
||||
"description": "Last date this session was used to access a service.",
|
||||
"type": "string",
|
||||
"example": "2025-03-29T18:20:05.267Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.SessionWToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -1088,7 +1606,92 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.User": {
|
||||
"models.EditPasswordDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"newPassword"
|
||||
],
|
||||
"properties": {
|
||||
"newPassword": {
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
},
|
||||
"oldPassword": {
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.EditUserDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": {
|
||||
"preferOriginal": " true"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.OidcHandle": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this oidc handle.",
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
|
||||
},
|
||||
"profileUrl": {
|
||||
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"example": "https://myanimelist.net/profile/zoriya"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user on the external service.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegisterDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Valid email that could be used for forgotten password requests. Can be used for login.",
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password to use.",
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the new account, can't contain @ signs. Can be used for login.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
@ -1112,6 +1715,10 @@ const docTemplate = `{
|
||||
"format": "email",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"hasPassword": {
|
||||
"description": "False if the user has never setup a password and only used oidc.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"description": "Id of the user.",
|
||||
"type": "string",
|
||||
@ -1126,7 +1733,7 @@ const docTemplate = `{
|
||||
"description": "List of other login method available for this user. Access tokens wont be returned here.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/main.OidcHandle"
|
||||
"$ref": "#/definitions/models.OidcHandle"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
|
||||
@ -36,6 +36,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/info": {
|
||||
"get": {
|
||||
"description": "List keibi's settings (oidc providers, public url...)",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "Auth info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.ServerInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/jwt": {
|
||||
"get": {
|
||||
"security": [
|
||||
@ -193,7 +213,252 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/callback/{provider}": {
|
||||
"get": {
|
||||
"description": "Exchange an opaque OIDC token for a local session.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Opaque token returned by /oidc/logged/:provider",
|
||||
"name": "token",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Optional tenant passthrough for federated setups",
|
||||
"name": "tenant",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Bearer token to link provider to current account",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.SessionWToken"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"410": {
|
||||
"description": "Login token expired or already used",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/logged/{provider}": {
|
||||
"get": {
|
||||
"description": "Callback endpoint called by OIDC providers after login.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC logged callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "State value returned by the provider",
|
||||
"name": "state",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Authorization code",
|
||||
"name": "code",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Provider callback error",
|
||||
"name": "error",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid state",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/login/{provider}": {
|
||||
"get": {
|
||||
"description": "Start an OIDC login with a provider.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC login",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "URL to redirect the browser to after provider callback",
|
||||
"name": "redirectUrl",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Optional tenant passthrough for federated setups",
|
||||
"name": "tenant",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found"
|
||||
},
|
||||
"400": {
|
||||
"description": "Missing redirectUrl",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Remove an OIDC provider from the current account.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"oidc"
|
||||
],
|
||||
"summary": "OIDC unlink provider",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "google",
|
||||
"description": "OIDC provider id",
|
||||
"name": "provider",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown OIDC provider",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "List all active sessions for the currently connected user",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "List my sessions",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/main.SessionWCurrent"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Login to your account and open a session",
|
||||
"consumes": [
|
||||
@ -368,7 +633,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Page-main_User"
|
||||
"$ref": "#/definitions/main.Page-models_User"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
@ -404,7 +669,7 @@
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.RegisterDto"
|
||||
"$ref": "#/definitions/models.RegisterDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -415,6 +680,12 @@
|
||||
"$ref": "#/definitions/main.SessionWToken"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Registrations are disabled",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Duplicated email or username",
|
||||
"schema": {
|
||||
@ -449,7 +720,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
@ -487,7 +758,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -515,7 +786,7 @@
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.EditUserDto"
|
||||
"$ref": "#/definitions/models.EditUserDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -523,7 +794,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
@ -541,6 +812,137 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me/logo": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Get the current user's logo (manual upload if available, gravatar otherwise)",
|
||||
"produces": [
|
||||
"image/*"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get my logo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No gravatar image found for this user",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Upload a manual profile picture for the current user",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Upload my logo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "file",
|
||||
"description": "Profile picture image (jpeg/png/gif/webp, max 5MB)",
|
||||
"name": "logo",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"413": {
|
||||
"description": "File too large",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Missing or invalid logo file",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete the current user's manually uploaded profile picture",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete my logo",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me/password": {
|
||||
"patch": {
|
||||
"security": [
|
||||
@ -572,7 +974,7 @@
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.EditPasswordDto"
|
||||
"$ref": "#/definitions/models.EditPasswordDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -589,6 +991,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me/{id}": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete the user's manually uploaded profile picture",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete user logo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The id or username of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
@ -620,7 +1065,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -669,7 +1114,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -718,7 +1163,7 @@
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.EditUserDto"
|
||||
"$ref": "#/definitions/models.EditUserDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -726,7 +1171,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
@ -743,6 +1188,104 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}/logo": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.read"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Get a user's logo (manual upload if available, gravatar otherwise)",
|
||||
"produces": [
|
||||
"image/*"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get user logo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The id or username of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No gravatar image found for this user",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}/sessions": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "List all active sessions for a user. Listing someone else's sessions requires users.read.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "List user sessions",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397",
|
||||
"description": "The id or username of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/main.Session"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Missing permissions: users.read.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No user found with id or username",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.KError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@ -828,40 +1371,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.EditPasswordDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.EditUserDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": {
|
||||
"preferOriginal": " true"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.JwkSet": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -943,24 +1452,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.OidcHandle": {
|
||||
"main.OidcInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this oidc handle.",
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
|
||||
"logo": {
|
||||
"type": "string"
|
||||
},
|
||||
"profileUrl": {
|
||||
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"example": "https://myanimelist.net/profile/zoriya"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user on the external service.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -983,13 +1482,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Page-main_User": {
|
||||
"main.Page-models_User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
"$ref": "#/definitions/models.User"
|
||||
}
|
||||
},
|
||||
"next": {
|
||||
@ -1002,29 +1501,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.RegisterDto": {
|
||||
"main.ServerInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Valid email that could be used for forgotten password requests. Can be used for login.",
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
"allowRegister": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password to use.",
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
"oidc": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/main.OidcInfo"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the new account, can't contain @ signs. Can be used for login.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
"publicUrl": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1053,6 +1543,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.SessionWCurrent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdDate": {
|
||||
"description": "When was the session first opened",
|
||||
"type": "string",
|
||||
"example": "2025-03-29T18:20:05.267Z"
|
||||
},
|
||||
"current": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device": {
|
||||
"description": "Device that created the session.",
|
||||
"type": "string",
|
||||
"example": "Web - Firefox"
|
||||
},
|
||||
"id": {
|
||||
"description": "Unique id of this session. Can be used for calls to DELETE",
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
|
||||
},
|
||||
"lastUsed": {
|
||||
"description": "Last date this session was used to access a service.",
|
||||
"type": "string",
|
||||
"example": "2025-03-29T18:20:05.267Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.SessionWToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -1082,7 +1600,92 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.User": {
|
||||
"models.EditPasswordDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"newPassword"
|
||||
],
|
||||
"properties": {
|
||||
"newPassword": {
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
},
|
||||
"oldPassword": {
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.EditUserDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": {
|
||||
"preferOriginal": " true"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.OidcHandle": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this oidc handle.",
|
||||
"type": "string",
|
||||
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
|
||||
},
|
||||
"profileUrl": {
|
||||
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"example": "https://myanimelist.net/profile/zoriya"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user on the external service.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegisterDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Valid email that could be used for forgotten password requests. Can be used for login.",
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password to use.",
|
||||
"type": "string",
|
||||
"example": "password1234"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the new account, can't contain @ signs. Can be used for login.",
|
||||
"type": "string",
|
||||
"example": "zoriya"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
@ -1106,6 +1709,10 @@
|
||||
"format": "email",
|
||||
"example": "kyoo@zoriya.dev"
|
||||
},
|
||||
"hasPassword": {
|
||||
"description": "False if the user has never setup a password and only used oidc.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"description": "Id of the user.",
|
||||
"type": "string",
|
||||
@ -1120,7 +1727,7 @@
|
||||
"description": "List of other login method available for this user. Access tokens wont be returned here.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/main.OidcHandle"
|
||||
"$ref": "#/definitions/models.OidcHandle"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
|
||||
1164
auth/docs/swagger.yaml
Normal file
1164
auth/docs/swagger.yaml
Normal file
File diff suppressed because it is too large
Load Diff
67
auth/go.mod
67
auth/go.mod
@ -7,15 +7,16 @@ require (
|
||||
github.com/exaring/otelpgx v0.10.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgx/v5 v5.8.0
|
||||
github.com/jackc/pgx/v5 v5.9.1
|
||||
github.com/labstack/echo-jwt/v5 v5.0.1
|
||||
github.com/labstack/echo-opentelemetry v0.0.2
|
||||
github.com/labstack/echo/v5 v5.0.4
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.13
|
||||
github.com/swaggo/echo-swagger v1.5.0
|
||||
github.com/mileusna/useragent v1.3.5
|
||||
github.com/swaggo/echo-swagger/v2 v2.0.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.17.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
|
||||
go.opentelemetry.io/otel v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.18.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.18.0
|
||||
@ -31,47 +32,47 @@ require (
|
||||
require (
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.3 // indirect
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.4 // indirect
|
||||
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/segmentio/asm v1.2.1 // indirect
|
||||
github.com/sv-tools/openapi v0.2.1 // indirect
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect
|
||||
github.com/sv-tools/openapi v0.4.0 // indirect
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.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.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/spec v0.22.3 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/spec v0.22.4 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.1
|
||||
@ -81,17 +82,15 @@ require (
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mileusna/useragent v1.3.5
|
||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
)
|
||||
|
||||
141
auth/go.sum
141
auth/go.sum
@ -15,11 +15,10 @@ github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
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=
|
||||
@ -34,40 +33,40 @@ github.com/exaring/otelpgx v0.10.0 h1:NGGegdoBQM3jNZDKG8ENhigUcgBN7d7943L0YlcIpZ
|
||||
github.com/exaring/otelpgx v0.10.0/go.mod h1:R5/M5LWsPPBZc1SrRE5e0DiU48bI78C1/GPTWs6I66U=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
|
||||
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
|
||||
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
|
||||
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.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
|
||||
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
|
||||
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
|
||||
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
@ -76,8 +75,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
@ -98,8 +97,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
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.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
|
||||
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
|
||||
github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc=
|
||||
github.com/jackc/pgx/v5 v5.9.1/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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@ -122,8 +121,8 @@ github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.3 h1:WjLHWkDkgWXeIUrKi/7lS/sGq2DjkSAwdTbH5RHXAKs=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.3/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.4 h1:pXyH2ppK8GYYggygxJ3TvxpCZnbEUWc9qSwRTTApaLA=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.4/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk=
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU=
|
||||
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
|
||||
@ -157,16 +156,16 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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.2.1 h1:ES1tMQMJFGibWndMagvdoo34T1Vllxr1Nlm5wz6b1aA=
|
||||
github.com/sv-tools/openapi v0.2.1/go.mod h1:k5VuZamTw1HuiS9p2Wl5YIDWzYnHG6/FgPOSFXLAhGg=
|
||||
github.com/swaggo/echo-swagger v1.5.0 h1:nkHxOaBy0SkbJMtMeXZC64KHSa0mJdZFQhVqwEcMres=
|
||||
github.com/swaggo/echo-swagger v1.5.0/go.mod h1:TzO363X1ZG/MSbjrG2IX6m65Yd3/zpqh5KM6lPctAhk=
|
||||
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/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5 h1:fK7d6ET9rrEsdB8IyuwXREWMcyQN3N7gawGFbbrjgHk=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5/go.mod h1:kCL8Fu4Zl8d5tB2Bgj96b8wRowwrwk175bZHXfuGVFI=
|
||||
github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
|
||||
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
@ -174,8 +173,8 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.17.0 h1:NFIS6x7wyObQ7cR84x7bt1sr8nYBx89s3x3GwRjw40k=
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.17.0/go.mod h1:39SaByOyDMRMe872AE7uelMuQZidIw7LLFAnQi0FWTE=
|
||||
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/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.40.0 h1:xariChe8OOVF3rNlfzGFgQc61npQmXhzZj/i82mxMfg=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.40.0/go.mod h1:72WvbdxbOfXaELEQfonFfOL6osvcVjI7uJEE8C2nkrs=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
@ -208,33 +207,35 @@ go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -243,8 +244,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -256,23 +257,23 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
@ -280,10 +281,8 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@ -25,7 +25,7 @@ import (
|
||||
echojwt "github.com/labstack/echo-jwt/v5"
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/middleware"
|
||||
echoSwagger "github.com/swaggo/echo-swagger"
|
||||
echoSwagger "github.com/swaggo/echo-swagger/v2"
|
||||
|
||||
"github.com/exaring/otelpgx"
|
||||
)
|
||||
|
||||
@ -544,7 +544,7 @@ type OidcInfo struct {
|
||||
// @Description List keibi's settings (oidc providers, public url...)
|
||||
// @Tags oidc
|
||||
// @Produce json
|
||||
// @Success 200 ServerInfo
|
||||
// @Success 200 {object} ServerInfo
|
||||
// @Router /info [get]
|
||||
func (h *Handler) Info(c *echo.Context) error {
|
||||
ret := ServerInfo{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { Role } from "~/models";
|
||||
import { Container, H2, Image, P, Poster, Skeleton, SubP } from "~/primitives";
|
||||
import { Container, H2, P, Poster, Skeleton, SubP } from "~/primitives";
|
||||
import { InfiniteGrid, type QueryIdentifier } from "~/query";
|
||||
import { EmptyView } from "../empty-view";
|
||||
|
||||
|
||||
@ -176,6 +176,7 @@ export const QualityMenu = ({
|
||||
}}
|
||||
/>
|
||||
{lvls
|
||||
.reverse()
|
||||
.map((x) => (
|
||||
<Menu.Item
|
||||
key={x.id}
|
||||
|
||||
@ -35,10 +35,7 @@ Projects using gocoder:
|
||||
I did a blog post explaining the core idea, you can find it at [zoriya.dev/blogs/transcoder](https://zoriya.dev/blogs/transcoder).
|
||||
|
||||
## TODO:
|
||||
- Add a swagger
|
||||
- Add configurable JWT authorization (v5 of kyoo)
|
||||
- Add credits/recaps/intro/preview detection
|
||||
- Add multiples qualities for audio streams
|
||||
- Improve multi-video support
|
||||
- Add optional redis synchronization for replication (`RunLock` was made with this in mind)
|
||||
- fmp4 support
|
||||
|
||||
@ -20,9 +20,9 @@ func RegisterStreamHandlers(e *echo.Group, transcoder *src.Transcoder) {
|
||||
e.GET("/:path/direct/:identifier", DirectStream)
|
||||
e.GET("/:path/master.m3u8", h.GetMaster)
|
||||
e.GET("/:path/:video/:quality/index.m3u8", h.GetVideoIndex)
|
||||
e.GET("/:path/audio/:audio/index.m3u8", h.GetAudioIndex)
|
||||
e.GET("/:path/audio/:audio/:quality/index.m3u8", h.GetAudioIndex)
|
||||
e.GET("/:path/:video/:quality/:chunk", h.GetVideoSegment)
|
||||
e.GET("/:path/audio/:audio/:chunk", h.GetAudioSegment)
|
||||
e.GET("/:path/audio/:audio/:quality/:chunk", h.GetAudioSegment)
|
||||
}
|
||||
|
||||
// @Summary Direct video
|
||||
@ -88,7 +88,7 @@ func (h *shandler) GetVideoIndex(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
quality, err := src.QualityFromString(c.Param("quality"))
|
||||
quality, err := src.VideoQualityFromString(c.Param("quality"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -115,7 +115,7 @@ func (h *shandler) GetVideoIndex(c echo.Context) error {
|
||||
// This route can take a few seconds to respond since it will way for at least one segment to be
|
||||
// available.
|
||||
//
|
||||
// Path: /:path/audio/:audio/index.m3u8
|
||||
// Path: /:path/audio/:audio/:quality/index.m3u8
|
||||
//
|
||||
// PRIVATE ROUTE (not documented in swagger, can change at any time)
|
||||
// Only reached via the master.m3u8.
|
||||
@ -124,6 +124,10 @@ func (h *shandler) GetAudioIndex(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
quality, err := src.AudioQualityFromString(c.Param("quality"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := getClientId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -133,7 +137,7 @@ func (h *shandler) GetAudioIndex(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, err := h.transcoder.GetAudioIndex(c.Request().Context(), path, uint32(audio), client, sha)
|
||||
ret, err := h.transcoder.GetAudioIndex(c.Request().Context(), path, uint32(audio), quality, client, sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -154,7 +158,7 @@ func (h *shandler) GetVideoSegment(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
quality, err := src.QualityFromString(c.Param("quality"))
|
||||
quality, err := src.VideoQualityFromString(c.Param("quality"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -190,7 +194,7 @@ func (h *shandler) GetVideoSegment(c echo.Context) error {
|
||||
//
|
||||
// Retrieve a chunk of a transcoded audio.
|
||||
//
|
||||
// Path: /:path/audio/:audio/segments-:chunk.ts
|
||||
// Path: /:path/audio/:audio/:quality/segments-:chunk.ts
|
||||
//
|
||||
// PRIVATE ROUTE (not documented in swagger, can change at any time)
|
||||
// Only reached via the master.m3u8.
|
||||
@ -199,6 +203,10 @@ func (h *shandler) GetAudioSegment(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
quality, err := src.AudioQualityFromString(c.Param("quality"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
segment, err := parseSegment(c.Param("chunk"))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -212,7 +220,7 @@ func (h *shandler) GetAudioSegment(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, err := h.transcoder.GetAudioSegment(c.Request().Context(), path, uint32(audio), segment, client, sha)
|
||||
ret, err := h.transcoder.GetAudioSegment(c.Request().Context(), path, uint32(audio), quality, segment, client, sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
60
transcoder/src/audioquality.go
Normal file
60
transcoder/src/audioquality.go
Normal file
@ -0,0 +1,60 @@
|
||||
package src
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type AudioQuality string
|
||||
|
||||
const (
|
||||
K128 AudioQuality = "128k"
|
||||
K192 AudioQuality = "192k"
|
||||
K256 AudioQuality = "256k"
|
||||
K320 AudioQuality = "320k"
|
||||
K512 AudioQuality = "512k"
|
||||
AOriginal AudioQuality = "original"
|
||||
)
|
||||
|
||||
var AudioQualities = []AudioQuality{K128, K192, K256, K320, K512}
|
||||
|
||||
func AudioQualityFromString(str string) (AudioQuality, error) {
|
||||
if str == string(AOriginal) {
|
||||
return AOriginal, nil
|
||||
}
|
||||
|
||||
for _, quality := range AudioQualities {
|
||||
if string(quality) == str {
|
||||
return quality, nil
|
||||
}
|
||||
}
|
||||
return AOriginal, echo.NewHTTPError(http.StatusBadRequest, "Invalid quality")
|
||||
}
|
||||
|
||||
func (a AudioQuality) Bitrate() uint32 {
|
||||
switch a {
|
||||
case K128:
|
||||
return 128_000
|
||||
case K192:
|
||||
return 192_000
|
||||
case K256:
|
||||
return 256_000
|
||||
case K320:
|
||||
return 320_000
|
||||
case K512:
|
||||
return 512_000
|
||||
case AOriginal:
|
||||
panic("Original quality must be handled specially")
|
||||
}
|
||||
panic("Invalid quality value")
|
||||
}
|
||||
|
||||
func (audio *Audio) Quality() AudioQuality {
|
||||
for _, quality := range AudioQualities {
|
||||
if quality.Bitrate() >= audio.Bitrate {
|
||||
return quality
|
||||
}
|
||||
}
|
||||
return K128
|
||||
}
|
||||
@ -7,10 +7,11 @@ import (
|
||||
|
||||
type AudioStream struct {
|
||||
Stream
|
||||
index uint32
|
||||
audio *Audio
|
||||
quality AudioQuality
|
||||
}
|
||||
|
||||
func (t *Transcoder) NewAudioStream(file *FileStream, idx uint32) (*AudioStream, error) {
|
||||
func (t *Transcoder) NewAudioStream(file *FileStream, idx uint32, quality AudioQuality) (*AudioStream, error) {
|
||||
log.Printf("Creating a audio stream %d for %s", idx, file.Info.Path)
|
||||
|
||||
keyframes, err := t.metadataService.GetKeyframes(file.Info, false, idx)
|
||||
@ -19,13 +20,20 @@ func (t *Transcoder) NewAudioStream(file *FileStream, idx uint32) (*AudioStream,
|
||||
}
|
||||
|
||||
ret := new(AudioStream)
|
||||
ret.index = idx
|
||||
ret.quality = quality
|
||||
for _, audio := range file.Info.Audios {
|
||||
if audio.Index == idx {
|
||||
ret.audio = &audio
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
NewStream(file, keyframes, ret, &ret.Stream)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (as *AudioStream) getOutPath(encoder_id int) string {
|
||||
return fmt.Sprintf("%s/segment-a%d-%d-%%d.ts", as.file.Out, as.index, encoder_id)
|
||||
return fmt.Sprintf("%s/segment-a%d-%s-%d-%%d.ts", as.file.Out, as.audio.Index, string(as.quality), encoder_id)
|
||||
}
|
||||
|
||||
func (as *AudioStream) getFlags() Flags {
|
||||
@ -33,12 +41,18 @@ func (as *AudioStream) getFlags() Flags {
|
||||
}
|
||||
|
||||
func (as *AudioStream) getTranscodeArgs(segments string) []string {
|
||||
return []string{
|
||||
"-map", fmt.Sprintf("0:a:%d", as.index),
|
||||
"-c:a", "aac",
|
||||
// TODO: Support 5.1 audio streams.
|
||||
"-ac", "2",
|
||||
// TODO: Support multi audio qualities.
|
||||
"-b:a", "128k",
|
||||
args := []string{
|
||||
"-map", fmt.Sprintf("0:a:%d", as.audio.Index),
|
||||
}
|
||||
if as.quality == AOriginal {
|
||||
args = append(args, "-c:a", "copy")
|
||||
} else {
|
||||
args = append(args,
|
||||
// TODO: Support 5.1 audio streams.
|
||||
"-ac", "2",
|
||||
"-b:a", fmt.Sprint(as.quality.Bitrate()),
|
||||
"-c:a", "aac",
|
||||
)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -19,12 +20,17 @@ type FileStream struct {
|
||||
Out string
|
||||
Info *MediaInfo
|
||||
videos CMap[VideoKey, *VideoStream]
|
||||
audios CMap[uint32, *AudioStream]
|
||||
audios CMap[AudioKey, *AudioStream]
|
||||
}
|
||||
|
||||
type AudioKey struct {
|
||||
idx uint32
|
||||
quality AudioQuality
|
||||
}
|
||||
|
||||
type VideoKey struct {
|
||||
idx uint32
|
||||
quality Quality
|
||||
quality VideoQuality
|
||||
}
|
||||
|
||||
func (t *Transcoder) newFileStream(path string, sha string) *FileStream {
|
||||
@ -32,7 +38,7 @@ func (t *Transcoder) newFileStream(path string, sha string) *FileStream {
|
||||
transcoder: t,
|
||||
Out: fmt.Sprintf("%s/%s", Settings.Outpath, sha),
|
||||
videos: NewCMap[VideoKey, *VideoStream](),
|
||||
audios: NewCMap[uint32, *AudioStream](),
|
||||
audios: NewCMap[AudioKey, *AudioStream](),
|
||||
}
|
||||
|
||||
ret.ready.Add(1)
|
||||
@ -71,10 +77,28 @@ func (fs *FileStream) Destroy() {
|
||||
func (fs *FileStream) GetMaster(client string) string {
|
||||
master := "#EXTM3U\n"
|
||||
|
||||
// TODO: support multiples audio qualities (and original)
|
||||
for _, audio := range fs.Info.Audios {
|
||||
for _, quality := range AudioQualities {
|
||||
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
|
||||
master += fmt.Sprintf("GROUP-ID=\"audio-%s\",", quality)
|
||||
if audio.Language != nil {
|
||||
master += fmt.Sprintf("LANGUAGE=\"%s\",", *audio.Language)
|
||||
}
|
||||
if audio.Title != nil {
|
||||
master += fmt.Sprintf("NAME=\"%s\",", *audio.Title)
|
||||
} else if audio.Language != nil {
|
||||
master += fmt.Sprintf("NAME=\"%s\",", *audio.Language)
|
||||
} else {
|
||||
master += fmt.Sprintf("NAME=\"Audio %d\",", audio.Index)
|
||||
}
|
||||
if audio.IsDefault {
|
||||
master += "DEFAULT=YES,"
|
||||
}
|
||||
master += "CHANNELS=\"2\","
|
||||
master += fmt.Sprintf("URI=\"audio/%d/%s/index.m3u8?clientId=%s\"\n", audio.Index, quality, client)
|
||||
}
|
||||
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
|
||||
master += "GROUP-ID=\"audio\","
|
||||
master += fmt.Sprintf("GROUP-ID=\"audio-%s\",", AOriginal)
|
||||
if audio.Language != nil {
|
||||
master += fmt.Sprintf("LANGUAGE=\"%s\",", *audio.Language)
|
||||
}
|
||||
@ -88,17 +112,17 @@ func (fs *FileStream) GetMaster(client string) string {
|
||||
if audio.IsDefault {
|
||||
master += "DEFAULT=YES,"
|
||||
}
|
||||
master += "CHANNELS=\"2\","
|
||||
master += fmt.Sprintf("URI=\"audio/%d/index.m3u8?clientId=%s\"\n", audio.Index, client)
|
||||
master += "CHANNELS=\"2\"," // TODO
|
||||
master += fmt.Sprintf("URI=\"audio/%d/%s/index.m3u8?clientId=%s\"\n", audio.Index, AOriginal, client)
|
||||
}
|
||||
master += "\n"
|
||||
|
||||
// codec is the prefix + the level, the level is not part of the codec we want to compare for the same_codec check bellow
|
||||
transcode_prefix := "avc1.6400"
|
||||
transcode_codec := transcode_prefix + "28"
|
||||
audio_codec := "mp4a.40.2"
|
||||
transcode_audio_codec := "mp4a.40.2"
|
||||
|
||||
var def_video *Video
|
||||
var def_audio *Audio
|
||||
for _, video := range fs.Info.Videos {
|
||||
if video.IsDefault {
|
||||
def_video = &video
|
||||
@ -108,12 +132,14 @@ func (fs *FileStream) GetMaster(client string) string {
|
||||
if def_video == nil && len(fs.Info.Videos) > 0 {
|
||||
def_video = &fs.Info.Videos[0]
|
||||
}
|
||||
if len(fs.Info.Audios) > 0 {
|
||||
def_audio = &fs.Info.Audios[0]
|
||||
}
|
||||
|
||||
if def_video != nil {
|
||||
qualities := utils.Filter(Qualities, func(quality Quality) bool {
|
||||
qualities := utils.Filter(VideoQualities, func(quality VideoQuality) bool {
|
||||
return quality.Height() < def_video.Height
|
||||
})
|
||||
transcode_count := len(qualities)
|
||||
|
||||
// NoResize is the same idea as Original but we change the codec.
|
||||
// This is only needed when the original's codec is different from what we would transcode it to.
|
||||
@ -146,22 +172,32 @@ func (fs *FileStream) GetMaster(client string) string {
|
||||
master += "\n"
|
||||
|
||||
aspectRatio := float32(def_video.Width) / float32(def_video.Height)
|
||||
for i, quality := range qualities {
|
||||
if i >= transcode_count {
|
||||
// original & noresize streams
|
||||
bitrate := float64(def_video.Bitrate)
|
||||
master += "#EXT-X-STREAM-INF:"
|
||||
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", int(math.Min(bitrate*0.8, float64(def_video.Quality().AverageBitrate()))))
|
||||
master += fmt.Sprintf("BANDWIDTH=%d,", int(math.Min(bitrate, float64(def_video.Quality().MaxBitrate()))))
|
||||
master += fmt.Sprintf("RESOLUTION=%dx%d,", def_video.Width, def_video.Height)
|
||||
if quality != Original {
|
||||
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, audio_codec}, ","))
|
||||
} else if def_video.MimeCodec != nil {
|
||||
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{*def_video.MimeCodec, audio_codec}, ","))
|
||||
for _, quality := range slices.Backward(qualities) {
|
||||
if quality == Original || quality == NoResize {
|
||||
audios := []AudioQuality{AOriginal}
|
||||
if def_audio != nil && (def_audio.MimeCodec == nil || *def_audio.MimeCodec != transcode_audio_codec) {
|
||||
audios = append(audios, matchAudioQuality(def_video.Quality()))
|
||||
}
|
||||
for _, audio_quality := range audios {
|
||||
// original & noresize streams
|
||||
bitrate := float64(def_video.Bitrate)
|
||||
master += "#EXT-X-STREAM-INF:"
|
||||
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", int(math.Min(bitrate*0.8, float64(def_video.Quality().AverageBitrate()))))
|
||||
master += fmt.Sprintf("BANDWIDTH=%d,", int(math.Min(bitrate, float64(def_video.Quality().MaxBitrate()))))
|
||||
master += fmt.Sprintf("RESOLUTION=%dx%d,", def_video.Width, def_video.Height)
|
||||
var audio_codec = transcode_audio_codec
|
||||
if def_audio != nil && audio_quality == AOriginal {
|
||||
audio_codec = *def_audio.MimeCodec
|
||||
}
|
||||
if quality != Original {
|
||||
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, audio_codec}, ","))
|
||||
} else if def_video.MimeCodec != nil {
|
||||
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{*def_video.MimeCodec, audio_codec}, ","))
|
||||
}
|
||||
master += fmt.Sprintf("AUDIO=\"audio-%s\",", string(audio_quality))
|
||||
master += "CLOSED-CAPTIONS=NONE\n"
|
||||
master += fmt.Sprintf("%d/%s/index.m3u8?clientId=%s\n", def_video.Index, quality, client)
|
||||
}
|
||||
master += "AUDIO=\"audio\","
|
||||
master += "CLOSED-CAPTIONS=NONE\n"
|
||||
master += fmt.Sprintf("%d/%s/index.m3u8?clientId=%s\n", def_video.Index, quality, client)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -169,8 +205,8 @@ func (fs *FileStream) GetMaster(client string) string {
|
||||
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", quality.AverageBitrate())
|
||||
master += fmt.Sprintf("BANDWIDTH=%d,", quality.MaxBitrate())
|
||||
master += fmt.Sprintf("RESOLUTION=%dx%d,", int(aspectRatio*float32(quality.Height())+0.5), quality.Height())
|
||||
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, audio_codec}, ","))
|
||||
master += "AUDIO=\"audio\","
|
||||
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, transcode_audio_codec}, ","))
|
||||
master += fmt.Sprintf("AUDIO=\"audio-%s\",", string(matchAudioQuality(quality)))
|
||||
master += "CLOSED-CAPTIONS=NONE\n"
|
||||
master += fmt.Sprintf("%d/%s/index.m3u8?clientId=%s\n", def_video.Index, quality, client)
|
||||
}
|
||||
@ -179,7 +215,7 @@ func (fs *FileStream) GetMaster(client string) string {
|
||||
return master
|
||||
}
|
||||
|
||||
func (fs *FileStream) getVideoStream(idx uint32, quality Quality) (*VideoStream, error) {
|
||||
func (fs *FileStream) getVideoStream(idx uint32, quality VideoQuality) (*VideoStream, error) {
|
||||
stream, _ := fs.videos.GetOrCreate(VideoKey{idx, quality}, func() *VideoStream {
|
||||
ret, _ := fs.transcoder.NewVideoStream(fs, idx, quality)
|
||||
return ret
|
||||
@ -188,7 +224,7 @@ func (fs *FileStream) getVideoStream(idx uint32, quality Quality) (*VideoStream,
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (fs *FileStream) GetVideoIndex(idx uint32, quality Quality, client string) (string, error) {
|
||||
func (fs *FileStream) GetVideoIndex(idx uint32, quality VideoQuality, client string) (string, error) {
|
||||
stream, err := fs.getVideoStream(idx, quality)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -196,7 +232,7 @@ func (fs *FileStream) GetVideoIndex(idx uint32, quality Quality, client string)
|
||||
return stream.GetIndex(client)
|
||||
}
|
||||
|
||||
func (fs *FileStream) GetVideoSegment(idx uint32, quality Quality, segment int32) (string, error) {
|
||||
func (fs *FileStream) GetVideoSegment(idx uint32, quality VideoQuality, segment int32) (string, error) {
|
||||
stream, err := fs.getVideoStream(idx, quality)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -204,27 +240,50 @@ func (fs *FileStream) GetVideoSegment(idx uint32, quality Quality, segment int32
|
||||
return stream.GetSegment(segment)
|
||||
}
|
||||
|
||||
func (fs *FileStream) getAudioStream(audio uint32) (*AudioStream, error) {
|
||||
stream, _ := fs.audios.GetOrCreate(audio, func() *AudioStream {
|
||||
ret, _ := fs.transcoder.NewAudioStream(fs, audio)
|
||||
func (fs *FileStream) getAudioStream(idx uint32, quality AudioQuality) (*AudioStream, error) {
|
||||
stream, _ := fs.audios.GetOrCreate(AudioKey{idx, quality}, func() *AudioStream {
|
||||
ret, _ := fs.transcoder.NewAudioStream(fs, idx, quality)
|
||||
return ret
|
||||
})
|
||||
stream.ready.Wait()
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (fs *FileStream) GetAudioIndex(audio uint32, client string) (string, error) {
|
||||
stream, err := fs.getAudioStream(audio)
|
||||
func (fs *FileStream) GetAudioIndex(idx uint32, quality AudioQuality, client string) (string, error) {
|
||||
stream, err := fs.getAudioStream(idx, quality)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return stream.GetIndex(client)
|
||||
}
|
||||
|
||||
func (fs *FileStream) GetAudioSegment(audio uint32, segment int32) (string, error) {
|
||||
stream, err := fs.getAudioStream(audio)
|
||||
func (fs *FileStream) GetAudioSegment(idx uint32, quality AudioQuality, segment int32) (string, error) {
|
||||
stream, err := fs.getAudioStream(idx, quality)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return stream.GetSegment(segment)
|
||||
}
|
||||
|
||||
func matchAudioQuality(q VideoQuality) AudioQuality {
|
||||
switch q {
|
||||
case P240:
|
||||
return K128
|
||||
case P360:
|
||||
return K128
|
||||
case P480:
|
||||
return K128
|
||||
case P720:
|
||||
return K192
|
||||
case P1080:
|
||||
return K192
|
||||
case P1440:
|
||||
return K256
|
||||
case P4k:
|
||||
return K512
|
||||
case P8k:
|
||||
return K512
|
||||
default:
|
||||
return AOriginal
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ type ClientInfo struct {
|
||||
sha string
|
||||
path string
|
||||
video *VideoKey
|
||||
audio *uint32
|
||||
audio *AudioKey
|
||||
vhead int32
|
||||
ahead int32
|
||||
}
|
||||
@ -151,7 +151,7 @@ func (t *Tracker) DestroyStreamIfOld(sha string) {
|
||||
stream.Destroy()
|
||||
}
|
||||
|
||||
func (t *Tracker) KillAudioIfDead(sha string, path string, audio uint32) bool {
|
||||
func (t *Tracker) KillAudioIfDead(sha string, path string, audio AudioKey) bool {
|
||||
for _, stream := range t.clients {
|
||||
if stream.sha == sha && stream.audio != nil && *stream.audio == audio {
|
||||
return false
|
||||
@ -191,7 +191,7 @@ func (t *Tracker) KillVideoIfDead(sha string, path string, video VideoKey) bool
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Tracker) KillOrphanedHeads(sha string, video *VideoKey, audio *uint32) {
|
||||
func (t *Tracker) KillOrphanedHeads(sha string, video *VideoKey, audio *AudioKey) {
|
||||
stream, ok := t.transcoder.streams.Get(sha)
|
||||
if !ok {
|
||||
return
|
||||
|
||||
@ -70,7 +70,7 @@ func (t *Transcoder) GetVideoIndex(
|
||||
ctx context.Context,
|
||||
path string,
|
||||
video uint32,
|
||||
quality Quality,
|
||||
quality VideoQuality,
|
||||
client string,
|
||||
sha string,
|
||||
) (string, error) {
|
||||
@ -94,6 +94,7 @@ func (t *Transcoder) GetAudioIndex(
|
||||
ctx context.Context,
|
||||
path string,
|
||||
audio uint32,
|
||||
quality AudioQuality,
|
||||
client string,
|
||||
sha string,
|
||||
) (string, error) {
|
||||
@ -105,18 +106,18 @@ func (t *Transcoder) GetAudioIndex(
|
||||
client: client,
|
||||
sha: sha,
|
||||
path: path,
|
||||
audio: &audio,
|
||||
audio: &AudioKey{audio, quality},
|
||||
vhead: -1,
|
||||
ahead: -1,
|
||||
}
|
||||
return stream.GetAudioIndex(audio, client)
|
||||
return stream.GetAudioIndex(audio, quality, client)
|
||||
}
|
||||
|
||||
func (t *Transcoder) GetVideoSegment(
|
||||
ctx context.Context,
|
||||
path string,
|
||||
video uint32,
|
||||
quality Quality,
|
||||
quality VideoQuality,
|
||||
segment int32,
|
||||
client string,
|
||||
sha string,
|
||||
@ -141,6 +142,7 @@ func (t *Transcoder) GetAudioSegment(
|
||||
ctx context.Context,
|
||||
path string,
|
||||
audio uint32,
|
||||
quality AudioQuality,
|
||||
segment int32,
|
||||
client string,
|
||||
sha string,
|
||||
@ -153,9 +155,9 @@ func (t *Transcoder) GetAudioSegment(
|
||||
client: client,
|
||||
sha: sha,
|
||||
path: path,
|
||||
audio: &audio,
|
||||
audio: &AudioKey{audio, quality},
|
||||
ahead: segment,
|
||||
vhead: -1,
|
||||
}
|
||||
return stream.GetAudioSegment(audio, segment)
|
||||
return stream.GetAudioSegment(audio, quality, segment)
|
||||
}
|
||||
|
||||
@ -6,25 +6,25 @@ import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Quality string
|
||||
type VideoQuality string
|
||||
|
||||
const (
|
||||
P240 Quality = "240p"
|
||||
P360 Quality = "360p"
|
||||
P480 Quality = "480p"
|
||||
P720 Quality = "720p"
|
||||
P1080 Quality = "1080p"
|
||||
P1440 Quality = "1440p"
|
||||
P4k Quality = "4k"
|
||||
P8k Quality = "8k"
|
||||
NoResize Quality = "transcode"
|
||||
Original Quality = "original"
|
||||
P240 VideoQuality = "240p"
|
||||
P360 VideoQuality = "360p"
|
||||
P480 VideoQuality = "480p"
|
||||
P720 VideoQuality = "720p"
|
||||
P1080 VideoQuality = "1080p"
|
||||
P1440 VideoQuality = "1440p"
|
||||
P4k VideoQuality = "4k"
|
||||
P8k VideoQuality = "8k"
|
||||
NoResize VideoQuality = "transcode"
|
||||
Original VideoQuality = "original"
|
||||
)
|
||||
|
||||
// Purposfully removing Original from this list (since it require special treatments anyways)
|
||||
var Qualities = []Quality{P240, P360, P480, P720, P1080, P1440, P4k, P8k}
|
||||
var VideoQualities = []VideoQuality{P240, P360, P480, P720, P1080, P1440, P4k, P8k}
|
||||
|
||||
func QualityFromString(str string) (Quality, error) {
|
||||
func VideoQualityFromString(str string) (VideoQuality, error) {
|
||||
if str == string(Original) {
|
||||
return Original, nil
|
||||
}
|
||||
@ -32,7 +32,7 @@ func QualityFromString(str string) (Quality, error) {
|
||||
return NoResize, nil
|
||||
}
|
||||
|
||||
for _, quality := range Qualities {
|
||||
for _, quality := range VideoQualities {
|
||||
if string(quality) == str {
|
||||
return quality, nil
|
||||
}
|
||||
@ -41,7 +41,7 @@ func QualityFromString(str string) (Quality, error) {
|
||||
}
|
||||
|
||||
// I'm not entierly sure about the values for bitrates. Double checking would be nice.
|
||||
func (v Quality) AverageBitrate() uint32 {
|
||||
func (v VideoQuality) AverageBitrate() uint32 {
|
||||
switch v {
|
||||
case P240:
|
||||
return 400_000
|
||||
@ -65,7 +65,7 @@ func (v Quality) AverageBitrate() uint32 {
|
||||
panic("Invalid quality value")
|
||||
}
|
||||
|
||||
func (v Quality) MaxBitrate() uint32 {
|
||||
func (v VideoQuality) MaxBitrate() uint32 {
|
||||
switch v {
|
||||
case P240:
|
||||
return 700_000
|
||||
@ -89,7 +89,7 @@ func (v Quality) MaxBitrate() uint32 {
|
||||
panic("Invalid quality value")
|
||||
}
|
||||
|
||||
func (q Quality) Height() uint32 {
|
||||
func (q VideoQuality) Height() uint32 {
|
||||
switch q {
|
||||
case P240:
|
||||
return 240
|
||||
@ -113,8 +113,8 @@ func (q Quality) Height() uint32 {
|
||||
panic("Invalid quality value")
|
||||
}
|
||||
|
||||
func (video *Video) Quality() Quality {
|
||||
for _, quality := range Qualities {
|
||||
func (video *Video) Quality() VideoQuality {
|
||||
for _, quality := range VideoQualities {
|
||||
if quality.Height() >= video.Height || quality.AverageBitrate() >= video.Bitrate {
|
||||
return quality
|
||||
}
|
||||
@ -8,10 +8,10 @@ import (
|
||||
type VideoStream struct {
|
||||
Stream
|
||||
video *Video
|
||||
quality Quality
|
||||
quality VideoQuality
|
||||
}
|
||||
|
||||
func (t *Transcoder) NewVideoStream(file *FileStream, idx uint32, quality Quality) (*VideoStream, error) {
|
||||
func (t *Transcoder) NewVideoStream(file *FileStream, idx uint32, quality VideoQuality) (*VideoStream, error) {
|
||||
log.Printf(
|
||||
"Creating a new video stream for %s (n %d) in quality %s",
|
||||
file.Info.Path,
|
||||
@ -84,7 +84,7 @@ func (vs *VideoStream) getTranscodeArgs(segments string) []string {
|
||||
args = append(args, "-vf", Settings.HwAccel.NoResizeFilter)
|
||||
|
||||
// NoResize doesn't have bitrate info, fallback to a know quality higher or equal.
|
||||
for _, q := range Qualities {
|
||||
for _, q := range VideoQualities {
|
||||
if q.Height() >= vs.video.Height {
|
||||
quality = q
|
||||
break
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user