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
8 changes: 4 additions & 4 deletions apps/frontend/src/pages/admin/analytics/events.vue
Original file line number Diff line number Diff line change
Expand Up @@ -485,9 +485,9 @@ async function saveEvent() {
const payload = buildEventPayload()

if (modalMode.value === 'edit' && editingEventId.value !== null) {
await client.labrinth.analytics_v3.editEvent(editingEventId.value, payload)
await client.labrinth.analytics_internal.editEvent(editingEventId.value, payload)
} else {
await client.labrinth.analytics_v3.createEvent(payload)
await client.labrinth.analytics_internal.createEvent(payload)
}

await queryClient.invalidateQueries({ queryKey: analyticsEventsQueryKey })
Expand Down Expand Up @@ -528,7 +528,7 @@ async function deleteEvent(eventId: Labrinth.Analytics.v3.AnalyticsEventId) {
setDeletingEvent(eventId, true)

try {
await client.labrinth.analytics_v3.deleteEvent(eventId)
await client.labrinth.analytics_internal.deleteEvent(eventId)
await queryClient.invalidateQueries({ queryKey: analyticsEventsQueryKey })
addNotification({
title: 'Analytics event deleted',
Expand Down Expand Up @@ -581,7 +581,7 @@ function commitAnnouncementUrl() {
committedAnnouncementUrl.value = form.value.announcementUrl
}

function buildEventPayload(): Labrinth.Analytics.v3.AnalyticsEventUpsert {
function buildEventPayload(): Labrinth.Analytics.Internal.AnalyticsEventUpsert {
const selectedRange = getEventFormDateRange()
if (!selectedRange) {
throw new Error('Select a valid start and end date')
Expand Down
192 changes: 192 additions & 0 deletions apps/labrinth/src/routes/internal/analytics_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use actix_web::{HttpRequest, delete, patch, post, web};
use chrono::{DateTime, Utc};
use eyre::eyre;
use serde::{Deserialize, Serialize};
use xredis::RedisPool;

use crate::{
auth::get_user_from_headers,
database::{
PgPool,
models::{
DBAnalyticsEvent, DBAnalyticsEventId, generate_analytics_event_id,
},
},
models::{
ids::AnalyticsEventId,
pats::Scopes,
v3::analytics_event::{AnalyticsEvent, AnalyticsEventMeta},
},
queue::session::AuthQueue,
routes::ApiError,
util::error::Context,
};

pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
cfg.service(analytics_event_create)
.service(analytics_event_edit)
.service(analytics_event_delete);
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct AnalyticsEventUpsert {
#[serde(flatten)]
pub meta: AnalyticsEventMeta,
pub starts: DateTime<Utc>,
pub ends: DateTime<Utc>,
}

/// Create an analytics event.
#[utoipa::path(
context_path = "/analytics-event",
tag = "analytics events", responses((status = OK, body = AnalyticsEvent))
)]
#[post("")]
pub async fn analytics_event_create(
req: HttpRequest,
event: web::Json<AnalyticsEventUpsert>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
session_queue: web::Data<AuthQueue>,
) -> Result<web::Json<AnalyticsEvent>, ApiError> {
let user = get_user_from_headers(
&req,
&**pool,
&redis,
&session_queue,
Scopes::empty(),
)
.await?
.1;

if !user.role.is_admin() {
return Err(ApiError::Auth(eyre!(
"you do not have permission to manage analytics events"
)));
}

let mut transaction = pool
.begin()
.await
.wrap_internal_err("failed to begin transaction")?;
let id = generate_analytics_event_id(&mut transaction)
.await
.wrap_internal_err("failed to generate analytics event ID")?;

let event = DBAnalyticsEvent {
id,
meta: event.meta.clone(),
starts: event.starts,
ends: event.ends,
};
event
.insert(&mut transaction)
.await
.wrap_internal_err("failed to insert analytics event")?;

transaction
.commit()
.await
.wrap_internal_err("failed to commit transaction")?;
DBAnalyticsEvent::clear_cache(&redis)
.await
.wrap_internal_err("failed to clear analytics event cache")?;

Ok(web::Json(event.into()))
}

/// Update an analytics event.
#[utoipa::path(
context_path = "/analytics-event",
tag = "analytics events", responses((status = OK, body = AnalyticsEvent))
)]
#[patch("/{id}")]
pub async fn analytics_event_edit(
req: HttpRequest,
id: web::Path<(AnalyticsEventId,)>,
event: web::Json<AnalyticsEventUpsert>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
session_queue: web::Data<AuthQueue>,
) -> Result<web::Json<AnalyticsEvent>, ApiError> {
let user = get_user_from_headers(
&req,
&**pool,
&redis,
&session_queue,
Scopes::empty(),
)
.await?
.1;

if !user.role.is_admin() {
return Err(ApiError::Auth(eyre!(
"you do not have permission to manage analytics events"
)));
}

let event = DBAnalyticsEvent {
id: DBAnalyticsEventId::from(id.into_inner().0),
meta: event.meta.clone(),
starts: event.starts,
ends: event.ends,
};

let updated = event
.update(&**pool)
.await
.wrap_internal_err("failed to update analytics event")?;
if !updated {
return Err(ApiError::NotFound);
}
DBAnalyticsEvent::clear_cache(&redis)
.await
.wrap_internal_err("failed to clear analytics event cache")?;

Ok(web::Json(event.into()))
}

/// Delete an analytics event.
#[utoipa::path(
context_path = "/analytics-event",
tag = "analytics events", responses((status = NO_CONTENT))
)]
#[delete("/{id}")]
pub async fn analytics_event_delete(
req: HttpRequest,
id: web::Path<(AnalyticsEventId,)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
session_queue: web::Data<AuthQueue>,
) -> Result<(), ApiError> {
let user = get_user_from_headers(
&req,
&**pool,
&redis,
&session_queue,
Scopes::empty(),
)
.await?
.1;

if !user.role.is_admin() {
return Err(ApiError::Auth(eyre!(
"you do not have permission to manage analytics events"
)));
}

let deleted = DBAnalyticsEvent::remove(
DBAnalyticsEventId::from(id.into_inner().0),
&**pool,
)
.await
.wrap_internal_err("failed to delete analytics event")?;
if !deleted {
return Err(ApiError::NotFound);
}
DBAnalyticsEvent::clear_cache(&redis)
.await
.wrap_internal_err("failed to clear analytics event cache")?;

Ok(())
}
17 changes: 8 additions & 9 deletions apps/labrinth/src/routes/internal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod admin;
pub mod affiliate;
pub mod analytics_event;
pub mod attribution;
pub mod billing;
pub mod blocked_users;
Expand Down Expand Up @@ -35,6 +36,10 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.configure(flows::config)
.configure(pats::config)
.configure(oauth_clients::config)
.service(
web::scope("/analytics-event")
.configure(analytics_event::config),
)
.service(web::scope("/moderation").configure(moderation::config))
.service(web::scope("/affiliate").configure(affiliate::config))
.service(web::scope("/campaign").configure(campaign::config))
Expand All @@ -50,11 +55,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.configure(medal::config)
.configure(mural::config)
.configure(statuses::config),
)
.service(
web::scope("/v3/analytics-event")
.wrap(default_cors())
.configure(super::v3::analytics_event::config),
);
}

Expand Down Expand Up @@ -180,10 +180,9 @@ pub fn config(cfg: &mut web::ServiceConfig) {
medal::redeem,
mural::get_bank_details,
statuses::ws_init,
super::v3::analytics_event::analytics_events_get,
super::v3::analytics_event::analytics_event_create,
super::v3::analytics_event::analytics_event_edit,
super::v3::analytics_event::analytics_event_delete,
analytics_event::analytics_event_create,
analytics_event::analytics_event_edit,
analytics_event::analytics_event_delete,
),
modifiers(&InternalPathModifier, &SecurityAddon)
)]
Expand Down
Loading
Loading