Skip to content

Commit

Permalink
Add subscription matrix access to public API
Browse files Browse the repository at this point in the history
  • Loading branch information
GamePad64 committed Jan 7, 2025
1 parent b33f1b6 commit 3afc831
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 67 deletions.
70 changes: 61 additions & 9 deletions Cargo.lock

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

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ members = [
sea-orm = { version = "1.1.3", features = ["sqlx-sqlite", "sqlx-postgres", "sqlx-mysql", "runtime-tokio-native-tls", "macros"] }
reqwest = { version = "0.12.12", default-features = false, features = ["json", "native-tls", "native-tls-alpn", "charset", "http2", "multipart", "stream"] }
axum = { version = "0.8.1", features = ["macros"] }
axum-extra = { version = "0.10.0", features = ["typed-header"] }
clap = { version = "4.5.23", features = ["derive", "color", "usage", "env"] }
url = { version = "2.5.4", features = ["serde"] }
utoipa = { git = "https://github.com/juhaku/utoipa.git", features = ["axum_extras", "uuid"] }
utoipa-axum = { git = "https://github.com/juhaku/utoipa.git" }
utoipa-swagger-ui = { git = "https://github.com/juhaku/utoipa.git", features = ["axum", "vendored"] }
utoipa = { version = "5.3.1", features = ["axum_extras", "uuid"] }
utoipa-axum = "0.1.4"
utoipa-swagger-ui = { version = "8.1.1", features = ["axum", "vendored"] }
uuid = { version = "1.11.0", features = ["serde", "v7"] }

[workspace.dependencies.sea-orm-migration]
Expand Down
1 change: 1 addition & 0 deletions notifico-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ notifico-attachment = { path = "../notifico-attachment" }
anyhow = "1.0.95"
async-trait = "0.1.84"
axum = { workspace = true }
axum-extra = { workspace = true }
backoff = { version = "0.4.0", features = ["tokio"] }
clap = { workspace = true }
dotenvy = "0.15.7"
Expand Down
9 changes: 6 additions & 3 deletions notifico-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod public;

use crate::amqp::AmqpClient;
use crate::ingest::http::HttpIngestExtensions;
use crate::management::http::HttpManagenemtExtensions;
use crate::management::http::HttpManagementExtensions;
use crate::public::http::HttpPublicExtensions;
use clap::{Parser, Subcommand};
use notifico_attachment::AttachmentPlugin;
Expand Down Expand Up @@ -168,6 +168,8 @@ async fn main() {
templater_source.setup().await.unwrap();
subscription_controller.setup().await.unwrap();

let secret_key = Arc::new(SecretKey(args.secret_key.as_bytes().to_vec()));

// Spawn HTTP servers
if components.is_empty() || components.contains(COMPONENT_INGEST) {
info!("Starting HTTP ingest server on {}", args.ingest);
Expand All @@ -180,7 +182,7 @@ async fn main() {
info!("Starting HTTP public server on {}", args.public);
let ext = HttpPublicExtensions {
subscription_controller: subscription_controller.clone(),
secret_key: Arc::new(SecretKey(args.secret_key.as_bytes().to_vec())),
secret_key: secret_key.clone(),
};
public::http::start(args.public, ext).await;
}
Expand All @@ -198,7 +200,7 @@ async fn main() {
project_controller.setup().await.unwrap();

info!("Starting HTTP management server on {}", args.management);
let ext = HttpManagenemtExtensions {
let ext = HttpManagementExtensions {
recipient_controller,
contact_controller,
project_controller,
Expand All @@ -207,6 +209,7 @@ async fn main() {
template_controller: templater_source.clone(),
event_controller,
group_controller,
secret_key: secret_key.clone(),
};

management::http::start(args.management, ext).await;
Expand Down
6 changes: 4 additions & 2 deletions notifico-app/src/management/http/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::management::http::HttpManagenemtExtensions;
use crate::management::http::HttpManagementExtensions;
use axum::{Extension, Router};
use tower_http::cors::CorsLayer;
use utoipa::OpenApi;
Expand All @@ -19,7 +19,7 @@ mod template;
#[openapi(info(title = "Notifico Management API", version = "0.1.0"))]
struct ApiDoc;

pub(crate) fn get_router(ext: HttpManagenemtExtensions) -> Router {
pub(crate) fn get_router(ext: HttpManagementExtensions) -> Router {
let router = OpenApiRouter::with_openapi(ApiDoc::openapi())
// Subscriptions
.routes(routes!(subscription::list))
Expand All @@ -31,6 +31,7 @@ pub(crate) fn get_router(ext: HttpManagenemtExtensions) -> Router {
recipients::update,
recipients::delete
))
.routes(routes!(recipients::token))
// Contacts
.routes(routes!(contacts::list, contacts::create))
.routes(routes!(contacts::get, contacts::update, contacts::delete))
Expand Down Expand Up @@ -58,6 +59,7 @@ pub(crate) fn get_router(ext: HttpManagenemtExtensions) -> Router {
.layer(Extension(ext.contact_controller))
.layer(Extension(ext.event_controller))
.layer(Extension(ext.group_controller))
.layer(Extension(ext.secret_key))
.layer(CorsLayer::permissive());

let (router, api) = router.split_for_parts();
Expand Down
32 changes: 32 additions & 0 deletions notifico-app/src/management/http/api/recipients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ use axum::extract::{Path, Query};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::{Extension, Json};
use jsonwebtoken::{EncodingKey, Header};
use notifico_core::http::admin::{
AdminCrudTable, ItemWithId, ListQueryParams, ReactAdminListQueryParams, RefineListQueryParams,
};
use notifico_core::http::auth::Claims;
use notifico_core::http::SecretKey;
use notifico_subscription::controllers::recipient::{RecipientDbController, RecipientItem};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use utoipa::ToSchema;
use uuid::Uuid;

Expand Down Expand Up @@ -99,3 +103,31 @@ pub async fn delete(
controller.delete(id).await.unwrap();
StatusCode::NO_CONTENT
}

// Additional endpoints

/// Issues a general-purpose JWT token for a recipient.
/// This token can be used for authorizing requests to public API endpoints.
#[utoipa::path(get, path = "/v1/recipients/{id}/token")]
pub async fn token(
Path((id,)): Path<(Uuid,)>,
Extension(secret_key): Extension<Arc<SecretKey>>,
) -> impl IntoResponse {
let claims = Claims::General {
recipient_id: id,
exp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 60 * 60 * 24 * 30, // TODO: Move this into a configuration option
};

let token = jsonwebtoken::encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(&secret_key.0),
)
.unwrap();

(StatusCode::OK, Json(json!({"token": token})))
}
6 changes: 4 additions & 2 deletions notifico-app/src/management/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use axum::http::header::CONTENT_TYPE;
use axum::http::{StatusCode, Uri};
use axum::response::{Html, IntoResponse, Response};
use axum::Router;
use notifico_core::http::SecretKey;
use notifico_dbpipeline::controllers::event::EventDbController;
use notifico_dbpipeline::controllers::pipeline::PipelineDbController;
use notifico_project::ProjectController;
Expand All @@ -18,7 +19,7 @@ use std::sync::Arc;
use tokio::net::TcpListener;

#[derive(Clone)]
pub(crate) struct HttpManagenemtExtensions {
pub(crate) struct HttpManagementExtensions {
pub recipient_controller: Arc<RecipientDbController>,
pub contact_controller: Arc<ContactDbController>,
pub subscription_controller: Arc<SubscriptionDbController>,
Expand All @@ -27,13 +28,14 @@ pub(crate) struct HttpManagenemtExtensions {
pub template_controller: Arc<DbTemplateSource>,
pub event_controller: Arc<EventDbController>,
pub group_controller: Arc<GroupDbController>,
pub secret_key: Arc<SecretKey>,
}

#[derive(Embed)]
#[folder = "assets/"]
struct Assets;

pub(crate) async fn start(bind: SocketAddr, ext: HttpManagenemtExtensions) {
pub(crate) async fn start(bind: SocketAddr, ext: HttpManagementExtensions) {
// Bind everything now to catch any errors before spinning up the coroutines
let service_listener = TcpListener::bind(bind).await.unwrap();

Expand Down
Loading

0 comments on commit 3afc831

Please sign in to comment.