Optimizations (#676)

This commit is contained in:
Geometrically 2023-08-07 23:05:08 -07:00 committed by GitHub
parent f21c756793
commit df83fcc5b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 278 additions and 244 deletions

1
.env
View File

@ -15,6 +15,7 @@ MEILISEARCH_ADDR=http://localhost:7700
MEILISEARCH_KEY=modrinth MEILISEARCH_KEY=modrinth
REDIS_URL=redis://localhost REDIS_URL=redis://localhost
REDIS_MAX_CONNECTIONS=10000
BIND_ADDR=127.0.0.1:8000 BIND_ADDR=127.0.0.1:8000
SELF_ADDR=http://localhost:8000 SELF_ADDR=http://localhost:8000

View File

@ -0,0 +1,4 @@
CREATE INDEX threads_report_id
ON threads (report_id);
CREATE INDEX threads_mod_id
ON threads (mod_id);

View File

@ -1381,51 +1381,6 @@
}, },
"query": "\n UPDATE users\n SET password = $1\n WHERE (id = $2)\n " "query": "\n UPDATE users\n SET password = $1\n WHERE (id = $2)\n "
}, },
"447350097928db863d47d756354cd52668f52f7156dd7f3673a826f7b9aca2fd": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Int4"
},
{
"name": "version_",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "type_",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "created",
"ordinal": 3,
"type_info": "Timestamptz"
},
{
"name": "major",
"ordinal": 4,
"type_info": "Bool"
}
],
"nullable": [
false,
false,
false,
false,
false
],
"parameters": {
"Left": [
"Bool",
"Text"
]
}
},
"query": "\n SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major major FROM game_versions gv\n WHERE major = $1 AND type = $2\n ORDER BY created DESC\n "
},
"4514723bdc1eb8a781215075bec51af1cc6fabe88a469338d5a59533eabf80c5": { "4514723bdc1eb8a781215075bec51af1cc6fabe88a469338d5a59533eabf80c5": {
"describe": { "describe": {
"columns": [ "columns": [
@ -2962,50 +2917,6 @@
}, },
"query": "\n DELETE FROM dependencies WHERE mod_dependency_id = $1\n " "query": "\n DELETE FROM dependencies WHERE mod_dependency_id = $1\n "
}, },
"78a60cf0febcc6e35b8ffe38f2c021c13ab660c81c4775bbb26004d30242a1a8": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Int4"
},
{
"name": "version_",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "type_",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "created",
"ordinal": 3,
"type_info": "Timestamptz"
},
{
"name": "major",
"ordinal": 4,
"type_info": "Bool"
}
],
"nullable": [
false,
false,
false,
false,
false
],
"parameters": {
"Left": [
"Bool"
]
}
},
"query": "\n SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major major FROM game_versions gv\n WHERE major = $1\n ORDER BY created DESC\n "
},
"7916fe4f04067324ae05598ec9dc6f97f18baf9eda30c64f32677158ada87478": { "7916fe4f04067324ae05598ec9dc6f97f18baf9eda30c64f32677158ada87478": {
"describe": { "describe": {
"columns": [], "columns": [],
@ -5616,50 +5527,6 @@
}, },
"query": "SELECT EXISTS(SELECT 1 FROM team_members WHERE id=$1)" "query": "SELECT EXISTS(SELECT 1 FROM team_members WHERE id=$1)"
}, },
"e8ad94314ec2972c3102041b1bf06872c8e4c8a55156a17334a0e317fe41b784": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Int4"
},
{
"name": "version_",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "type_",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "created",
"ordinal": 3,
"type_info": "Timestamptz"
},
{
"name": "major",
"ordinal": 4,
"type_info": "Bool"
}
],
"nullable": [
false,
false,
false,
false,
false
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "\n SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major major FROM game_versions gv\n WHERE type = $1\n ORDER BY created DESC\n "
},
"e8d4589132b094df1e7a3ca0440344fc8013c0d20b3c71a1142ccbee91fb3c70": { "e8d4589132b094df1e7a3ca0440344fc8013c0d20b3c71a1142ccbee91fb3c70": {
"describe": { "describe": {
"columns": [ "columns": [

View File

@ -3,7 +3,11 @@ use super::DatabaseError;
use chrono::DateTime; use chrono::DateTime;
use chrono::Utc; use chrono::Utc;
use futures::TryStreamExt; use futures::TryStreamExt;
use serde::Deserialize; use redis::cmd;
use serde::{Deserialize, Serialize};
const TAGS_NAMESPACE: &str = "tags";
const DEFAULT_EXPIRY: i64 = 1800; // 30 minutes
pub struct ProjectType { pub struct ProjectType {
pub id: ProjectTypeId, pub id: ProjectTypeId,
@ -15,6 +19,7 @@ pub struct SideType {
pub name: String, pub name: String,
} }
#[derive(Serialize, Deserialize)]
pub struct Loader { pub struct Loader {
pub id: LoaderId, pub id: LoaderId,
pub loader: String, pub loader: String,
@ -22,7 +27,7 @@ pub struct Loader {
pub supported_project_types: Vec<String>, pub supported_project_types: Vec<String>,
} }
#[derive(Clone, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct GameVersion { pub struct GameVersion {
pub id: GameVersionId, pub id: GameVersionId,
pub version: String, pub version: String,
@ -32,6 +37,7 @@ pub struct GameVersion {
pub major: bool, pub major: bool,
} }
#[derive(Serialize, Deserialize)]
pub struct Category { pub struct Category {
pub id: CategoryId, pub id: CategoryId,
pub category: String, pub category: String,
@ -45,6 +51,7 @@ pub struct ReportType {
pub report_type: String, pub report_type: String,
} }
#[derive(Serialize, Deserialize)]
pub struct DonationPlatform { pub struct DonationPlatform {
pub id: DonationPlatformId, pub id: DonationPlatformId,
pub short: String, pub short: String,
@ -91,10 +98,24 @@ impl Category {
Ok(result.map(|r| CategoryId(r.id))) Ok(result.map(|r| CategoryId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<Category>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<Category>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:category", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<Category>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT c.id id, c.category category, c.icon icon, c.header category_header, pt.name project_type SELECT c.id id, c.category category, c.icon icon, c.header category_header, pt.name project_type
@ -116,6 +137,14 @@ impl Category {
.try_collect::<Vec<Category>>() .try_collect::<Vec<Category>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:category", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
} }
@ -138,10 +167,24 @@ impl Loader {
Ok(result.map(|r| LoaderId(r.id))) Ok(result.map(|r| LoaderId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<Loader>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<Loader>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:loader", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<Loader>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT l.id id, l.loader loader, l.icon icon, SELECT l.id id, l.loader loader, l.icon icon,
@ -169,6 +212,14 @@ impl Loader {
.try_collect::<Vec<_>>() .try_collect::<Vec<_>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:loader", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
} }
@ -205,10 +256,24 @@ impl GameVersion {
Ok(result.map(|r| GameVersionId(r.id))) Ok(result.map(|r| GameVersionId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<GameVersion>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<GameVersion>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:game_version", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<GameVersion>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major FROM game_versions gv SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major FROM game_versions gv
@ -226,6 +291,14 @@ impl GameVersion {
.try_collect::<Vec<GameVersion>>() .try_collect::<Vec<GameVersion>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:game_version", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
@ -233,75 +306,27 @@ impl GameVersion {
version_type_option: Option<&str>, version_type_option: Option<&str>,
major_option: Option<bool>, major_option: Option<bool>,
exec: E, exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<GameVersion>, DatabaseError> ) -> Result<Vec<GameVersion>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let result; let result = Self::list(exec, redis)
.await?
.into_iter()
.filter(|x| {
let mut bool = true;
if let Some(version_type) = version_type_option { if let Some(version_type) = version_type_option {
if let Some(major) = major_option { bool &= &*x.type_ == version_type;
result = sqlx::query!( }
" if let Some(major) = major_option {
SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major major FROM game_versions gv bool &= x.major == major;
WHERE major = $1 AND type = $2 }
ORDER BY created DESC
", bool
major, })
version_type .collect();
)
.fetch_many(exec)
.try_filter_map(|e| async { Ok(e.right().map(|c| GameVersion {
id: GameVersionId(c.id),
version: c.version_,
type_: c.type_,
created: c.created,
major: c.major,
})) })
.try_collect::<Vec<GameVersion>>()
.await?;
} else {
result = sqlx::query!(
"
SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major major FROM game_versions gv
WHERE type = $1
ORDER BY created DESC
",
version_type
)
.fetch_many(exec)
.try_filter_map(|e| async { Ok(e.right().map(|c| GameVersion {
id: GameVersionId(c.id),
version: c.version_,
type_: c.type_,
created: c.created,
major: c.major,
})) })
.try_collect::<Vec<GameVersion>>()
.await?;
}
} else if let Some(major) = major_option {
result = sqlx::query!(
"
SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major major FROM game_versions gv
WHERE major = $1
ORDER BY created DESC
",
major
)
.fetch_many(exec)
.try_filter_map(|e| async { Ok(e.right().map(|c| GameVersion {
id: GameVersionId(c.id),
version: c.version_,
type_: c.type_,
created: c.created,
major: c.major,
})) })
.try_collect::<Vec<GameVersion>>()
.await?;
} else {
result = Vec::new();
}
Ok(result) Ok(result)
} }
@ -381,10 +406,24 @@ impl DonationPlatform {
Ok(result.map(|r| DonationPlatformId(r.id))) Ok(result.map(|r| DonationPlatformId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<DonationPlatform>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<DonationPlatform>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:donation_platform", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<DonationPlatform>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT id, short, name FROM donation_platforms SELECT id, short, name FROM donation_platforms
@ -401,6 +440,14 @@ impl DonationPlatform {
.try_collect::<Vec<DonationPlatform>>() .try_collect::<Vec<DonationPlatform>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:donation_platform", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
} }
@ -423,10 +470,24 @@ impl ReportType {
Ok(result.map(|r| ReportTypeId(r.id))) Ok(result.map(|r| ReportTypeId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<String>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<String>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:report_type", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<String>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT name FROM report_types SELECT name FROM report_types
@ -437,6 +498,14 @@ impl ReportType {
.try_collect::<Vec<String>>() .try_collect::<Vec<String>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:report_type", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
} }
@ -459,10 +528,24 @@ impl ProjectType {
Ok(result.map(|r| ProjectTypeId(r.id))) Ok(result.map(|r| ProjectTypeId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<String>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<String>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:project_type", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<String>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT name FROM project_types SELECT name FROM project_types
@ -473,6 +556,14 @@ impl ProjectType {
.try_collect::<Vec<String>>() .try_collect::<Vec<String>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:project_type", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
} }
@ -495,10 +586,24 @@ impl SideType {
Ok(result.map(|r| SideTypeId(r.id))) Ok(result.map(|r| SideTypeId(r.id)))
} }
pub async fn list<'a, E>(exec: E) -> Result<Vec<String>, DatabaseError> pub async fn list<'a, E>(
exec: E,
redis: &deadpool_redis::Pool,
) -> Result<Vec<String>, DatabaseError>
where where
E: sqlx::Executor<'a, Database = sqlx::Postgres>, E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{ {
let mut redis = redis.get().await?;
let res = cmd("GET")
.arg(format!("{}:side_type", TAGS_NAMESPACE))
.query_async::<_, Option<String>>(&mut redis)
.await?
.and_then(|x| serde_json::from_str::<Vec<String>>(&x).ok());
if let Some(res) = res {
return Ok(res);
}
let result = sqlx::query!( let result = sqlx::query!(
" "
SELECT name FROM side_types SELECT name FROM side_types
@ -509,6 +614,14 @@ impl SideType {
.try_collect::<Vec<String>>() .try_collect::<Vec<String>>()
.await?; .await?;
cmd("SET")
.arg(format!("{}:side_type", TAGS_NAMESPACE))
.arg(serde_json::to_string(&result)?)
.arg("EX")
.arg(DEFAULT_EXPIRY)
.query_async::<_, ()>(&mut redis)
.await?;
Ok(result) Ok(result)
} }
} }

View File

@ -161,13 +161,13 @@ pub struct DonationPlatformId(pub i32);
#[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[sqlx(transparent)] #[sqlx(transparent)]
pub struct VersionId(pub i64); pub struct VersionId(pub i64);
#[derive(Copy, Clone, Debug, Type, Deserialize)] #[derive(Copy, Clone, Debug, Type, Deserialize, Serialize)]
#[sqlx(transparent)] #[sqlx(transparent)]
pub struct GameVersionId(pub i32); pub struct GameVersionId(pub i32);
#[derive(Copy, Clone, Debug, Type)] #[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)] #[sqlx(transparent)]
pub struct LoaderId(pub i32); pub struct LoaderId(pub i32);
#[derive(Copy, Clone, Debug, Type)] #[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)] #[sqlx(transparent)]
pub struct CategoryId(pub i32); pub struct CategoryId(pub i32);

View File

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
const PROJECTS_NAMESPACE: &str = "projects"; const PROJECTS_NAMESPACE: &str = "projects";
const PROJECTS_SLUGS_NAMESPACE: &str = "projects_slugs"; const PROJECTS_SLUGS_NAMESPACE: &str = "projects_slugs";
const PROJECTS_DEPENDENCIES_NAMESPACE: &str = "projects_dependencies"; const PROJECTS_DEPENDENCIES_NAMESPACE: &str = "projects_dependencies";
const DEFAULT_EXPIRY: i64 = 3600; // 60 minutes const DEFAULT_EXPIRY: i64 = 1800; // 30 minutes
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DonationUrl { pub struct DonationUrl {
@ -524,7 +524,8 @@ impl Project {
{ {
remaining_strings.retain(|x| { remaining_strings.retain(|x| {
&to_base62(project.inner.id.0 as u64) != x &to_base62(project.inner.id.0 as u64) != x
&& project.inner.slug.as_ref() != Some(x) && project.inner.slug.as_ref().map(|x| x.to_lowercase())
!= Some(x.to_lowercase())
}); });
found_projects.push(project); found_projects.push(project);
continue; continue;

View File

@ -185,8 +185,10 @@ impl User {
for user in users { for user in users {
if let Some(user) = user.and_then(|x| serde_json::from_str::<User>(&x).ok()) { if let Some(user) = user.and_then(|x| serde_json::from_str::<User>(&x).ok()) {
remaining_strings remaining_strings.retain(|x| {
.retain(|x| &to_base62(user.id.0 as u64) != x && &user.username != x); &to_base62(user.id.0 as u64) != x
&& user.username.to_lowercase() != x.to_lowercase()
});
found_users.push(user); found_users.push(user);
continue; continue;
} }

View File

@ -716,7 +716,7 @@ impl Version {
for (key, mut files) in save_files { for (key, mut files) in save_files {
cmd("SET") cmd("SET")
.arg(format!("{}:{}", VERSIONS_NAMESPACE, key)) .arg(format!("{}:{}", VERSION_FILES_NAMESPACE, key))
.arg(serde_json::to_string(&files)?) .arg(serde_json::to_string(&files)?)
.arg("EX") .arg("EX")
.arg(DEFAULT_EXPIRY) .arg(DEFAULT_EXPIRY)

View File

@ -79,9 +79,17 @@ async fn main() -> std::io::Result<()> {
.expect("Database connection failed"); .expect("Database connection failed");
// Redis connector // Redis connector
let redis_cfg = Config::from_url(dotenvy::var("REDIS_URL").expect("Redis URL not set")); let redis_pool = Config::from_url(dotenvy::var("REDIS_URL").expect("Redis URL not set"))
let redis_pool = redis_cfg .builder()
.create_pool(Some(Runtime::Tokio1)) .expect("Error building Redis pool")
.max_size(
dotenvy::var("DATABASE_MAX_CONNECTIONS")
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(10000),
)
.runtime(Runtime::Tokio1)
.build()
.expect("Redis connection failed"); .expect("Redis connection failed");
let storage_backend = dotenvy::var("STORAGE_BACKEND").unwrap_or_else(|_| "local".to_string()); let storage_backend = dotenvy::var("STORAGE_BACKEND").unwrap_or_else(|_| "local".to_string());
@ -175,6 +183,7 @@ async fn main() -> std::io::Result<()> {
// Reminding moderators to review projects which have been in the queue longer than 40hr // Reminding moderators to review projects which have been in the queue longer than 40hr
let pool_ref = pool.clone(); let pool_ref = pool.clone();
let redis_ref = redis_pool.clone();
let webhook_message_sent = Arc::new(Mutex::new(Vec::<( let webhook_message_sent = Arc::new(Mutex::new(Vec::<(
database::models::ProjectId, database::models::ProjectId,
DateTime<Utc>, DateTime<Utc>,
@ -182,6 +191,7 @@ async fn main() -> std::io::Result<()> {
scheduler.run(std::time::Duration::from_secs(10 * 60), move || { scheduler.run(std::time::Duration::from_secs(10 * 60), move || {
let pool_ref = pool_ref.clone(); let pool_ref = pool_ref.clone();
let redis_ref = redis_ref.clone();
let webhook_message_sent_ref = webhook_message_sent.clone(); let webhook_message_sent_ref = webhook_message_sent.clone();
info!("Checking reviewed projects submitted more than 40hrs ago"); info!("Checking reviewed projects submitted more than 40hrs ago");
@ -217,6 +227,7 @@ async fn main() -> std::io::Result<()> {
util::webhook::send_discord_webhook( util::webhook::send_discord_webhook(
project.into(), project.into(),
&pool_ref, &pool_ref,
&redis_ref,
webhook_url, webhook_url,
Some("<@&783155186491195394> This project has been in the queue for over 40 hours!".to_string()), Some("<@&783155186491195394> This project has been in the queue for over 40 hours!".to_string()),
) )

View File

@ -367,8 +367,8 @@ async fn project_create_inner(
let mut versions_map = std::collections::HashMap::new(); let mut versions_map = std::collections::HashMap::new();
let mut gallery_urls = Vec::new(); let mut gallery_urls = Vec::new();
let all_game_versions = models::categories::GameVersion::list(&mut *transaction).await?; let all_game_versions = models::categories::GameVersion::list(&mut *transaction, redis).await?;
let all_loaders = models::categories::Loader::list(&mut *transaction).await?; let all_loaders = models::categories::Loader::list(&mut *transaction, redis).await?;
{ {
// The first multipart field must be named "data" and contain a // The first multipart field must be named "data" and contain a
@ -836,9 +836,15 @@ async fn project_create_inner(
if status == ProjectStatus::Processing { if status == ProjectStatus::Processing {
if let Ok(webhook_url) = dotenvy::var("MODERATION_DISCORD_WEBHOOK") { if let Ok(webhook_url) = dotenvy::var("MODERATION_DISCORD_WEBHOOK") {
crate::util::webhook::send_discord_webhook(response.id, pool, webhook_url, None) crate::util::webhook::send_discord_webhook(
.await response.id,
.ok(); pool,
redis,
webhook_url,
None,
)
.await
.ok();
} }
} }

View File

@ -504,6 +504,7 @@ pub async fn project_edit(
crate::util::webhook::send_discord_webhook( crate::util::webhook::send_discord_webhook(
project_item.inner.id.into(), project_item.inner.id.into(),
&pool, &pool,
&redis,
webhook_url, webhook_url,
None, None,
) )
@ -530,6 +531,7 @@ pub async fn project_edit(
crate::util::webhook::send_discord_webhook( crate::util::webhook::send_discord_webhook(
project_item.inner.id.into(), project_item.inner.id.into(),
&pool, &pool,
&redis,
webhook_url, webhook_url,
None, None,
) )
@ -1251,8 +1253,9 @@ pub async fn projects_edit(
let team_members = let team_members =
database::models::TeamMember::get_from_team_full_many(&team_ids, &**pool, &redis).await?; database::models::TeamMember::get_from_team_full_many(&team_ids, &**pool, &redis).await?;
let categories = database::models::categories::Category::list(&**pool).await?; let categories = database::models::categories::Category::list(&**pool, &redis).await?;
let donation_platforms = database::models::categories::DonationPlatform::list(&**pool).await?; let donation_platforms =
database::models::categories::DonationPlatform::list(&**pool, &redis).await?;
let mut transaction = pool.begin().await?; let mut transaction = pool.begin().await?;

View File

@ -30,8 +30,11 @@ pub struct CategoryData {
} }
#[get("category")] #[get("category")]
pub async fn category_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> { pub async fn category_list(
let results = Category::list(&**pool) pool: web::Data<PgPool>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> {
let results = Category::list(&**pool, &redis)
.await? .await?
.into_iter() .into_iter()
.map(|x| CategoryData { .map(|x| CategoryData {
@ -53,8 +56,11 @@ pub struct LoaderData {
} }
#[get("loader")] #[get("loader")]
pub async fn loader_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> { pub async fn loader_list(
let mut results = Loader::list(&**pool) pool: web::Data<PgPool>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> {
let mut results = Loader::list(&**pool, &redis)
.await? .await?
.into_iter() .into_iter()
.map(|x| LoaderData { .map(|x| LoaderData {
@ -88,11 +94,12 @@ pub struct GameVersionQuery {
pub async fn game_version_list( pub async fn game_version_list(
pool: web::Data<PgPool>, pool: web::Data<PgPool>,
query: web::Query<GameVersionQuery>, query: web::Query<GameVersionQuery>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> { ) -> Result<HttpResponse, ApiError> {
let results: Vec<GameVersionQueryData> = if query.type_.is_some() || query.major.is_some() { let results: Vec<GameVersionQueryData> = if query.type_.is_some() || query.major.is_some() {
GameVersion::list_filter(query.type_.as_deref(), query.major, &**pool).await? GameVersion::list_filter(query.type_.as_deref(), query.major, &**pool, &redis).await?
} else { } else {
GameVersion::list(&**pool).await? GameVersion::list(&**pool, &redis).await?
} }
.into_iter() .into_iter()
.map(|x| GameVersionQueryData { .map(|x| GameVersionQueryData {
@ -163,8 +170,11 @@ pub struct DonationPlatformQueryData {
} }
#[get("donation_platform")] #[get("donation_platform")]
pub async fn donation_platform_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> { pub async fn donation_platform_list(
let results: Vec<DonationPlatformQueryData> = DonationPlatform::list(&**pool) pool: web::Data<PgPool>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> {
let results: Vec<DonationPlatformQueryData> = DonationPlatform::list(&**pool, &redis)
.await? .await?
.into_iter() .into_iter()
.map(|x| DonationPlatformQueryData { .map(|x| DonationPlatformQueryData {
@ -176,19 +186,28 @@ pub async fn donation_platform_list(pool: web::Data<PgPool>) -> Result<HttpRespo
} }
#[get("report_type")] #[get("report_type")]
pub async fn report_type_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> { pub async fn report_type_list(
let results = ReportType::list(&**pool).await?; pool: web::Data<PgPool>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> {
let results = ReportType::list(&**pool, &redis).await?;
Ok(HttpResponse::Ok().json(results)) Ok(HttpResponse::Ok().json(results))
} }
#[get("project_type")] #[get("project_type")]
pub async fn project_type_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> { pub async fn project_type_list(
let results = ProjectType::list(&**pool).await?; pool: web::Data<PgPool>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> {
let results = ProjectType::list(&**pool, &redis).await?;
Ok(HttpResponse::Ok().json(results)) Ok(HttpResponse::Ok().json(results))
} }
#[get("side_type")] #[get("side_type")]
pub async fn side_type_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> { pub async fn side_type_list(
let results = SideType::list(&**pool).await?; pool: web::Data<PgPool>,
redis: web::Data<deadpool_redis::Pool>,
) -> Result<HttpResponse, ApiError> {
let results = SideType::list(&**pool, &redis).await?;
Ok(HttpResponse::Ok().json(results)) Ok(HttpResponse::Ok().json(results))
} }

View File

@ -135,8 +135,8 @@ async fn version_create_inner(
let mut initial_version_data = None; let mut initial_version_data = None;
let mut version_builder = None; let mut version_builder = None;
let all_game_versions = models::categories::GameVersion::list(&mut *transaction).await?; let all_game_versions = models::categories::GameVersion::list(&mut *transaction, redis).await?;
let all_loaders = models::categories::Loader::list(&mut *transaction).await?; let all_loaders = models::categories::Loader::list(&mut *transaction, redis).await?;
let user = get_user_from_headers( let user = get_user_from_headers(
&req, &req,
@ -561,7 +561,8 @@ async fn upload_file_to_version_inner(
.await? .await?
.name; .name;
let all_game_versions = models::categories::GameVersion::list(&mut *transaction).await?; let all_game_versions =
models::categories::GameVersion::list(&mut *transaction, &redis).await?;
let mut error = None; let mut error = None;
while let Some(item) = payload.next().await { while let Some(item) = payload.next().await {

View File

@ -115,8 +115,13 @@ pub async fn version_list(
// Attempt to populate versions with "auto featured" versions // Attempt to populate versions with "auto featured" versions
if response.is_empty() && !versions.is_empty() && filters.featured.unwrap_or(false) { if response.is_empty() && !versions.is_empty() && filters.featured.unwrap_or(false) {
let (loaders, game_versions) = futures::future::try_join( let (loaders, game_versions) = futures::future::try_join(
database::models::categories::Loader::list(&**pool), database::models::categories::Loader::list(&**pool, &redis),
database::models::categories::GameVersion::list_filter(None, Some(true), &**pool), database::models::categories::GameVersion::list_filter(
None,
Some(true),
&**pool,
&redis,
),
) )
.await?; .await?;

View File

@ -72,10 +72,11 @@ const PLUGIN_LOADERS: &[&str] = &[
pub async fn send_discord_webhook( pub async fn send_discord_webhook(
project_id: ProjectId, project_id: ProjectId,
pool: &PgPool, pool: &PgPool,
redis: &deadpool_redis::Pool,
webhook_url: String, webhook_url: String,
message: Option<String>, message: Option<String>,
) -> Result<(), ApiError> { ) -> Result<(), ApiError> {
let all_game_versions = GameVersion::list(pool).await?; let all_game_versions = GameVersion::list(pool, redis).await?;
let row = let row =
sqlx::query!( sqlx::query!(