Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 127 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ log4rs = { version = "1.3.0", features = [
] }
thiserror = "2.0.12"
moka = { version = "0.12.13", features = ["future"] }
utoipa = { version = "5.4.0", features = ["actix_extras", "chrono", "uuid"] }
utoipa-swagger-ui = { version = "9.0.2", features = ["actix-web"] }
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
# Geode Server

The new home of the Geode Index, hosted on its own server rather than a Github repository. Uses Actix, PostgreSQL and SQLX in Rust.
Server for the Geode Index, the API used by Geode SDK to retrieve mod information. Based on actix-web and sqlx.

The API documentation can be found [here](https://geode-sdk.github.io/server/)
## Requirements for hosting

## How do I contribute?
- rust stable
- PostgreSQL 14 or later

Check out the [development environment setup](docs/dev_setup.md)
## Documentation

The API documentation can be found [here](https://api.geode-sdk.org/swagger/). A machine readable openapi.json specification can be found [here](https://api.geode-sdk.org/swagger/openapi.json).

## Configuration

Check out the [development environment setup](docs/dev_setup.md) to get started!

## Building the server

```bash
cargo build # or cargo build --release
```
60 changes: 57 additions & 3 deletions src/endpoints/auth/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use actix_web::{dev::ConnectionInfo, post, web, HttpResponse, Responder};
use serde::Deserialize;
use sqlx::{types::ipnetwork::IpNetwork, Acquire};
use uuid::Uuid;
use utoipa::ToSchema;

use crate::config::AppData;
use crate::database::repository::{
Expand All @@ -12,23 +13,32 @@ use crate::endpoints::auth::TokensResponse;
use crate::endpoints::ApiError;
use crate::{auth::github, types::api::ApiResponse};

#[derive(Deserialize)]
#[derive(Deserialize, ToSchema)]
struct PollParams {
uuid: String,
expiry: Option<bool>,
}

#[derive(Deserialize)]
#[derive(Deserialize, ToSchema)]
struct TokenLoginParams {
token: String,
}

#[derive(Deserialize)]
#[derive(Deserialize, ToSchema)]
struct CallbackParams {
code: String,
state: String,
}

/// Start GitHub device flow authentication
#[utoipa::path(
post,
path = "/v1/login/github",
tag = "auth",
responses(
(status = 200, description = "Device flow started", body = inline(ApiResponse<String>))
)
)]
#[post("v1/login/github")]
pub async fn start_github_login(
data: web::Data<AppData>,
Expand Down Expand Up @@ -57,6 +67,15 @@ pub async fn start_github_login(
}))
}

/// Start GitHub web OAuth flow
#[utoipa::path(
post,
path = "/v1/login/github/web",
tag = "auth",
responses(
(status = 200, description = "OAuth URL generated", body = inline(ApiResponse<String>))
)
)]
#[post("v1/login/github/web")]
pub async fn start_github_web_login(data: web::Data<AppData>) -> Result<impl Responder, ApiError> {
let mut pool = data.db().acquire().await?;
Expand All @@ -74,6 +93,18 @@ pub async fn start_github_web_login(data: web::Data<AppData>) -> Result<impl Res
}))
}

/// Handle GitHub OAuth callback
#[utoipa::path(
post,
path = "/v1/login/github/callback",
tag = "auth",
request_body = CallbackParams,
responses(
(status = 200, description = "Login successful", body = inline(ApiResponse<TokensResponse>)),
(status = 400, description = "Bad request"),
(status = 404, description = "Invalid secret")
)
)]
#[post("v1/login/github/callback")]
pub async fn github_web_callback(
json: web::Json<CallbackParams>,
Expand Down Expand Up @@ -119,6 +150,18 @@ pub async fn github_web_callback(
}))
}

/// Poll GitHub device flow for authentication
#[utoipa::path(
post,
path = "/v1/login/github/poll",
tag = "auth",
request_body = PollParams,
responses(
(status = 200, description = "Login successful", body = inline(ApiResponse<TokensResponse>)),
(status = 400, description = "Bad request or too fast"),
(status = 404, description = "Login attempt not found")
)
)]
#[post("v1/login/github/poll")]
pub async fn poll_github_login(
json: web::Json<PollParams>,
Expand Down Expand Up @@ -212,6 +255,17 @@ pub async fn poll_github_login(
}
}

/// Login using a GitHub personal access token
#[utoipa::path(
post,
path = "/v1/login/github/token",
tag = "auth",
request_body = TokenLoginParams,
responses(
(status = 200, description = "Login successful", body = inline(ApiResponse<String>)),
(status = 400, description = "Invalid access token")
)
)]
#[post("v1/login/github/token")]
pub async fn github_token_login(
json: web::Json<TokenLoginParams>,
Expand Down
16 changes: 14 additions & 2 deletions src/endpoints/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,32 @@ use actix_web::{post, web, Responder};
use serde::{Deserialize, Serialize};
use sqlx::Acquire;
use uuid::Uuid;
use utoipa::ToSchema;

pub mod github;

#[derive(Serialize)]
#[derive(Serialize, ToSchema)]
struct TokensResponse {
access_token: String,
refresh_token: String,
}

#[derive(Deserialize)]
#[derive(Deserialize, ToSchema)]
struct RefreshBody {
refresh_token: String,
}

/// Refresh an access token using a refresh token
#[utoipa::path(
post,
path = "/v1/login/refresh",
tag = "auth",
request_body = RefreshBody,
responses(
(status = 200, description = "New tokens generated", body = inline(ApiResponse<TokensResponse>)),
(status = 400, description = "Invalid or expired refresh token")
)
)]
#[post("v1/login/refresh")]
pub async fn refresh_token(
json: web::Json<RefreshBody>,
Expand Down
Loading