Skip to content

Commit cd28c72

Browse files
committed
chore: reduce user login requirements
1 parent ec3fb3d commit cd28c72

File tree

17 files changed

+385
-234
lines changed

17 files changed

+385
-234
lines changed

api/src/discord.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use twilight_model::channel::message::Component;
1111
use twilight_model::guild::{Guild, Member, Role};
1212
use twilight_model::id::Id;
1313
use twilight_model::id::marker::{ChannelMarker, GuildMarker, MessageMarker, RoleMarker, UserMarker};
14-
use twilight_model::user::{CurrentUser, CurrentUserGuild};
14+
use twilight_model::user::{CurrentUser, CurrentUserGuild, User};
1515

1616
pub fn ise<T: std::fmt::Debug>(e: T) -> StatusCode {
1717
error!("Internal Server Error: {:?}", e);
@@ -85,6 +85,11 @@ pub async fn get_current_user(client: &Client) -> Result<CurrentUser,StatusCode>
8585
retry_on_rl(|| async { client.current_user().await}).await.map_err(as_http_err)?.model().await.map_err(ise)
8686
}
8787

88+
#[cached(time = 3600, key = "String", convert = r##"{ format!("{user_id}{:?}", client.token().unwrap()) }"##)]
89+
pub async fn get_user(user_id: Id<UserMarker>, client: &Client) -> Result<User,StatusCode> {
90+
retry_on_rl(|| async { client.user(user_id).await}).await.map_err(as_http_err)?.model().await.map_err(ise)
91+
}
92+
8893
#[cached(time = 60, key = "String", convert = r##"{ format!("{guild_id}{:?}", client.token().unwrap()) }"##)]
8994
pub async fn get_guild(guild_id: Id<GuildMarker>, client: &Client) -> Result<Guild,StatusCode> {
9095
retry_on_rl(|| async { client.guild(guild_id).await}).await.map_err(as_http_err)?.model().await.map_err(ise)

api/src/meta/guilds.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use std::ops::{Add, Sub};
2+
use std::sync::Arc;
3+
use std::time::Duration;
4+
use axum::{Extension, Json, Router};
5+
use axum::extract::{Path, State};
6+
use axum::routing::get;
7+
use http::StatusCode;
8+
use lambda_http::tracing::{debug, error, info, warn};
9+
use serde::{Deserialize, Serialize};
10+
use serde_json::{json, Value};
11+
use tokio::time::{sleep, Instant};
12+
use tower_http::cors::CorsLayer;
13+
use twilight_http::Client;
14+
use twilight_model::channel::Channel;
15+
use twilight_model::guild::{Permissions, Role};
16+
use twilight_model::id::Id;
17+
use twilight_model::id::marker::GuildMarker;
18+
use twilight_model::user::CurrentUserGuild;
19+
use twilight_model::util::ImageHash;
20+
use crate::AppState;
21+
use crate::discord::{get_current_user_guild, get_current_user_guilds_prime_cache, get_guild, get_guild_channels, get_guild_prime_cache};
22+
use crate::utils::member_guilds;
23+
24+
pub fn router() -> Router<AppState> {
25+
Router::new()
26+
.route("/", get(get_meta_guilds))
27+
.route("/{guild_id}", get(get_meta_guilds_id))
28+
.layer(CorsLayer::permissive())
29+
}
30+
31+
pub fn setup(discord_bot: Arc<Client>) {
32+
info!("Spawning meta cache refresh task");
33+
if false {
34+
tokio::spawn(refresh_meta_cache(discord_bot));
35+
}
36+
}
37+
38+
39+
#[derive(Debug, Serialize, Deserialize)]
40+
struct PartialGuildMeta {
41+
id: Id<GuildMarker>,
42+
name: String,
43+
icon: Option<ImageHash>,
44+
is_admin: bool,
45+
}
46+
impl From<CurrentUserGuild> for PartialGuildMeta {
47+
fn from(guild: CurrentUserGuild) -> Self {
48+
PartialGuildMeta {
49+
id: guild.id,
50+
name: guild.name,
51+
icon: guild.icon,
52+
is_admin: guild.owner || guild.permissions & Permissions::ADMINISTRATOR == Permissions::ADMINISTRATOR,
53+
}
54+
}
55+
}
56+
57+
58+
#[derive(Debug, Serialize, Deserialize)]
59+
struct GuildMeta {
60+
id: Id<GuildMarker>,
61+
name: String,
62+
icon: Option<ImageHash>,
63+
is_admin: bool,
64+
roles: Vec<Role>,
65+
channels: Vec<Channel>,
66+
}
67+
68+
async fn get_meta_guilds(
69+
Extension(discord_user): Extension<Arc<Client>>,
70+
State(app_state): State<AppState>,
71+
) -> Result<Json<Value>, StatusCode> {
72+
Ok(Json(json!(
73+
member_guilds(&discord_user, &app_state.discord_bot).await?
74+
.into_iter().map(PartialGuildMeta::from).collect::<Vec<PartialGuildMeta>>()
75+
)))
76+
}
77+
78+
async fn get_meta_guilds_id(
79+
Path(guild_id): Path<Id<GuildMarker>>,
80+
Extension(discord_user): Extension<Arc<Client>>,
81+
State(app_state): State<AppState>,
82+
) -> Result<Json<Value>, StatusCode> {
83+
if !crate::guilds::utils::is_intersect_admin_guild(guild_id, &discord_user, &app_state.discord_bot).await? {
84+
warn!("User is not an admin in guild {}", guild_id);
85+
return Err(StatusCode::NOT_FOUND);
86+
}
87+
88+
let u_guild = get_current_user_guild(guild_id, &discord_user).await?;
89+
let guild = get_guild(guild_id, &app_state.discord_bot).await?;
90+
let channels = get_guild_channels(guild_id, &app_state.discord_bot).await?;
91+
92+
Ok(Json(json!(
93+
GuildMeta{
94+
id: guild_id,
95+
name: guild.name,
96+
icon: guild.icon,
97+
is_admin: u_guild.owner || u_guild.permissions & Permissions::ADMINISTRATOR == Permissions::ADMINISTRATOR,
98+
roles: guild.roles,
99+
channels
100+
}
101+
)))
102+
}
103+
104+
105+
106+
async fn refresh_meta_cache(discord_bot: Arc<Client>) {
107+
loop {
108+
debug!("Refreshing meta cache");
109+
let time = Instant::now();
110+
match get_current_user_guilds_prime_cache(&discord_bot).await {
111+
Ok(guilds) => {
112+
debug!("Refreshing meta cache: {}", guilds.len());
113+
for guild in guilds {
114+
let _ = get_guild_prime_cache(guild.id, &discord_bot).await;
115+
}
116+
}
117+
Err(e) => {
118+
error!("refresh_meta_cache error: {:#?}", e);
119+
}
120+
}
121+
debug!("{:?} {:?} Waiting {} seconds",time, Instant::now(), (time.sub(Instant::now()).add(Duration::from_secs(50))).as_secs());
122+
sleep((time.sub(Instant::now())).add(Duration::from_secs(50))).await;
123+
}
124+
}

api/src/meta/mod.rs

Lines changed: 8 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,21 @@
1-
use std::ops::{Add, Sub};
21
use std::sync::Arc;
3-
use std::time::Duration;
4-
use axum::{Extension, Json, Router};
5-
use axum::extract::{Path, State};
6-
use axum::routing::get;
7-
use http::StatusCode;
8-
use lambda_http::tracing::{debug, error, info, warn};
9-
use serde::{Deserialize, Serialize};
10-
use serde_json::{json, Value};
11-
use tokio::time::{sleep, Instant};
2+
use axum::Router;
3+
use lambda_http::tracing::info;
124
use tower_http::cors::CorsLayer;
135
use twilight_http::Client;
14-
use twilight_model::channel::Channel;
15-
use twilight_model::guild::{Permissions, Role};
16-
use twilight_model::id::Id;
17-
use twilight_model::id::marker::GuildMarker;
18-
use twilight_model::user::CurrentUserGuild;
19-
use twilight_model::util::ImageHash;
206
use crate::AppState;
21-
use crate::discord::{get_current_user_guild, get_current_user_guilds_prime_cache, get_guild, get_guild_channels, get_guild_prime_cache};
22-
use crate::utils::member_guilds;
7+
8+
mod guilds;
9+
mod users;
2310

2411
pub fn router() -> Router<AppState> {
2512
Router::new()
26-
.route("/guilds", get(get_meta_guilds))
27-
.route("/guilds/{guild_id}", get(get_meta_guilds_id))
13+
.nest("/guilds", guilds::router())
14+
.nest("/users", users::router())
2815
.layer(CorsLayer::permissive())
2916
}
3017

3118
pub fn setup(discord_bot: Arc<Client>) {
3219
info!("Spawning meta cache refresh task");
33-
if false {
34-
tokio::spawn(refresh_meta_cache(discord_bot));
35-
}
36-
}
37-
38-
39-
#[derive(Debug, Serialize, Deserialize)]
40-
struct PartialGuildMeta {
41-
id: Id<GuildMarker>,
42-
name: String,
43-
icon: Option<ImageHash>,
44-
is_admin: bool,
45-
}
46-
impl From<CurrentUserGuild> for PartialGuildMeta {
47-
fn from(guild: CurrentUserGuild) -> Self {
48-
PartialGuildMeta {
49-
id: guild.id,
50-
name: guild.name,
51-
icon: guild.icon,
52-
is_admin: guild.owner || guild.permissions & Permissions::ADMINISTRATOR == Permissions::ADMINISTRATOR,
53-
}
54-
}
55-
}
56-
57-
58-
#[derive(Debug, Serialize, Deserialize)]
59-
struct GuildMeta {
60-
id: Id<GuildMarker>,
61-
name: String,
62-
icon: Option<ImageHash>,
63-
is_admin: bool,
64-
roles: Vec<Role>,
65-
channels: Vec<Channel>,
66-
}
67-
68-
async fn get_meta_guilds(
69-
Extension(discord_user): Extension<Arc<Client>>,
70-
State(app_state): State<AppState>,
71-
) -> Result<Json<Value>, StatusCode> {
72-
Ok(Json(json!(
73-
member_guilds(&discord_user, &app_state.discord_bot).await?
74-
.into_iter().map(PartialGuildMeta::from).collect::<Vec<PartialGuildMeta>>()
75-
)))
76-
}
77-
78-
async fn get_meta_guilds_id(
79-
Path(guild_id): Path<Id<GuildMarker>>,
80-
Extension(discord_user): Extension<Arc<Client>>,
81-
State(app_state): State<AppState>,
82-
) -> Result<Json<Value>, StatusCode> {
83-
if !crate::guilds::utils::is_intersect_admin_guild(guild_id, &discord_user, &app_state.discord_bot).await? {
84-
warn!("User is not an admin in guild {}", guild_id);
85-
return Err(StatusCode::NOT_FOUND);
86-
}
87-
88-
let u_guild = get_current_user_guild(guild_id, &discord_user).await?;
89-
let guild = get_guild(guild_id, &app_state.discord_bot).await?;
90-
let channels = get_guild_channels(guild_id, &app_state.discord_bot).await?;
91-
92-
Ok(Json(json!(
93-
GuildMeta{
94-
id: guild_id,
95-
name: guild.name,
96-
icon: guild.icon,
97-
is_admin: u_guild.owner || u_guild.permissions & Permissions::ADMINISTRATOR == Permissions::ADMINISTRATOR,
98-
roles: guild.roles,
99-
channels
100-
}
101-
)))
102-
}
103-
104-
105-
106-
async fn refresh_meta_cache(discord_bot: Arc<Client>) {
107-
loop {
108-
debug!("Refreshing meta cache");
109-
let time = Instant::now();
110-
match get_current_user_guilds_prime_cache(&discord_bot).await {
111-
Ok(guilds) => {
112-
debug!("Refreshing meta cache: {}", guilds.len());
113-
for guild in guilds {
114-
let _ = get_guild_prime_cache(guild.id, &discord_bot).await;
115-
}
116-
}
117-
Err(e) => {
118-
error!("refresh_meta_cache error: {:#?}", e);
119-
}
120-
}
121-
debug!("{:?} {:?} Waiting {} seconds",time, Instant::now(), (time.sub(Instant::now()).add(Duration::from_secs(50))).as_secs());
122-
sleep((time.sub(Instant::now())).add(Duration::from_secs(50))).await;
123-
}
20+
guilds::setup(discord_bot);
12421
}

api/src/meta/users.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use axum::extract::{Path, State};
2+
use axum::{Extension, Json, Router};
3+
use axum::routing::get;
4+
use http::StatusCode;
5+
use serde_json::{json, Value};
6+
use tower_http::cors::CorsLayer;
7+
use twilight_model::id::Id;
8+
use twilight_model::id::marker::UserMarker;
9+
use twilight_model::user::CurrentUser;
10+
use crate::AppState;
11+
use crate::discord::get_user;
12+
13+
pub fn router() -> Router<AppState> {
14+
Router::new()
15+
.route("/@me", get(get_meta_users_me))
16+
.route("/{user_id}", get(get_meta_users_id))
17+
.layer(CorsLayer::permissive())
18+
}
19+
20+
pub async fn get_meta_users_me(Extension(current_user): Extension<CurrentUser>,
21+
State(state): State<AppState>)
22+
-> Result<Json<Value>, StatusCode> {
23+
get_meta_users_id(Path(current_user.id), Extension(current_user), State(state)).await
24+
}
25+
26+
pub async fn get_meta_users_id(Path(user_id): Path<Id<UserMarker>>,
27+
Extension(current_user): Extension<CurrentUser>,
28+
State(state): State<AppState>)
29+
-> Result<Json<Value>, StatusCode> {
30+
if current_user.id != user_id {
31+
return Err(StatusCode::UNAUTHORIZED);
32+
}
33+
34+
Ok(Json(json!(get_user(user_id, &state.discord_bot).await?)))
35+
}

0 commit comments

Comments
 (0)