Working utoipa setup

This commit is contained in:
Antoine Gersant 2025-01-13 22:15:59 -08:00
parent 1b142b1855
commit 2e2ddf017b
32 changed files with 55 additions and 1550 deletions

View file

@ -43,6 +43,7 @@
### API ### API
- API version is now 8.0. - API version is now 8.0.
- Documentation is now served under `/docs` instead of `/swagger` (eg. `http://localhost:5050/docs`)
- Clients are now expected to send their preferred API major version in a `Accept-Version` header. Omitting this currently defaults to `7`, but will become an error in future Polaris releases. Support for API version 7 will be removed entirely in a future release. - Clients are now expected to send their preferred API major version in a `Accept-Version` header. Omitting this currently defaults to `7`, but will become an error in future Polaris releases. Support for API version 7 will be removed entirely in a future release.
- Most API responses now support gzip compression. - Most API responses now support gzip compression.
- The response format of the `/browse`, `/flatten`, `/get_playlist`, `/search/<query>` endpoints has been modified to accomodate large lists. - The response format of the `/browse`, `/flatten`, `/get_playlist`, `/search/<query>` endpoints has been modified to accomodate large lists.

53
Cargo.lock generated
View file

@ -1851,7 +1851,11 @@ dependencies = [
"ureq", "ureq",
"utoipa", "utoipa",
"utoipa-axum", "utoipa-axum",
<<<<<<< HEAD
"utoipa-swagger-ui", "utoipa-swagger-ui",
=======
"utoipa-scalar",
>>>>>>> 7653e4b (Working utoipa setup)
"winres", "winres",
] ]
@ -2711,6 +2715,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.96",
<<<<<<< HEAD
] ]
[[package]] [[package]]
@ -2722,6 +2727,8 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.96",
=======
>>>>>>> 7653e4b (Working utoipa setup)
] ]
[[package]] [[package]]
@ -3106,6 +3113,7 @@ dependencies = [
] ]
[[package]] [[package]]
<<<<<<< HEAD
name = "utoipa-swagger-ui" name = "utoipa-swagger-ui"
version = "8.1.1" version = "8.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
@ -3131,6 +3139,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2eebbbfe4093922c2b6734d7c679ebfebd704a0d7e56dfcb0d05818ce28977d" checksum = "e2eebbbfe4093922c2b6734d7c679ebfebd704a0d7e56dfcb0d05818ce28977d"
[[package]] [[package]]
=======
name = "utoipa-scalar"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "088e93bf19f6bd06e0aacb02ca432b3c5a449c4aec2e4aa9fc333a667f2b2c55"
dependencies = [
"axum",
"serde",
"serde_json",
"utoipa",
]
[[package]]
>>>>>>> 7653e4b (Working utoipa setup)
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
@ -3527,37 +3549,6 @@ dependencies = [
"syn 2.0.96", "syn 2.0.96",
] ]
[[package]]
name = "zip"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45"
dependencies = [
"arbitrary",
"crc32fast",
"crossbeam-utils",
"displaydoc",
"flate2",
"indexmap",
"memchr",
"thiserror 2.0.11",
"zopfli",
]
[[package]]
name = "zopfli"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
dependencies = [
"bumpalo",
"crc32fast",
"lockfree-object-pool",
"log",
"once_cell",
"simd-adler32",
]
[[package]] [[package]]
name = "zune-core" name = "zune-core"
version = "0.4.12" version = "0.4.12"

View file

@ -66,8 +66,8 @@ trie-rs = { version = "0.4.2", features = ["serde"] }
unicase = "2.7.0" unicase = "2.7.0"
ureq = { version = "2.10.0", default-features = false, features = ["tls"] } ureq = { version = "2.10.0", default-features = false, features = ["tls"] }
utoipa = { version = "5.3", features = ["axum_extras"] } utoipa = { version = "5.3", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "8.1", features = ["axum", "vendored"] }
utoipa-axum = { version = "0.1" } utoipa-axum = { version = "0.1" }
utoipa-scalar = { version = "0.2", features = ["axum"] }
[dependencies.axum] [dependencies.axum]
version = "0.8.1" version = "0.8.1"

View file

@ -40,7 +40,7 @@ Password: `demo_password`
### API Documentation ### API Documentation
The Polaris server API is documented via [Swagger](https://demo.polaris.stream/swagger/). Every installation of Polaris distributes this documentation, with the ability to use the `Try it out` buttons. To access it, simply open http://localhost:5050/swagger/ in your browser on the machine running Polaris. The Polaris server API is documented via [OpenAPI](https://demo.polaris.stream/docs/). Every installation of Polaris distributes this interactive documentation. To access it, simply open http://localhost:5050/docs/ in your browser on the machine running Polaris.
## Credits & License Information ## Credits & License Information

View file

@ -13,10 +13,9 @@ Polaris supports a few command line arguments which are useful during developmen
- `-c some/config.toml` sets the location of the configuration file. This is useful to preconfigure users and music directories. - `-c some/config.toml` sets the location of the configuration file. This is useful to preconfigure users and music directories.
- `--data some/path` sets the folder Polaris will use to store runtime data such as playlists, collection index and auth secrets. - `--data some/path` sets the folder Polaris will use to store runtime data such as playlists, collection index and auth secrets.
- `-w some/path/to/web/dir` lets you point to the directory to be served as the web interface. You can find a suitable directory in your Polaris install (under `/web`), or from the [latest polaris-web release](https://github.com/agersant/polaris-web/releases/latest/download/web.zip). - `-w some/path/to/web/dir` lets you point to the directory to be served as the web interface. You can find a suitable directory in your Polaris install (under `/web`), or from the [latest polaris-web release](https://github.com/agersant/polaris-web/releases/latest/download/web.zip).
- `-s some/path/to/swagger/dir` lets you point to the directory to be served as the swagger API documentation. You'll probably want to point this to the `/docs/swagger` directory of the polaris repository.
- `-f` (on Linux) makes Polaris not fork into a separate process. - `-f` (on Linux) makes Polaris not fork into a separate process.
Putting it all together, a typical command to compile and run the program would be: `cargo run -- -w web -s docs/swagger -c test-config.toml` Putting it all together, a typical command to compile and run the program would be: `cargo run -- -w web -c test-config.toml`
While Polaris is running, access the web UI at [http://localhost:5050](http://localhost:5050). While Polaris is running, access the web UI at [http://localhost:5050](http://localhost:5050).

Binary file not shown.

Before

(image error) Size: 665 B

Binary file not shown.

Before

(image error) Size: 628 B

View file

@ -1,60 +0,0 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Polaris Swagger UI</title>
<link rel="stylesheet" type="text/css" href="swagger-ui.css">
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
<style>
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="swagger-ui-bundle.js"> </script>
<script src="swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "polaris-api.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>

View file

@ -1,67 +0,0 @@
<!doctype html>
<html lang="en-US">
<body onload="run()">
</body>
</html>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
) : {}
isValid = qp.state === sentState
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
</script>

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -3,7 +3,7 @@ echo "Creating output directory"
mkdir -p release/tmp/polaris mkdir -p release/tmp/polaris
echo "Copying package files" echo "Copying package files"
cp -r web docs/swagger src migrations test-data build.rs Cargo.toml Cargo.lock rust-toolchain res/unix/Makefile release/tmp/polaris cp -r web src migrations test-data build.rs Cargo.toml Cargo.lock rust-toolchain res/unix/Makefile release/tmp/polaris
echo "Creating tarball" echo "Creating tarball"
tar -zc -C release/tmp -f release/polaris.tar.gz polaris tar -zc -C release/tmp -f release/polaris.tar.gz polaris

View file

@ -49,7 +49,6 @@
<ComponentRef Id="ProgramMenuDir" /> <ComponentRef Id="ProgramMenuDir" />
<ComponentRef Id="CleanupExtraData" /> <ComponentRef Id="CleanupExtraData" />
<ComponentGroupRef Id="WebUI" /> <ComponentGroupRef Id="WebUI" />
<ComponentGroupRef Id="SwaggerUI" />
</Feature> </Feature>
<Icon Id="polaris.exe" SourceFile="polaris.exe" /> <Icon Id="polaris.exe" SourceFile="polaris.exe" />
<Property Id="ARPPRODUCTICON" Value="polaris.exe" /> <Property Id="ARPPRODUCTICON" Value="polaris.exe" />

View file

@ -8,7 +8,6 @@ if (!(Test-Path env:POLARIS_VERSION)) {
# And remove the code setting these as defaults in `service/mod.rs` # And remove the code setting these as defaults in `service/mod.rs`
# $script:INSTALL_DIR = "%LOCALAPPDATA%\Permafrost\Polaris" # $script:INSTALL_DIR = "%LOCALAPPDATA%\Permafrost\Polaris"
# $env:POLARIS_WEB_DIR = "$INSTALL_DIR\web" # $env:POLARIS_WEB_DIR = "$INSTALL_DIR\web"
# $env:POLARIS_SWAGGER_DIR = "$INSTALL_DIR\swagger"
# $env:POLARIS_DB_DIR = "$INSTALL_DIR" # $env:POLARIS_DB_DIR = "$INSTALL_DIR"
# $env:POLARIS_LOG_DIR = "$INSTALL_DIR" # $env:POLARIS_LOG_DIR = "$INSTALL_DIR"
# $env:POLARIS_CACHE_DIR = "$INSTALL_DIR" # $env:POLARIS_CACHE_DIR = "$INSTALL_DIR"
@ -29,7 +28,6 @@ Copy-Item .\res\windows\installer\dialog.bmp .\release\tmp\
Copy-Item .\target\release\polaris.exe .\release\tmp\ Copy-Item .\target\release\polaris.exe .\release\tmp\
Copy-Item .\target\release\polaris-cli.exe .\release\tmp\ Copy-Item .\target\release\polaris-cli.exe .\release\tmp\
Copy-Item .\web .\release\tmp\web -recurse Copy-Item .\web .\release\tmp\web -recurse
Copy-Item .\docs\swagger .\release\tmp\swagger -recurse
"" ""
"Inserting version number in installer config" "Inserting version number in installer config"
@ -41,15 +39,13 @@ $wxs.Save('.\res\windows\installer\installer.wxs')
"Creating installer" "Creating installer"
$heat_exe = Join-Path $env:WIX bin\heat.exe $heat_exe = Join-Path $env:WIX bin\heat.exe
& $heat_exe dir .\release\tmp\web\ -ag -g1 -dr AppDataPolaris -cg WebUI -sfrag -var wix.WebUIDir -out .\release\tmp\web_ui_fragment.wxs & $heat_exe dir .\release\tmp\web\ -ag -g1 -dr AppDataPolaris -cg WebUI -sfrag -var wix.WebUIDir -out .\release\tmp\web_ui_fragment.wxs
& $heat_exe dir .\release\tmp\swagger\ -ag -g1 -dr AppDataPolaris -cg SwaggerUI -sfrag -var wix.SwaggerUIDir -out .\release\tmp\swagger_ui_fragment.wxs
$candle_exe = Join-Path $env:WIX bin\candle.exe $candle_exe = Join-Path $env:WIX bin\candle.exe
& $candle_exe -wx -ext WixUtilExtension -arch x64 -out .\release\tmp\web_ui_fragment.wixobj .\release\tmp\web_ui_fragment.wxs & $candle_exe -wx -ext WixUtilExtension -arch x64 -out .\release\tmp\web_ui_fragment.wixobj .\release\tmp\web_ui_fragment.wxs
& $candle_exe -wx -ext WixUtilExtension -arch x64 -out .\release\tmp\swagger_ui_fragment.wixobj .\release\tmp\swagger_ui_fragment.wxs
& $candle_exe -wx -ext WixUtilExtension -arch x64 -out .\release\tmp\installer.wixobj .\res\windows\installer\installer.wxs & $candle_exe -wx -ext WixUtilExtension -arch x64 -out .\release\tmp\installer.wixobj .\res\windows\installer\installer.wxs
$light_exe = Join-Path $env:WIX bin\light.exe $light_exe = Join-Path $env:WIX bin\light.exe
& $light_exe -dWebUIDir=".\release\tmp\web" -dSwaggerUIDir=".\release\tmp\swagger" -wx -ext WixUtilExtension -ext WixUIExtension -spdb -sw1076 -sice:ICE38 -sice:ICE64 -out .\release\polaris.msi .\release\tmp\installer.wixobj .\release\tmp\web_ui_fragment.wixobj .\release\tmp\swagger_ui_fragment.wixobj & $light_exe -dWebUIDir=".\release\tmp\web" -wx -ext WixUtilExtension -ext WixUIExtension -spdb -sw1076 -sice:ICE38 -sice:ICE64 -out .\release\polaris.msi .\release\tmp\installer.wixobj .\release\tmp\web_ui_fragment.wixobj
"Cleaning up" "Cleaning up"
Remove-Item -Recurse .\release\tmp Remove-Item -Recurse .\release\tmp

View file

@ -154,7 +154,6 @@ pub enum Error {
pub struct App { pub struct App {
pub port: u16, pub port: u16,
pub web_dir_path: PathBuf, pub web_dir_path: PathBuf,
pub swagger_dir_path: PathBuf,
pub ddns_manager: ddns::Manager, pub ddns_manager: ddns::Manager,
pub scanner: scanner::Scanner, pub scanner: scanner::Scanner,
pub index_manager: index::Manager, pub index_manager: index::Manager,
@ -172,9 +171,6 @@ impl App {
fs::create_dir_all(&paths.web_dir_path) fs::create_dir_all(&paths.web_dir_path)
.map_err(|e| Error::Io(paths.web_dir_path.clone(), e))?; .map_err(|e| Error::Io(paths.web_dir_path.clone(), e))?;
fs::create_dir_all(&paths.swagger_dir_path)
.map_err(|e| Error::Io(paths.swagger_dir_path.clone(), e))?;
let peaks_dir_path = paths.cache_dir_path.join("peaks"); let peaks_dir_path = paths.cache_dir_path.join("peaks");
fs::create_dir_all(&peaks_dir_path).map_err(|e| Error::Io(peaks_dir_path.clone(), e))?; fs::create_dir_all(&peaks_dir_path).map_err(|e| Error::Io(peaks_dir_path.clone(), e))?;
@ -198,7 +194,6 @@ impl App {
let app = Self { let app = Self {
port, port,
web_dir_path: paths.web_dir_path, web_dir_path: paths.web_dir_path,
swagger_dir_path: paths.swagger_dir_path,
ddns_manager, ddns_manager,
scanner, scanner,
index_manager, index_manager,

View file

@ -135,7 +135,6 @@ fn main() -> Result<(), Error> {
if !cli_options.foreground { if !cli_options.foreground {
info!("Pid file location is {:#?}", paths.pid_file_path); info!("Pid file location is {:#?}", paths.pid_file_path);
} }
info!("Swagger files location is {:#?}", paths.swagger_dir_path);
info!("Web client files location is {:#?}", paths.web_dir_path); info!("Web client files location is {:#?}", paths.web_dir_path);
async_main(cli_options, paths) async_main(cli_options, paths)

View file

@ -12,7 +12,6 @@ pub struct CLIOptions {
pub cache_dir_path: Option<PathBuf>, pub cache_dir_path: Option<PathBuf>,
pub data_dir_path: Option<PathBuf>, pub data_dir_path: Option<PathBuf>,
pub web_dir_path: Option<PathBuf>, pub web_dir_path: Option<PathBuf>,
pub swagger_dir_path: Option<PathBuf>,
pub port: Option<u16>, pub port: Option<u16>,
pub log_level: Option<LevelFilter>, pub log_level: Option<LevelFilter>,
} }
@ -45,7 +44,6 @@ impl Manager {
cache_dir_path: matches.opt_str("cache").map(PathBuf::from), cache_dir_path: matches.opt_str("cache").map(PathBuf::from),
data_dir_path: matches.opt_str("data").map(PathBuf::from), data_dir_path: matches.opt_str("data").map(PathBuf::from),
web_dir_path: matches.opt_str("w").map(PathBuf::from), web_dir_path: matches.opt_str("w").map(PathBuf::from),
swagger_dir_path: matches.opt_str("s").map(PathBuf::from),
port: matches.opt_str("p").and_then(|p| p.parse().ok()), port: matches.opt_str("p").and_then(|p| p.parse().ok()),
log_level: matches.opt_str("log-level").and_then(|l| l.parse().ok()), log_level: matches.opt_str("log-level").and_then(|l| l.parse().ok()),
}) })
@ -62,7 +60,6 @@ fn get_options() -> getopts::Options {
options.optopt("p", "port", "set polaris to run on a custom port", "PORT"); options.optopt("p", "port", "set polaris to run on a custom port", "PORT");
options.optopt("d", "database", "set the path to index database", "FILE"); options.optopt("d", "database", "set the path to index database", "FILE");
options.optopt("w", "web", "set the path to web client files", "DIRECTORY"); options.optopt("w", "web", "set the path to web client files", "DIRECTORY");
options.optopt("s", "swagger", "set the path to swagger files", "DIRECTORY");
options.optopt( options.optopt(
"", "",
"cache", "cache",

View file

@ -10,7 +10,6 @@ pub struct Paths {
pub log_file_path: Option<PathBuf>, pub log_file_path: Option<PathBuf>,
#[cfg(unix)] #[cfg(unix)]
pub pid_file_path: PathBuf, pub pid_file_path: PathBuf,
pub swagger_dir_path: PathBuf,
pub web_dir_path: PathBuf, pub web_dir_path: PathBuf,
} }
@ -26,7 +25,6 @@ impl Default for Paths {
db_file_path: [".", "db.sqlite"].iter().collect(), db_file_path: [".", "db.sqlite"].iter().collect(),
log_file_path: Some([".", "polaris.log"].iter().collect()), log_file_path: Some([".", "polaris.log"].iter().collect()),
pid_file_path: [".", "polaris.pid"].iter().collect(), pid_file_path: [".", "polaris.pid"].iter().collect(),
swagger_dir_path: [".", "docs", "swagger"].iter().collect(),
web_dir_path: [".", "web"].iter().collect(), web_dir_path: [".", "web"].iter().collect(),
} }
} }
@ -44,7 +42,6 @@ impl Default for Paths {
data_dir_path: install_directory.clone(), data_dir_path: install_directory.clone(),
db_file_path: install_directory.join("db.sqlite"), db_file_path: install_directory.join("db.sqlite"),
log_file_path: Some(install_directory.join("polaris.log")), log_file_path: Some(install_directory.join("polaris.log")),
swagger_dir_path: install_directory.join("swagger"),
web_dir_path: install_directory.join("web"), web_dir_path: install_directory.join("web"),
} }
} }
@ -76,9 +73,6 @@ impl Paths {
.map(PathBuf::from) .map(PathBuf::from)
.map(|p| p.join("polaris.pid")) .map(|p| p.join("polaris.pid"))
.unwrap_or(defaults.pid_file_path), .unwrap_or(defaults.pid_file_path),
swagger_dir_path: option_env!("POLARIS_SWAGGER_DIR")
.map(PathBuf::from)
.unwrap_or(defaults.swagger_dir_path),
web_dir_path: option_env!("POLARIS_WEB_DIR") web_dir_path: option_env!("POLARIS_WEB_DIR")
.map(PathBuf::from) .map(PathBuf::from)
.unwrap_or(defaults.web_dir_path), .unwrap_or(defaults.web_dir_path),
@ -103,9 +97,6 @@ impl Paths {
if let Some(path) = &cli_options.pid_file_path { if let Some(path) = &cli_options.pid_file_path {
path.clone_into(&mut paths.pid_file_path); path.clone_into(&mut paths.pid_file_path);
} }
if let Some(path) = &cli_options.swagger_dir_path {
path.clone_into(&mut paths.swagger_dir_path);
}
if let Some(path) = &cli_options.web_dir_path { if let Some(path) = &cli_options.web_dir_path {
path.clone_into(&mut paths.web_dir_path); path.clone_into(&mut paths.web_dir_path);
} }

View file

@ -9,7 +9,7 @@ use tower_http::{
}; };
use utoipa::OpenApi; use utoipa::OpenApi;
use utoipa_axum::router::OpenApiRouter; use utoipa_axum::router::OpenApiRouter;
use utoipa_swagger_ui::SwaggerUi; use utoipa_scalar::{Scalar, Servable};
mod api; mod api;
mod auth; mod auth;
@ -21,20 +21,18 @@ mod version;
pub mod test; pub mod test;
pub fn make_router(app: App) -> NormalizePath<Router> { pub fn make_router(app: App) -> NormalizePath<Router> {
let swagger = ServeDir::new(&app.swagger_dir_path);
let static_files = Router::new() let static_files = Router::new()
.fallback_service(ServeDir::new(&app.web_dir_path)) .fallback_service(ServeDir::new(&app.web_dir_path))
.layer(CompressionLayer::new()); .layer(CompressionLayer::new());
let (open_api_router, open_api) = let (open_api_router, open_api) = OpenApiRouter::with_openapi(doc::ApiDoc::openapi())
OpenApiRouter::with_openapi(doc::ApiDoc::openapi()).split_for_parts(); .nest("/api", api::router())
.split_for_parts();
let router = open_api_router let router = open_api_router
.merge(SwaggerUi::new("/swagger-ui"))
.nest("/api", api::router())
.with_state(app.clone()) .with_state(app.clone())
.nest("/", static_files) .merge(Scalar::with_url("/docs", open_api))
.fallback_service(static_files)
.layer(logger::LogLayer::new()); .layer(logger::LogLayer::new());
NormalizePathLayer::trim_trailing_slash().layer(router) NormalizePathLayer::trim_trailing_slash().layer(router)

View file

@ -4,13 +4,14 @@ use axum::{
extract::{DefaultBodyLimit, Path, Query, State}, extract::{DefaultBodyLimit, Path, Query, State},
response::{IntoResponse, Response}, response::{IntoResponse, Response},
routing::{delete, get, post, put}, routing::{delete, get, post, put},
Json, Router, Json,
}; };
use axum_extra::headers::Range; use axum_extra::headers::Range;
use axum_extra::TypedHeader; use axum_extra::TypedHeader;
use axum_range::{KnownSize, Ranged}; use axum_range::{KnownSize, Ranged};
use regex::Regex; use regex::Regex;
use tower_http::{compression::CompressionLayer, CompressionLevel}; use tower_http::{compression::CompressionLayer, CompressionLevel};
use utoipa_axum::{router::OpenApiRouter, routes};
use crate::{ use crate::{
app::{auth, config, ddns, index, peaks, playlist, scanner, thumbnail, App}, app::{auth, config, ddns, index, peaks, playlist, scanner, thumbnail, App},
@ -22,11 +23,11 @@ use crate::{
use super::auth::{AdminRights, Auth}; use super::auth::{AdminRights, Auth};
pub fn router() -> Router<App> { pub fn router() -> OpenApiRouter<App> {
Router::new() OpenApiRouter::new()
// Basic // Basic
.route("/version", get(get_version)) .routes(routes!(get_version))
.route("/initial_setup", get(get_initial_setup)) .routes(routes!(get_initial_setup))
.route("/auth", post(post_auth)) .route("/auth", post(post_auth))
// Configuration // Configuration
.route("/settings", get(get_settings)) .route("/settings", get(get_settings))
@ -77,6 +78,13 @@ pub fn router() -> Router<App> {
.route("/audio/{*path}", get(get_audio)) .route("/audio/{*path}", get(get_audio))
} }
#[utoipa::path(
get,
path = "/version",
responses(
(status = 200, body = dto::Version),
),
)]
async fn get_version() -> Json<dto::Version> { async fn get_version() -> Json<dto::Version> {
let current_version = dto::Version { let current_version = dto::Version {
major: API_MAJOR_VERSION, major: API_MAJOR_VERSION,

View file

@ -29,7 +29,6 @@ impl TestService for AxumTestService {
#[cfg(unix)] #[cfg(unix)]
pid_file_path: output_dir.join("polaris.pid"), pid_file_path: output_dir.join("polaris.pid"),
log_file_path: None, log_file_path: None,
swagger_dir_path: ["docs", "swagger"].iter().collect(),
web_dir_path: ["test-data", "web"].iter().collect(), web_dir_path: ["test-data", "web"].iter().collect(),
}; };

View file

@ -4,7 +4,7 @@ use utoipa::ToSchema;
use crate::app::{config, index, peaks, playlist, scanner, thumbnail}; use crate::app::{config, index, peaks, playlist, scanner, thumbnail};
use std::{collections::HashMap, convert::From, path::PathBuf, time::UNIX_EPOCH}; use std::{collections::HashMap, convert::From, path::PathBuf, time::UNIX_EPOCH};
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)] #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, ToSchema)]
pub struct Version { pub struct Version {
pub major: i32, pub major: i32,
pub minor: i32, pub minor: i32,

View file

@ -14,11 +14,11 @@ mod admin;
mod auth; mod auth;
mod browser; mod browser;
mod collection; mod collection;
mod docs;
mod media; mod media;
mod playlist; mod playlist;
mod search; mod search;
mod settings; mod settings;
mod swagger;
mod user; mod user;
mod web; mod web;

View file

@ -4,17 +4,17 @@ use crate::server::test::{add_trailing_slash, protocol, ServiceType, TestService
use crate::test_name; use crate::test_name;
#[tokio::test] #[tokio::test]
async fn can_get_swagger_index() { async fn can_get_docs_index() {
let mut service = ServiceType::new(&test_name!()).await; let mut service = ServiceType::new(&test_name!()).await;
let request = protocol::swagger_index(); let request = protocol::docs_index();
let response = service.fetch(&request).await; let response = service.fetch(&request).await;
assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.status(), StatusCode::OK);
} }
#[tokio::test] #[tokio::test]
async fn can_get_swagger_index_with_trailing_slash() { async fn can_get_docs_index_with_trailing_slash() {
let mut service = ServiceType::new(&test_name!()).await; let mut service = ServiceType::new(&test_name!()).await;
let mut request = protocol::swagger_index(); let mut request = protocol::docs_index();
add_trailing_slash(&mut request); add_trailing_slash(&mut request);
let response = service.fetch(&request).await; let response = service.fetch(&request).await;
assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.status(), StatusCode::OK);

View file

@ -32,10 +32,10 @@ pub fn web_index() -> Request<()> {
.unwrap() .unwrap()
} }
pub fn swagger_index() -> Request<()> { pub fn docs_index() -> Request<()> {
Request::builder() Request::builder()
.method(Method::GET) .method(Method::GET)
.uri("/swagger") .uri("/docs")
.body(()) .body(())
.unwrap() .unwrap()
} }