From d08800f9e0d5d9ea6bde818df0f3e3f18d82b6da Mon Sep 17 00:00:00 2001 From: Benjamin Sherriff Date: Thu, 5 Sep 2024 11:52:14 -0400 Subject: [PATCH] Switched from diesel to sqlx --- Cargo.toml | 4 +- Makefile | 2 +- docker-compose.yml | 78 +++--- migrations/000000_create_messages/down.sql | 1 - migrations/000000_create_messages/up.sql | 12 - migrations/000001_create_races/down.sql | 1 - migrations/000001_create_races/up.sql | 7 - migrations/000002_create_classes/down.sql | 1 - migrations/000002_create_classes/up.sql | 3 - migrations/000003_create_feats/down.sql | 1 - migrations/000003_create_feats/up.sql | 3 - .../000004_create_options_features/down.sql | 1 - .../000004_create_options_features/up.sql | 3 - migrations/000005_create_backgrounds/down.sql | 1 - migrations/000005_create_backgrounds/up.sql | 3 - migrations/000006_create_items/down.sql | 1 - migrations/000006_create_items/up.sql | 3 - migrations/000007_create_spells/down.sql | 1 - migrations/000007_create_spells/up.sql | 15 -- migrations/000008_create_conditions/down.sql | 1 - migrations/000008_create_conditions/up.sql | 3 - migrations/000009_create_bestiary/down.sql | 1 - migrations/000009_create_bestiary/up.sql | 3 - migrations/000010_create_guilds/down.sql | 1 - migrations/000010_create_guilds/up.sql | 5 - migrations/000011_create_users/down.sql | 1 - migrations/000011_create_users/up.sql | 11 - migrations/000_base.sql | 28 +++ migrations/001_dnd_tables.sql | 43 ++++ src/bot/commands/audio/play.rs | 2 +- src/bot/commands/audio/volume.rs | 4 +- src/bot/commands/chat.rs | 55 ++-- src/bot/guilds/model.rs | 63 +++-- src/bot/handler.rs | 4 +- src/bot/messages/model.rs | 157 +++--------- src/database/mod.rs | 81 ++++++ src/dnd/spells/mod.rs | 37 ++- src/dnd/spells/model.rs | 235 +----------------- src/error.rs | 46 +++- src/main.rs | 11 +- src/storage/mod.rs | 65 ----- src/storage/schema.rs | 54 ---- 42 files changed, 365 insertions(+), 687 deletions(-) delete mode 100644 migrations/000000_create_messages/down.sql delete mode 100644 migrations/000000_create_messages/up.sql delete mode 100644 migrations/000001_create_races/down.sql delete mode 100644 migrations/000001_create_races/up.sql delete mode 100644 migrations/000002_create_classes/down.sql delete mode 100644 migrations/000002_create_classes/up.sql delete mode 100644 migrations/000003_create_feats/down.sql delete mode 100644 migrations/000003_create_feats/up.sql delete mode 100644 migrations/000004_create_options_features/down.sql delete mode 100644 migrations/000004_create_options_features/up.sql delete mode 100644 migrations/000005_create_backgrounds/down.sql delete mode 100644 migrations/000005_create_backgrounds/up.sql delete mode 100644 migrations/000006_create_items/down.sql delete mode 100644 migrations/000006_create_items/up.sql delete mode 100644 migrations/000007_create_spells/down.sql delete mode 100644 migrations/000007_create_spells/up.sql delete mode 100644 migrations/000008_create_conditions/down.sql delete mode 100644 migrations/000008_create_conditions/up.sql delete mode 100644 migrations/000009_create_bestiary/down.sql delete mode 100644 migrations/000009_create_bestiary/up.sql delete mode 100644 migrations/000010_create_guilds/down.sql delete mode 100644 migrations/000010_create_guilds/up.sql delete mode 100644 migrations/000011_create_users/down.sql delete mode 100644 migrations/000011_create_users/up.sql create mode 100644 migrations/000_base.sql create mode 100644 migrations/001_dnd_tables.sql create mode 100644 src/database/mod.rs delete mode 100644 src/storage/mod.rs delete mode 100644 src/storage/schema.rs diff --git a/Cargo.toml b/Cargo.toml index 1506a54..6350ce5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,7 @@ serde_json = "1.0.127" serenity = { version = "0.12.2", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "voice", "cache", "framework", "standard_framework"] } songbird = { version = "0.4.3", features = ["builtin-queue"] } symphonia = { version = "0.5.4", features = ["all"] } -diesel = { version = "2.1.5", default-features = false, features = ["postgres", "chrono", "r2d2", "32-column-tables", "serde_json", "with-deprecated"] } -diesel_migrations = { version = "2.1.0", features = ["postgres"] } -r2d2 = "0.8.10" +sqlx = { version = "0.7.4", features = ["runtime-tokio", "postgres", "chrono", "uuid"] } chrono = { version = "0.4.38", features = ["serde"] } reqwest = { version = "0.11", default-features = false, features = ["json"] } lazy_static = "1.5.0" diff --git a/Makefile b/Makefile index 61a1dbc..51cd163 100644 --- a/Makefile +++ b/Makefile @@ -50,4 +50,4 @@ docker-clean: ## Stop the docker containers and remove volumes docker-refresh: docker-clean backend-up ## Refresh the docker containers psql: ## Connect to the database - @docker exec -it siren-db psql -U ${DATABASE_USER} -P pager=off \ No newline at end of file + @docker exec -it siren-postgres psql -U ${DATABASE_USER} -P pager=off \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d46d2f4..8726df5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,51 +6,51 @@ x-env_file: &env name: siren services: - # bot: - # image: siren-service:${SIREN_VERSION:-latest} - # container_name: siren-service - # build: - # context: . - # dockerfile: ./Dockerfile - # args: - # - VERSION=${SIREN_VERSION:-latest} - # env_file: *env - # environment: - # DATABASE_HOST: db - # DATABASE_PORT: 5432 - # REDIS_HOST: redis - # REDIS_PORT: 6379 - # MINIO_HOST: minio - # MINIO_PORT: 9000 - # SERVICE_HOST: service - # SERVICE_PORT: 5000 - # DATA_DIR_PATH: /data - # volumes: - # - ${DATA_DIR_PATH:-/data}:/data - # ports: - # - ${SERVICE_PORT:-5000}:5000 - # depends_on: - # - db - # - redis - # - minio - # networks: - # - frontend - # - backend - # restart: unless-stopped - # profiles: - # - bot + bot: + image: siren-service:${SIREN_VERSION:-latest} + container_name: siren-service + build: + context: . + dockerfile: ./Dockerfile + args: + - VERSION=${SIREN_VERSION:-latest} + env_file: *env + environment: + DATABASE_HOST: db + DATABASE_PORT: 5432 + REDIS_HOST: redis + REDIS_PORT: 6379 + MINIO_HOST: minio + MINIO_PORT: 9000 + SERVICE_HOST: service + SERVICE_PORT: 5000 + DATA_DIR_PATH: /data + volumes: + - ${DATA_DIR_PATH:-/data}:/data + ports: + - ${SERVICE_PORT:-5000}:5000 + depends_on: + - db + - redis + - minio + networks: + - frontend + - backend + restart: unless-stopped + profiles: + - bot - db: + postgres: image: postgres:latest - container_name: siren-db + container_name: siren-postgres env_file: *env environment: POSTGRES_USER: ${DATABASE_USER} POSTGRES_PASSWORD: ${DATABASE_PASSWORD} POSTGRES_DB: ${DATABASE_NAME} volumes: - - db:/var/lib/postgresql/data - - db_logs:/var/log + - postgres:/var/lib/postgresql/data + - postgres_logs:/var/log ports: - ${DATABASE_PORT:-5432}:5432 networks: @@ -73,8 +73,8 @@ services: restart: unless-stopped volumes: - db: - db_logs: + postgres: + postgres_logs: redis: networks: diff --git a/migrations/000000_create_messages/down.sql b/migrations/000000_create_messages/down.sql deleted file mode 100644 index be13677..0000000 --- a/migrations/000000_create_messages/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE messages; \ No newline at end of file diff --git a/migrations/000000_create_messages/up.sql b/migrations/000000_create_messages/up.sql deleted file mode 100644 index f876ba8..0000000 --- a/migrations/000000_create_messages/up.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE IF NOT EXISTS messages ( - id TEXT PRIMARY KEY NOT NULL, - guild_id BIGINT NOT NULL, - channel_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - created BIGINT NOT NULL, - model TEXT NOT NULL, - request TEXT NOT NULL, - response TEXT NOT NULL, - request_tags TEXT[] NOT NULL, - response_tags TEXT[] NOT NULL -); \ No newline at end of file diff --git a/migrations/000001_create_races/down.sql b/migrations/000001_create_races/down.sql deleted file mode 100644 index f075840..0000000 --- a/migrations/000001_create_races/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE races; \ No newline at end of file diff --git a/migrations/000001_create_races/up.sql b/migrations/000001_create_races/up.sql deleted file mode 100644 index 5ec4de7..0000000 --- a/migrations/000001_create_races/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS races ( - id INTEGER GENERATED ALWAYS AS IDENTITY, - name TEXT NOT NULL, - size TEXT NOT NULL, - source TEXT NOT NULL, - data JSON NOT NULL -); \ No newline at end of file diff --git a/migrations/000002_create_classes/down.sql b/migrations/000002_create_classes/down.sql deleted file mode 100644 index 288ada8..0000000 --- a/migrations/000002_create_classes/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE classes; \ No newline at end of file diff --git a/migrations/000002_create_classes/up.sql b/migrations/000002_create_classes/up.sql deleted file mode 100644 index 36f79bf..0000000 --- a/migrations/000002_create_classes/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS classes ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000003_create_feats/down.sql b/migrations/000003_create_feats/down.sql deleted file mode 100644 index 4afe0e1..0000000 --- a/migrations/000003_create_feats/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE feats; \ No newline at end of file diff --git a/migrations/000003_create_feats/up.sql b/migrations/000003_create_feats/up.sql deleted file mode 100644 index 0193ae4..0000000 --- a/migrations/000003_create_feats/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS feats ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000004_create_options_features/down.sql b/migrations/000004_create_options_features/down.sql deleted file mode 100644 index 0fcca0f..0000000 --- a/migrations/000004_create_options_features/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE options_features; \ No newline at end of file diff --git a/migrations/000004_create_options_features/up.sql b/migrations/000004_create_options_features/up.sql deleted file mode 100644 index d64ba24..0000000 --- a/migrations/000004_create_options_features/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS options_features ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000005_create_backgrounds/down.sql b/migrations/000005_create_backgrounds/down.sql deleted file mode 100644 index 74f4344..0000000 --- a/migrations/000005_create_backgrounds/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE backgrounds; \ No newline at end of file diff --git a/migrations/000005_create_backgrounds/up.sql b/migrations/000005_create_backgrounds/up.sql deleted file mode 100644 index 0d4bb7f..0000000 --- a/migrations/000005_create_backgrounds/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS backgrounds ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000006_create_items/down.sql b/migrations/000006_create_items/down.sql deleted file mode 100644 index 9f8e6a3..0000000 --- a/migrations/000006_create_items/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE items; \ No newline at end of file diff --git a/migrations/000006_create_items/up.sql b/migrations/000006_create_items/up.sql deleted file mode 100644 index f12f9b2..0000000 --- a/migrations/000006_create_items/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS items ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000007_create_spells/down.sql b/migrations/000007_create_spells/down.sql deleted file mode 100644 index 1b09bdf..0000000 --- a/migrations/000007_create_spells/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE spells; \ No newline at end of file diff --git a/migrations/000007_create_spells/up.sql b/migrations/000007_create_spells/up.sql deleted file mode 100644 index 0997c05..0000000 --- a/migrations/000007_create_spells/up.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE IF NOT EXISTS spells ( - id INTEGER GENERATED ALWAYS AS IDENTITY, - name TEXT NOT NULL, - school TEXT NOT NULL, - level INTEGER NOT NULL, - ritual BOOLEAN DEFAULT FALSE, - concentration BOOLEAN DEFAULT FALSE, - classes TEXT[] NOT NULL, - damage_inflict TEXT[] NOT NULL, - damage_resist TEXT[] NOT NULL, - conditions TEXT[] NOT NULL, - saving_throw TEXT[] NOT NULL, - attack_type TEXT, - data JSONB NOT NULL -); \ No newline at end of file diff --git a/migrations/000008_create_conditions/down.sql b/migrations/000008_create_conditions/down.sql deleted file mode 100644 index 4cf8449..0000000 --- a/migrations/000008_create_conditions/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE conditions; \ No newline at end of file diff --git a/migrations/000008_create_conditions/up.sql b/migrations/000008_create_conditions/up.sql deleted file mode 100644 index 76b5220..0000000 --- a/migrations/000008_create_conditions/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS conditions ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000009_create_bestiary/down.sql b/migrations/000009_create_bestiary/down.sql deleted file mode 100644 index 9bbe034..0000000 --- a/migrations/000009_create_bestiary/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE bestiary; \ No newline at end of file diff --git a/migrations/000009_create_bestiary/up.sql b/migrations/000009_create_bestiary/up.sql deleted file mode 100644 index a6019df..0000000 --- a/migrations/000009_create_bestiary/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE IF NOT EXISTS bestiary ( - id INTEGER GENERATED ALWAYS AS IDENTITY -); \ No newline at end of file diff --git a/migrations/000010_create_guilds/down.sql b/migrations/000010_create_guilds/down.sql deleted file mode 100644 index c2477fc..0000000 --- a/migrations/000010_create_guilds/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE guilds; \ No newline at end of file diff --git a/migrations/000010_create_guilds/up.sql b/migrations/000010_create_guilds/up.sql deleted file mode 100644 index 6fef802..0000000 --- a/migrations/000010_create_guilds/up.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE IF NOT EXISTS guilds ( - id BIGINT PRIMARY KEY NOT NULL, - bot_id BIGINT NOT NULL, - volume INTEGER NOT NULL -); \ No newline at end of file diff --git a/migrations/000011_create_users/down.sql b/migrations/000011_create_users/down.sql deleted file mode 100644 index 441087a..0000000 --- a/migrations/000011_create_users/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE users; \ No newline at end of file diff --git a/migrations/000011_create_users/up.sql b/migrations/000011_create_users/up.sql deleted file mode 100644 index 97e07e6..0000000 --- a/migrations/000011_create_users/up.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE IF NOT EXISTS users ( - email TEXT PRIMARY KEY NOT NULL, - hash TEXT NOT NULL, - role TEXT NOT NULL, - first_name TEXT NOT NULL, - last_name TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - profile_picture TEXT, - verified BOOLEAN NOT NULL DEFAULT FALSE -); \ No newline at end of file diff --git a/migrations/000_base.sql b/migrations/000_base.sql new file mode 100644 index 0000000..7cfbfa7 --- /dev/null +++ b/migrations/000_base.sql @@ -0,0 +1,28 @@ +CREATE TABLE IF NOT EXISTS guilds ( + id BIGINT PRIMARY KEY NOT NULL, + bot_id BIGINT NOT NULL, + volume INTEGER NOT NULL +); +CREATE TABLE IF NOT EXISTS users ( + email TEXT PRIMARY KEY NOT NULL, + hash TEXT NOT NULL, + role TEXT NOT NULL, + first_name TEXT NOT NULL, + last_name TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + profile_picture TEXT, + verified BOOLEAN NOT NULL DEFAULT FALSE +); +CREATE TABLE IF NOT EXISTS messages ( + id TEXT PRIMARY KEY NOT NULL, + guild_id BIGINT NOT NULL, + channel_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + created BIGINT NOT NULL, + model TEXT NOT NULL, + request TEXT NOT NULL, + response TEXT NOT NULL, + request_tags TEXT[] NOT NULL, + response_tags TEXT[] NOT NULL +); \ No newline at end of file diff --git a/migrations/001_dnd_tables.sql b/migrations/001_dnd_tables.sql new file mode 100644 index 0000000..98ca9dd --- /dev/null +++ b/migrations/001_dnd_tables.sql @@ -0,0 +1,43 @@ +CREATE TABLE IF NOT EXISTS races ( + id INTEGER GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + size TEXT NOT NULL, + source TEXT NOT NULL, + data JSON NOT NULL +); +CREATE TABLE IF NOT EXISTS classes ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); +CREATE TABLE IF NOT EXISTS feats ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); +CREATE TABLE IF NOT EXISTS options_features ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); +CREATE TABLE IF NOT EXISTS backgrounds ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); +CREATE TABLE IF NOT EXISTS items ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); +CREATE TABLE IF NOT EXISTS spells ( + id INTEGER GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + school TEXT NOT NULL, + level INTEGER NOT NULL, + ritual BOOLEAN DEFAULT FALSE, + concentration BOOLEAN DEFAULT FALSE, + classes TEXT[] NOT NULL, + damage_inflict TEXT[] NOT NULL, + damage_resist TEXT[] NOT NULL, + conditions TEXT[] NOT NULL, + saving_throw TEXT[] NOT NULL, + attack_type TEXT, + data JSONB NOT NULL +); +CREATE TABLE IF NOT EXISTS conditions ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); +CREATE TABLE IF NOT EXISTS bestiary ( + id INTEGER GENERATED ALWAYS AS IDENTITY +); \ No newline at end of file diff --git a/src/bot/commands/audio/play.rs b/src/bot/commands/audio/play.rs index 38abc12..c900985 100644 --- a/src/bot/commands/audio/play.rs +++ b/src/bot/commands/audio/play.rs @@ -106,7 +106,7 @@ pub async fn play_track( let call_handler = handler_lock.lock().await; call_handler.queue().is_empty() }; - let guild = GuildCache::get(guild_id.get() as i64)?; + let guild = GuildCache::get_by_id(guild_id.get() as i64).await?.unwrap(); let valid = is_valid_url(&track_url); // Check if the URL is valid if !valid.0 { diff --git a/src/bot/commands/audio/volume.rs b/src/bot/commands/audio/volume.rs index eaff139..d31563d 100644 --- a/src/bot/commands/audio/volume.rs +++ b/src/bot/commands/audio/volume.rs @@ -62,7 +62,9 @@ pub async fn set_volume(manager: Arc, guild_id: GuildId, volume: i32) // Format volume to f32 bound between 0.0 and 1.0 let volume = std::cmp::min(100, std::cmp::max(0, volume)); let bound_volume = volume as f32 / 100.0; - let _ = GuildCache::update_audio(guild_id.get() as i64, volume); + let mut guild_cache = GuildCache::get_by_id(guild_id.get() as i64).await.unwrap().unwrap(); + guild_cache.volume = volume; + guild_cache.update().await.unwrap(); if let Some(handler_lock) = manager.get(guild_id) { let handler = handler_lock.lock().await; diff --git a/src/bot/commands/chat.rs b/src/bot/commands/chat.rs index 3bf09bf..e3c3555 100644 --- a/src/bot/commands/chat.rs +++ b/src/bot/commands/chat.rs @@ -6,7 +6,7 @@ use serenity::model::channel::Message; use serenity::model::prelude::{ChannelType, PermissionOverwrite, PermissionOverwriteType}; use serenity::prelude::*; -use crate::bot::messages::{QueryFilters, QueryMessage}; +use crate::bot::messages::MessageCache; use crate::bot::oai::{ChatCompletionMessage, ChatCompletionRequest, GPTRole, OAI}; pub async fn generate_response(ctx: &Context, msg: &Message, oai: &OAI) { @@ -27,30 +27,30 @@ pub async fn generate_response(ctx: &Context, msg: &Message, oai: &OAI) { }, ]; - match QueryMessage::get_all( - &QueryFilters { - by_guild_id: Some(guild_id.get() as i64), - by_channel_id: Some(channel_id.get() as i64), - by_user_id: Some(author_id.get() as i64), - ..Default::default() - }, - 100, - 1, - ) { - Ok(m) => { - for message in m { - messages.push(ChatCompletionMessage { - role: GPTRole::User, - content: format!("{}", message.request), - }); - messages.push(ChatCompletionMessage { - role: GPTRole::Assistant, - content: format!("{}", message.response), - }); - } - } - Err(err) => warn!("Could not load previous messages: {}", err), - }; + // match MessageCache::get_all( + // &QueryFilters { + // by_guild_id: Some(guild_id.get() as i64), + // by_channel_id: Some(channel_id.get() as i64), + // by_user_id: Some(author_id.get() as i64), + // ..Default::default() + // }, + // 100, + // 1, + // ) { + // Ok(m) => { + // for message in m { + // messages.push(ChatCompletionMessage { + // role: GPTRole::User, + // content: format!("{}", message.request), + // }); + // messages.push(ChatCompletionMessage { + // role: GPTRole::Assistant, + // content: format!("{}", message.response), + // }); + // } + // } + // Err(err) => warn!("Could not load previous messages: {}", err), + // }; messages.push(ChatCompletionMessage { role: GPTRole::User, content: parsed_content.clone(), @@ -98,7 +98,7 @@ pub async fn generate_response(ctx: &Context, msg: &Message, oai: &OAI) { trace!("Processing response received from OpenAI"); if !r.choices.is_empty() { let res = r.choices[0].message.content.clone(); - if let Err(err) = QueryMessage::insert(QueryMessage { + let message_cache = MessageCache { id: r.id, guild_id: guild_id.get() as i64, channel_id: response_channel.get() as i64, @@ -109,7 +109,8 @@ pub async fn generate_response(ctx: &Context, msg: &Message, oai: &OAI) { response: res.clone(), request_tags: vec![], response_tags: vec![], - }) { + }; + if let Err(err) = message_cache.insert().await { warn!("{}", err); } res diff --git a/src/bot/guilds/model.rs b/src/bot/guilds/model.rs index 74d8f98..c7857eb 100644 --- a/src/bot/guilds/model.rs +++ b/src/bot/guilds/model.rs @@ -1,11 +1,9 @@ -use diesel::prelude::*; use serde::{Serialize, Deserialize}; use crate::error::SirenResult; -use crate::storage::{schema::guilds, connection}; +const TABLE_NAME: &str = "guilds"; -#[derive(Insertable, AsChangeset, Queryable, QueryableByName, Serialize, Deserialize)] -#[diesel(table_name = guilds)] +#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct GuildCache { pub id: i64, pub bot_id: i64, @@ -13,25 +11,50 @@ pub struct GuildCache { } impl GuildCache { - pub fn insert(&self) -> SirenResult { - let mut conn = connection()?; - let guild = diesel::insert_into(guilds::table) - .values(self) - .get_result(&mut conn)?; - Ok(guild) + pub async fn insert(&self) -> SirenResult<()> { + let pool = crate::database::pool(); + sqlx::query(&format!( + "INSERT INTO {} ( + id, + bot_id, + volume + ) VALUES ( + $1, $2, $3 + )", + TABLE_NAME + )) + .bind(self.id) + .bind(self.bot_id) + .bind(self.volume) + .execute(pool) + .await?; + Ok(()) } - pub fn get(id: i64) -> SirenResult { - let mut conn = connection()?; - let guild = guilds::table.filter(guilds::id.eq(id)).first(&mut conn)?; - Ok(guild) + pub async fn get_by_id(id: i64) -> SirenResult> { + let pool = crate::database::pool(); + let item = + sqlx::query_as::<_, Self>(&format!("SELECT * FROM {} WHERE id = $1", TABLE_NAME)) + .bind(id) + .fetch_optional(pool) + .await?; + + Ok(item) } - pub fn update_audio(id: i64, volume: i32) -> SirenResult { - let mut conn = connection()?; - let guild = diesel::update(guilds::table.filter(guilds::id.eq(id))) - .set(guilds::volume.eq(volume)) - .get_result(&mut conn)?; - Ok(guild) + pub async fn update(&self) -> SirenResult<()> { + let pool = crate::database::pool(); + sqlx::query(&format!( + "UPDATE {} SET + bot_id = $2, + volume = $3 + WHERE id = $1", + TABLE_NAME)) + .bind(self.id) + .bind(self.bot_id) + .bind(self.volume) + .execute(pool) + .await?; + Ok(()) } } diff --git a/src/bot/handler.rs b/src/bot/handler.rs index 9d01c85..ff5650f 100644 --- a/src/bot/handler.rs +++ b/src/bot/handler.rs @@ -82,13 +82,13 @@ impl EventHandler for Handler { for guild in ready.guilds { // Check if guild exists in database let guild_id = guild.id.get() as i64; - if let Err(why) = GuildCache::get(guild_id) { + if let None = GuildCache::get_by_id(guild_id).await.unwrap() { let guild_cache = GuildCache { id: guild_id, bot_id: 1, volume: 100 }; - guild_cache.insert(); + guild_cache.insert().await.unwrap(); } let commands = guild .id diff --git a/src/bot/messages/model.rs b/src/bot/messages/model.rs index d2d840c..1df12ad 100644 --- a/src/bot/messages/model.rs +++ b/src/bot/messages/model.rs @@ -1,15 +1,10 @@ -use diesel::prelude::*; use serde::{Deserialize, Serialize}; use crate::error::SirenResult; -use crate::storage::{ - schema::messages::{self}, - connection, -}; +const TABLE_NAME: &str = "messages"; -#[derive(Queryable, Selectable, Insertable, AsChangeset, Serialize, Deserialize)] -#[diesel(table_name = messages)] -pub struct QueryMessage { +#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] +pub struct MessageCache { pub id: String, pub guild_id: i64, pub channel_id: i64, @@ -22,118 +17,38 @@ pub struct QueryMessage { pub response_tags: Vec, } -pub struct QueryFilters { - pub by_id: Option, - pub by_guild_id: Option, - pub by_channel_id: Option, - pub by_user_id: Option, - pub by_model: Option, - pub by_request: Option, - pub by_response: Option, - pub by_request_tags: Option>, - pub by_response_tags: Option>, -} - -impl Default for QueryFilters { - fn default() -> Self { - QueryFilters { - by_id: None, - by_guild_id: None, - by_channel_id: None, - by_user_id: None, - by_model: None, - by_request: None, - by_response: None, - by_request_tags: None, - by_response_tags: None, - } - } -} - -impl QueryMessage { - pub fn get_all(filters: &QueryFilters, limit: i32, page: i32) -> SirenResult> { - let mut conn = connection()?; - let mut query = messages::table - .limit(limit as i64) - .order(messages::created.asc()) - .into_boxed(); - // Limit query to page and limit - let offset = (page - 1) * limit; - query = query.offset(offset as i64); - // Apply filters - if let Some(id) = &filters.by_id { - query = query.filter(messages::id.eq(id)); - } - if let Some(guild_id) = &filters.by_guild_id { - query = query.filter(messages::guild_id.eq(guild_id)); - } - if let Some(channel_id) = &filters.by_channel_id { - query = query.filter(messages::channel_id.eq(channel_id)); - } - if let Some(user_id) = &filters.by_user_id { - query = query.filter(messages::user_id.eq(user_id)); - } - if let Some(model) = &filters.by_model { - query = query.filter(messages::model.eq(model)); - } - if let Some(request) = &filters.by_request { - query = query.filter(messages::request.eq(request)); - } - if let Some(response) = &filters.by_response { - query = query.filter(messages::response.eq(response)); - } - if let Some(request_tags) = &filters.by_request_tags { - query = query.filter(messages::request_tags.eq(request_tags)); - } - if let Some(response_tags) = &filters.by_response_tags { - query = query.filter(messages::response_tags.eq(response_tags)); - } - // Execute query - let messages = query.load::(&mut conn)?; - Ok(messages) - } - - pub fn get_count(fitlers: &QueryFilters) -> SirenResult { - let mut conn = connection()?; - let mut query = messages::table.into_boxed(); - // Apply filters - if let Some(id) = &fitlers.by_id { - query = query.filter(messages::id.eq(id)); - } - if let Some(guild_id) = &fitlers.by_guild_id { - query = query.filter(messages::guild_id.eq(guild_id)); - } - if let Some(channel_id) = &fitlers.by_channel_id { - query = query.filter(messages::channel_id.eq(channel_id)); - } - if let Some(user_id) = &fitlers.by_user_id { - query = query.filter(messages::user_id.eq(user_id)); - } - if let Some(model) = &fitlers.by_model { - query = query.filter(messages::model.eq(model)); - } - if let Some(request) = &fitlers.by_request { - query = query.filter(messages::request.eq(request)); - } - if let Some(response) = &fitlers.by_response { - query = query.filter(messages::response.eq(response)); - } - if let Some(request_tags) = &fitlers.by_request_tags { - query = query.filter(messages::request_tags.eq(request_tags)); - } - if let Some(response_tags) = &fitlers.by_response_tags { - query = query.filter(messages::response_tags.eq(response_tags)); - } - // Execute query - let count = query.count().get_result::(&mut conn)?; - Ok(count) - } - - pub fn insert(message: Self) -> SirenResult { - let mut conn = connection()?; - let message = diesel::insert_into(messages::table) - .values(message) - .get_result(&mut conn)?; - Ok(message) +impl MessageCache { + pub async fn insert(&self) -> SirenResult<()> { + let pool = crate::database::pool(); + sqlx::query(&format!( + "INSERT INTO {} ( + id, + guild_id, + channel_id, + user_id, + created, + model, + request, + response, + request_tags, + response_tags + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 + )", + TABLE_NAME + )) + .bind(&self.id) + .bind(self.guild_id) + .bind(self.channel_id) + .bind(self.user_id) + .bind(self.created) + .bind(&self.model) + .bind(&self.request) + .bind(&self.response) + .bind(&self.request_tags) + .bind(&self.response_tags) + .execute(pool) + .await?; + Ok(()) } } diff --git a/src/database/mod.rs b/src/database/mod.rs new file mode 100644 index 0000000..c5e9918 --- /dev/null +++ b/src/database/mod.rs @@ -0,0 +1,81 @@ +use std::{sync::OnceLock, time::Duration}; + +use redis::{aio::MultiplexedConnection as RedisConnection, Client as RedisClient, RedisResult}; +use sqlx::{postgres::PgPoolOptions, Pool, Postgres}; +use crate::error::SirenResult; + +static POOL: OnceLock> = OnceLock::new(); +static REDIS: OnceLock = OnceLock::new(); + +pub async fn initialize() -> SirenResult<()> { + log::info!("Initializing database..."); + let db_user = std::env::var("DATABASE_USER").unwrap_or("siren".to_string()); + let db_password = std::env::var("DATABASE_PASSWORD").expect("DATABASE_PASSWORD must be set"); + let db_host: String = std::env::var("DATABASE_HOST").expect("DATABASE_HOST must be set"); + let db_port = std::env::var("DATABASE_PORT").unwrap_or("5432".to_string()); + let db_name = std::env::var("DATABASE_NAME").unwrap_or("siren".to_string()); + + // Setup Postgres pool connection + let pool = PgPoolOptions::new() + .max_connections(5) + .acquire_timeout(Duration::from_secs(30)) + .connect(&format!( + "postgres://{}:{}@{}:{}/{}", + db_user, db_password, db_host, db_port, db_name + )) + .await?; + match POOL.set(pool) { + Ok(_) => {} + Err(_) => { + log::warn!("Database pool already initialized"); + } + } + + // Setup Redis connection + let redis = { + let host = std::env::var("REDIS_HOST").unwrap_or("localhost".to_string()); + let port = std::env::var("REDIS_PORT").unwrap_or("6379".to_string()); + let url = format!("redis://{}:{}", host, port); + RedisClient::open(url).expect("Failed to create redis client") + }; + match REDIS.set(redis) { + Ok(_) => {} + Err(_) => { + log::warn!("Redis client already initialized"); + } + } + + // Run migrations + match run_migrations().await { + Ok(_) => log::debug!("Successfully ran migrations"), + Err(e) => log::error!("Failed to run migrations: {}", e), + } + + log::info!("Database initialized"); + Ok(()) +} + +pub fn pool() -> &'static Pool { + POOL.get().unwrap() +} + +fn redis() -> &'static RedisClient { + REDIS.get().unwrap() +} + +pub fn redis_connection() -> RedisResult { + let conn = redis().get_connection()?; + Ok(conn) +} + +pub async fn redis_async_connection() -> RedisResult { + let conn = redis().get_multiplexed_async_connection().await?; + Ok(conn) +} + +async fn run_migrations() -> SirenResult<()> { + log::debug!("Running migrations"); + let pool = pool(); + sqlx::migrate!().run(pool).await?; + Ok(()) +} diff --git a/src/dnd/spells/mod.rs b/src/dnd/spells/mod.rs index e3e29db..a6dec23 100644 --- a/src/dnd/spells/mod.rs +++ b/src/dnd/spells/mod.rs @@ -7,7 +7,6 @@ use std::{ io::BufReader, }; -use log::{warn, trace}; pub use model::*; pub use types::*; @@ -29,25 +28,25 @@ pub fn load_data(data_dir_path: &str) { match result { Ok(spells) => { for spell in spells { - let mut filters = QueryFilters::default(); - filters.by_name = Some(spell.name.clone()); - match QuerySpell::get_all(&filters, 100, 1) { - Ok(spells) => { - if spells.len() > 0 { - trace!("Spell '{}' already exists", spell.name); - continue; - } - } - Err(err) => { - warn!("Error checking if spell '{}' exists: {}", spell.name, err); - continue; - } - }; - let spell = InsertSpell::insert(spell.into()).unwrap(); - trace!("Inserted spell: {}", spell.name); + // let mut filters = QueryFilters::default(); + // filters.by_name = Some(spell.name.clone()); + // match QuerySpell::get_all(&filters, 100, 1) { + // Ok(spells) => { + // if spells.len() > 0 { + // trace!("Spell '{}' already exists", spell.name); + // continue; + // } + // } + // Err(err) => { + // warn!("Error checking if spell '{}' exists: {}", spell.name, err); + // continue; + // } + // }; + // let spell = InsertSpell::insert(spell.into()).unwrap(); + // trace!("Inserted spell: {}", spell.name); } } - Err(err) => warn!("Error reading spells from file: {}", err), + Err(err) => log::warn!("Error reading spells from file: {}", err), }; } } @@ -55,7 +54,7 @@ pub fn load_data(data_dir_path: &str) { } } } else { - warn!( + log::warn!( "Data path '{}' does not exist, no data imported", data_dir_path ); diff --git a/src/dnd/spells/model.rs b/src/dnd/spells/model.rs index 65bca7d..eefa7d7 100644 --- a/src/dnd/spells/model.rs +++ b/src/dnd/spells/model.rs @@ -1,9 +1,5 @@ -use diesel::prelude::*; use serde::{Deserialize, Serialize}; -use crate::error::SirenResult; -use crate::storage::connection; -use crate::storage::schema::spells::{self}; use crate::dnd::{classes::AbilityType, conditions::ConditionType}; use super::{ @@ -11,8 +7,7 @@ use super::{ Source, Description, DurationType, Effect, }; -#[derive(Debug, Queryable, QueryableByName, Serialize, Deserialize)] -#[diesel(table_name = spells)] +#[derive(Debug, Serialize, Deserialize)] pub struct QuerySpell { pub id: i32, pub name: String, @@ -30,216 +25,6 @@ pub struct QuerySpell { } #[derive(Debug)] -pub struct QueryFilters { - pub by_name: Option, - pub like_name: Option, - pub by_schools: Option>, - pub by_levels: Option>, - pub by_ritual: Option, - pub by_concentration: Option, - pub by_classes: Option>, - pub by_damage_inflict: Option>, - pub by_damage_resist: Option>, - pub by_conditions: Option>, - pub by_saving_throw: Option>, - pub by_attack_type: Option, -} - -impl Default for QueryFilters { - fn default() -> Self { - Self { - by_name: None, - like_name: None, - by_schools: None, - by_levels: None, - by_ritual: None, - by_concentration: None, - by_classes: None, - by_damage_inflict: None, - by_damage_resist: None, - by_conditions: None, - by_saving_throw: None, - by_attack_type: None, - } - } -} - -impl QuerySpell { - pub fn get_all(filters: &QueryFilters, limit: i32, page: i32) -> SirenResult> { - let mut conn = connection()?; - let mut query = spells::table.limit(limit as i64).into_boxed(); - // Limit query to page and limit - let offset = (page - 1) * limit; - query = query.offset(offset as i64); - if let Some(name) = &filters.by_name { - query = query.filter(spells::name.eq(name)); - } - if let Some(name) = &filters.like_name { - query = query.filter(spells::name.ilike(format!("%{}%", name))); - } - if let Some(schools) = &filters.by_schools { - query = query.filter( - spells::school.eq_any( - schools - .iter() - .map(|school| school.to_string()) - .collect::>(), - ), - ); - } - if let Some(levels) = &filters.by_levels { - query = query.filter(spells::level.eq_any(levels)); - } - if let Some(ritual) = filters.by_ritual { - query = query.filter(spells::ritual.eq(ritual)); - } - if let Some(concentration) = filters.by_concentration { - query = query.filter(spells::concentration.eq(concentration)); - } - if let Some(classes) = &filters.by_classes { - query = query.filter(spells::classes.overlaps_with(classes)); - } - if let Some(damage_inflict) = &filters.by_damage_inflict { - query = query.filter( - spells::damage_inflict.overlaps_with( - damage_inflict - .iter() - .map(|damage_inflict| damage_inflict.to_string()) - .collect::>(), - ), - ); - } - if let Some(damage_resist) = &filters.by_damage_resist { - query = query.filter( - spells::damage_resist.overlaps_with( - damage_resist - .iter() - .map(|damage_resist| damage_resist.to_string()) - .collect::>(), - ), - ); - } - if let Some(conditions) = &filters.by_conditions { - query = query.filter( - spells::conditions.overlaps_with( - conditions - .iter() - .map(|condition| condition.to_string()) - .collect::>(), - ), - ); - } - if let Some(saving_throw) = &filters.by_saving_throw { - query = query.filter( - spells::saving_throw.overlaps_with( - saving_throw - .iter() - .map(|saving_throw| saving_throw.to_string()) - .collect::>(), - ), - ); - } - if let Some(attack_type) = &filters.by_attack_type { - query = query.filter(spells::attack_type.eq(attack_type.to_string())); - } - - let spells = query.load::(&mut conn)?; - Ok(spells) - } - - pub fn get_count(filters: &QueryFilters) -> SirenResult { - let mut conn = connection()?; - let mut query = spells::table.count().into_boxed(); - if let Some(name) = &filters.by_name { - query = query.filter(spells::name.ilike(format!("%{}%", name))); - } - if let Some(schools) = &filters.by_schools { - query = query.filter( - spells::school.eq_any( - schools - .iter() - .map(|school| school.to_string()) - .collect::>(), - ), - ); - } - if let Some(levels) = &filters.by_levels { - query = query.filter(spells::level.eq_any(levels)); - } - if let Some(ritual) = filters.by_ritual { - query = query.filter(spells::ritual.eq(ritual)); - } - if let Some(concentration) = filters.by_concentration { - query = query.filter(spells::concentration.eq(concentration)); - } - if let Some(classes) = &filters.by_classes { - query = query.filter(spells::classes.overlaps_with(classes)); - } - if let Some(damage_inflict) = &filters.by_damage_inflict { - query = query.filter( - spells::damage_inflict.overlaps_with( - damage_inflict - .iter() - .map(|damage_inflict| damage_inflict.to_string()) - .collect::>(), - ), - ); - } - if let Some(damage_resist) = &filters.by_damage_resist { - query = query.filter( - spells::damage_resist.overlaps_with( - damage_resist - .iter() - .map(|damage_resist| damage_resist.to_string()) - .collect::>(), - ), - ); - } - if let Some(conditions) = &filters.by_conditions { - query = query.filter( - spells::conditions.overlaps_with( - conditions - .iter() - .map(|condition| condition.to_string()) - .collect::>(), - ), - ); - } - if let Some(saving_throw) = &filters.by_saving_throw { - query = query.filter( - spells::saving_throw.overlaps_with( - saving_throw - .iter() - .map(|saving_throw| saving_throw.to_string()) - .collect::>(), - ), - ); - } - if let Some(attack_type) = &filters.by_attack_type { - query = query.filter(spells::attack_type.eq(attack_type.to_string())); - } - - let count = query.get_result(&mut conn)?; - Ok(count) - } - - pub fn get_by_id(id: i32) -> SirenResult { - let mut conn = connection()?; - let spell = spells::table - .filter(spells::id.eq(id)) - .first::(&mut conn)?; - Ok(spell) - } - - pub fn delete(id: i32) -> SirenResult { - let mut conn = connection()?; - let spell = diesel::delete(spells::table.filter(spells::id.eq(id))).get_result(&mut conn)?; - Ok(spell) - } -} - -#[derive(Debug, Insertable, AsChangeset)] -#[diesel(table_name = spells)] pub struct InsertSpell { pub name: String, pub school: String, @@ -255,24 +40,6 @@ pub struct InsertSpell { pub data: serde_json::Value, } -impl InsertSpell { - pub fn insert(spell: Self) -> SirenResult { - let mut conn = connection()?; - let spell = diesel::insert_into(spells::table) - .values(spell) - .get_result(&mut conn)?; - Ok(spell) - } - - pub fn update(id: i32, spell: Self) -> SirenResult { - let mut conn = connection()?; - let spell = diesel::update(spells::table.filter(spells::id.eq(id))) - .set(spell) - .get_result(&mut conn)?; - Ok(spell) - } -} - #[derive(Debug, Serialize, Deserialize)] pub struct Spell { pub id: Option, diff --git a/src/error.rs b/src/error.rs index 0f81a78..a6fa1be 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,4 @@ use std::fmt; -use diesel::result::Error as DieselError; use serde::{Deserialize, Serialize}; pub type SirenResult = Result; @@ -37,22 +36,47 @@ impl From for Error { } } -impl From for Error { - fn from(error: DieselError) -> Self { +impl From for Error { + fn from(error: sqlx::Error) -> Self { match error { - DieselError::DatabaseError(kind, err) => match kind { - diesel::result::DatabaseErrorKind::UniqueViolation => { - Self::new(409, err.message().to_string()) + sqlx::Error::RowNotFound => Error::new(404, "Not found".to_string()), + sqlx::Error::ColumnIndexOutOfBounds { .. } => Error::new(422, error.to_string()), + sqlx::Error::ColumnNotFound { .. } => Error::new(422, error.to_string()), + sqlx::Error::ColumnDecode { .. } => Error::new(422, error.to_string()), + sqlx::Error::Decode(_) => Error::new(422, error.to_string()), + sqlx::Error::PoolTimedOut => Error::new(503, error.to_string()), + sqlx::Error::PoolClosed => Error::new(503, error.to_string()), + sqlx::Error::Tls(_) => Error::new(500, error.to_string()), + sqlx::Error::Io(_) => Error::new(500, error.to_string()), + sqlx::Error::Protocol(_) => Error::new(500, error.to_string()), + sqlx::Error::Configuration(_) => Error::new(500, error.to_string()), + sqlx::Error::AnyDriverError(_) => Error::new(500, error.to_string()), + sqlx::Error::Database(err) => { + if let Some(code) = err.code() { + match code.trim() { + // Unique violation + "23505" => return Error::new(409, err.to_string()), + _ => (), + } } - _ => Self::new(500, err.message().to_string()), - }, - DieselError::NotFound => Self::new(404, "The record was not found".to_string()), - DieselError::SerializationError(err) => Self::new(422, err.to_string()), - err => Self::new(500, format!("Unknown database error: {}", err)), + Error::new(500, err.to_string()) + } + sqlx::Error::Migrate(_) => Error::new(500, error.to_string()), + sqlx::Error::TypeNotFound { type_name } => { + Error::new(500, format!("Type not found: {}", type_name)) + } + sqlx::Error::WorkerCrashed => Error::new(500, error.to_string()), + _ => Error::new(500, error.to_string()), } } } +impl From for Error { + fn from(error: sqlx::migrate::MigrateError) -> Self { + Error::new(500, error.to_string()) + } +} + impl From for Error { fn from(error: reqwest::Error) -> Self { Self::new(500, format!("Unknown reqwest error: {}", error)) diff --git a/src/main.rs b/src/main.rs index c491b3e..780970b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,3 @@ -extern crate diesel; -#[macro_use] -extern crate diesel_migrations; - use std::env; use std::collections::HashSet; use std::sync::Arc; @@ -13,9 +9,9 @@ use reqwest::Client as HttpClient; use crate::bot::handler::Handler; mod bot; +mod database; mod dnd; mod error; -mod storage; pub struct HttpKey; @@ -27,7 +23,10 @@ impl TypeMapKey for HttpKey { async fn main() { dotenv::dotenv().ok(); env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,siren=info")); - storage::init().await; + if let Err(err) = database::initialize().await { + log::error!("Failed to initialize database: {err}"); + return; + }; let token: String = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); let intents: GatewayIntents = GatewayIntents::all(); diff --git a/src/storage/mod.rs b/src/storage/mod.rs deleted file mode 100644 index 0350a48..0000000 --- a/src/storage/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -use diesel::{r2d2::ConnectionManager as DieselConnectionManager, PgConnection}; -use redis::{aio::MultiplexedConnection, Client as RedisClient}; -use crate::{diesel_migrations::MigrationHarness, error::{Error as SirenError, SirenResult}}; -use lazy_static::lazy_static; -use log::{error, info}; -use r2d2; -use std::env; - -pub mod schema; - -type DbPool = r2d2::Pool>; -pub type DbConnection = r2d2::PooledConnection>; - -pub const MIGRATIONS: diesel_migrations::EmbeddedMigrations = embed_migrations!(); - -lazy_static! { - static ref POOL: DbPool = { - let username = env::var("DATABASE_USER").expect("DATABASE_USERNAME is not set"); - let password = env::var("DATABASE_PASSWORD").expect("DATABASE_PASSWORD is not set"); - let host = env::var("DATABASE_HOST").unwrap_or("localhost".to_string()); - let name = env::var("DATABASE_NAME").expect("DATABASE_NAME is not set"); - let port = env::var("DATABASE_PORT").unwrap_or("5432".to_string()); - let url = format!( - "postgres://{}:{}@{}:{}/{}", - username, password, host, port, name - ); - let manager = DieselConnectionManager::::new(url); - DbPool::builder() - .test_on_check_out(true) - .build(manager) - .expect("Failed to create db pool") - }; - static ref REDIS: RedisClient = { - let host = env::var("REDIS_HOST").unwrap_or("localhost".to_string()); - let port = env::var("REDIS_PORT").unwrap_or("6379".to_string()); - let url = format!("redis://{}:{}", host, port); - RedisClient::open(url).expect("Failed to create redis client") - }; -} - -pub async fn init() { - lazy_static::initialize(&POOL); - lazy_static::initialize(&REDIS); - let mut pool: DbConnection = connection().expect("Failed to get db connection"); - match pool.run_pending_migrations(MIGRATIONS) { - Ok(_) => info!("Database initialized"), - Err(err) => error!("Failed to initialize database; {}", err), - }; -} - -pub fn connection() -> SirenResult { - POOL - .get() - .map_err(|e| SirenError::new(500, format!("Failed getting db connection: {}", e))) -} - -pub fn redis_connection() -> SirenResult { - let conn = REDIS.get_connection()?; - Ok(conn) -} - -pub async fn redis_async_connection() -> SirenResult { - let conn = REDIS.get_multiplexed_async_connection().await?; - Ok(conn) -} diff --git a/src/storage/schema.rs b/src/storage/schema.rs deleted file mode 100644 index 29f6538..0000000 --- a/src/storage/schema.rs +++ /dev/null @@ -1,54 +0,0 @@ -diesel::table! { - messages (id) { - id -> Text, - guild_id -> BigInt, - channel_id -> BigInt, - user_id -> BigInt, - created -> BigInt, - model -> Text, - request -> Text, - response -> Text, - request_tags -> Array, - response_tags -> Array, - } -} - -diesel::table! { - spells (id) { - id -> Integer, - name -> Text, - school -> Text, - level -> Integer, - ritual -> Bool, - concentration -> Bool, - classes -> Array, - damage_inflict -> Array, - damage_resist -> Array, - conditions -> Array, - saving_throw -> Array, - attack_type -> Nullable, - data -> Jsonb - } -} - -diesel::table! { - guilds (id) { - id -> BigInt, - bot_id -> BigInt, - volume -> Integer, - } -} - -diesel::table! { - users (email) { - email -> Text, - hash -> Text, - role -> Text, - first_name -> Text, - last_name -> Text, - updated_at -> Timestamp, - created_at -> Timestamp, - profile_picture -> Nullable, - verified -> Bool, - } -}