mirror of
https://git.dirksys.ovh/dirk/bankserver.git
synced 2025-12-20 02:59:20 +01:00
implement user info, user balance and list_users endpoint
This commit is contained in:
parent
9a52d120bf
commit
01629f00b5
@ -65,7 +65,7 @@ jobs:
|
|||||||
done
|
done
|
||||||
echo "Pushing manifest"
|
echo "Pushing manifest"
|
||||||
podman login --get-login git.dirksys.ovh
|
podman login --get-login git.dirksys.ovh
|
||||||
podman --log-level debug manifest push git.dirksys.ovh/dirk/bankserver:latest
|
podman manifest push git.dirksys.ovh/dirk/bankserver:latest
|
||||||
- name: Notify server
|
- name: Notify server
|
||||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
|
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
|
||||||
env:
|
env:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
/result-*
|
/result-*
|
||||||
/result
|
/result
|
||||||
/schemas
|
/schemas
|
||||||
|
openapi.json
|
||||||
|
|||||||
2
justfile
2
justfile
@ -4,7 +4,7 @@ default:
|
|||||||
|
|
||||||
dev:
|
dev:
|
||||||
just openapi
|
just openapi
|
||||||
cargo run
|
cargo run --bin bankserver
|
||||||
|
|
||||||
openapi:
|
openapi:
|
||||||
yq eval-all -n 'load("openapi-def.yaml") *n load("schemas/schemas.json")' > openapi-temp.yaml
|
yq eval-all -n 'load("openapi-def.yaml") *n load("schemas/schemas.json")' > openapi-temp.yaml
|
||||||
|
|||||||
@ -74,6 +74,8 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/UserInfo'
|
$ref: '#/components/schemas/UserInfo'
|
||||||
|
404:
|
||||||
|
$ref: '#/components/responses/ResourceNotFound'
|
||||||
401:
|
401:
|
||||||
$ref: '#/components/responses/Unauthorized'
|
$ref: '#/components/responses/Unauthorized'
|
||||||
default:
|
default:
|
||||||
@ -289,6 +291,15 @@ components:
|
|||||||
value:
|
value:
|
||||||
id: auth.jwt.invalid
|
id: auth.jwt.invalid
|
||||||
message: string
|
message: string
|
||||||
|
ResourceNotFound:
|
||||||
|
description: Resource not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ApiError'
|
||||||
|
example:
|
||||||
|
id: not-found
|
||||||
|
message: Not found
|
||||||
UnprocessableEntity:
|
UnprocessableEntity:
|
||||||
description: Unprocessable Entity
|
description: Unprocessable Entity
|
||||||
content:
|
content:
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -6,7 +6,7 @@ use std::{
|
|||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
Router,
|
Router,
|
||||||
extract::FromRequestParts,
|
extract::{FromRef, FromRequestParts},
|
||||||
http::{StatusCode, request::Parts},
|
http::{StatusCode, request::Parts},
|
||||||
routing::post,
|
routing::post,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,9 +14,11 @@ static OPENAPI_JSON: &'static str = include_str!("../../openapi.json");
|
|||||||
static SCALAR_DOCS: &'static str = include_str!("./docs/scalar.html");
|
static SCALAR_DOCS: &'static str = include_str!("./docs/scalar.html");
|
||||||
static SWAGGER_DOCS: &'static str = include_str!("./docs/swagger.html");
|
static SWAGGER_DOCS: &'static str = include_str!("./docs/swagger.html");
|
||||||
static RAPIDOC_DOCS: &'static str = include_str!("./docs/rapidoc.html");
|
static RAPIDOC_DOCS: &'static str = include_str!("./docs/rapidoc.html");
|
||||||
|
static INDEX: &'static str = include_str!("./docs/index.html");
|
||||||
|
|
||||||
pub(super) fn router() -> Router<Arc<AppState>> {
|
pub(super) fn router() -> Router<Arc<AppState>> {
|
||||||
Router::new()
|
Router::new()
|
||||||
|
.route("/", get(index_html))
|
||||||
.route("/openapi.json", get(openapi_json))
|
.route("/openapi.json", get(openapi_json))
|
||||||
.route("/scalar", get(scalar_html))
|
.route("/scalar", get(scalar_html))
|
||||||
.route("/swagger", get(swagger_html))
|
.route("/swagger", get(swagger_html))
|
||||||
@ -44,3 +46,6 @@ async fn swagger_html() -> Html<&'static str> {
|
|||||||
async fn rapidoc_html() -> Html<&'static str> {
|
async fn rapidoc_html() -> Html<&'static str> {
|
||||||
Html(RAPIDOC_DOCS)
|
Html(RAPIDOC_DOCS)
|
||||||
}
|
}
|
||||||
|
async fn index_html() -> Html<&'static str> {
|
||||||
|
Html(INDEX)
|
||||||
|
}
|
||||||
|
|||||||
25
src/api/docs/index.html
Normal file
25
src/api/docs/index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>API Reference</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
font-family: sans-serif;
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li><a href="./docs/swagger">Swagger</a></li>
|
||||||
|
<li><a href="./docs/scalar">Scalar</a></li>
|
||||||
|
<li><a href="./docs/rapidoc">Rapidoc</a></li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -175,7 +175,7 @@ impl IntoResponse for InnerError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static INTERNAL_SERVER_ERROR: ApiError<'static> = ApiError {
|
pub static INTERNAL_SERVER_ERROR: ApiError<'static> = ApiError {
|
||||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
id: Cow::Borrowed("internal_server_error"),
|
id: Cow::Borrowed("internal_server_error"),
|
||||||
message: Cow::Borrowed("Internal Server Error"),
|
message: Cow::Borrowed("Internal Server Error"),
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum::{Router, extract::Path, routing::get};
|
use axum::{Router, extract::Path, http::StatusCode, routing::get};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::model::{UserAccountInfo, UserInfo};
|
use crate::{
|
||||||
|
api::ApiError,
|
||||||
|
model::{Account, User, UserAccountInfo, UserInfo},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{AppState, EState, State, auth::Auth, make_schemas};
|
use super::{
|
||||||
|
AppState, EState, Error, INTERNAL_SERVER_ERROR, Json, State, auth::Auth, make_schemas,
|
||||||
|
};
|
||||||
|
|
||||||
pub(super) fn router() -> Router<Arc<AppState>> {
|
pub(super) fn router() -> Router<Arc<AppState>> {
|
||||||
Router::new()
|
Router::new()
|
||||||
@ -52,8 +57,30 @@ pub struct UserBalance {
|
|||||||
pub balance: u64,
|
pub balance: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user_info(EState(state): State, auth: Auth, Path(target): Path<UserTarget>) {
|
pub async fn user_info(
|
||||||
|
EState(state): State,
|
||||||
|
auth: Auth,
|
||||||
|
Path(target): Path<UserTarget>,
|
||||||
|
) -> Result<Json<UserInfo>, Error> {
|
||||||
let user = target.user_id(&auth);
|
let user = target.user_id(&auth);
|
||||||
|
let conn = state.conn().await?;
|
||||||
|
let info = User::info(&conn, user).await?;
|
||||||
|
if let Some(info) = info {
|
||||||
|
return Ok(Json(info));
|
||||||
|
}
|
||||||
|
if matches!(target, UserTarget::Me) {
|
||||||
|
return Err(INTERNAL_SERVER_ERROR.clone().into());
|
||||||
|
}
|
||||||
|
Err(ApiError::const_new(StatusCode::NOT_FOUND, "not.found", "Not found").into())
|
||||||
|
}
|
||||||
|
pub async fn user_balance(EState(state): State, auth: Auth) -> Result<Json<UserBalance>, Error> {
|
||||||
|
let conn = state.conn().await?;
|
||||||
|
let info = Account::list_for_user(&conn, auth.user_id()).await?;
|
||||||
|
let balance = info.iter().map(|info| info.balance).sum();
|
||||||
|
Ok(Json(UserBalance { balance }))
|
||||||
|
}
|
||||||
|
pub async fn list_users(EState(state): State, _: Auth) -> Result<Json<ListUsers>, Error> {
|
||||||
|
let conn = state.conn().await?;
|
||||||
|
let users = User::list(&conn).await?;
|
||||||
|
Ok(Json(ListUsers { users }))
|
||||||
}
|
}
|
||||||
pub async fn user_balance(EState(state): State, auth: Auth) {}
|
|
||||||
pub async fn list_users(EState(state): State, _: Auth) {}
|
|
||||||
|
|||||||
@ -93,4 +93,16 @@ impl User {
|
|||||||
.collect();
|
.collect();
|
||||||
Ok(users)
|
Ok(users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(client))]
|
||||||
|
pub async fn info(
|
||||||
|
client: &impl GenericClient,
|
||||||
|
id: Uuid,
|
||||||
|
) -> Result<Option<UserInfo>, tokio_postgres::Error> {
|
||||||
|
let stmt = client
|
||||||
|
.prepare_cached("select id,name from users where id = $1")
|
||||||
|
.await?;
|
||||||
|
let info = client.query_opt(&stmt, &[]).await?.map(UserInfo::from);
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user