Added a route for importing templates
This commit is contained in:
parent
b8ee99cb45
commit
0a9c390f32
@ -195,7 +195,7 @@ pub async fn initialize(
|
||||
routes::dashboard::api::guild::get_guild_roles,
|
||||
routes::dashboard::api::guild::get_guild_emojis,
|
||||
routes::dashboard::api::guild::get_reminder_templates,
|
||||
routes::dashboard::api::guild::create_reminder_template,
|
||||
routes::dashboard::api::guild::create_guild_reminder_template,
|
||||
routes::dashboard::api::guild::delete_reminder_template,
|
||||
routes::dashboard::api::guild::create_guild_reminder,
|
||||
routes::dashboard::api::guild::get_reminders,
|
||||
@ -204,11 +204,12 @@ pub async fn initialize(
|
||||
routes::dashboard::api::guild::todos::get_todo,
|
||||
routes::dashboard::api::guild::todos::update_todo,
|
||||
routes::dashboard::api::guild::todos::delete_todo,
|
||||
routes::dashboard::export::export_reminders,
|
||||
routes::dashboard::export::export_reminder_templates,
|
||||
routes::dashboard::export::export_todos,
|
||||
routes::dashboard::export::import_reminders,
|
||||
routes::dashboard::export::import_todos,
|
||||
routes::dashboard::export::reminders::export,
|
||||
routes::dashboard::export::reminders::import,
|
||||
routes::dashboard::export::reminder_templates::export,
|
||||
routes::dashboard::export::reminder_templates::import,
|
||||
routes::dashboard::export::todos::export,
|
||||
routes::dashboard::export::todos::import,
|
||||
],
|
||||
)
|
||||
.launch()
|
||||
|
@ -6,9 +6,12 @@ use rocket::{
|
||||
serde::json::{json, Json},
|
||||
State,
|
||||
};
|
||||
use serenity::all::GuildId;
|
||||
use serenity::client::Context;
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
use crate::web::guards::transaction::Transaction;
|
||||
use crate::web::routes::dashboard::create_reminder_template;
|
||||
use crate::web::{
|
||||
check_authorization,
|
||||
consts::{
|
||||
@ -49,109 +52,32 @@ pub async fn get_reminder_templates(
|
||||
}
|
||||
|
||||
#[post("/api/guild/<id>/templates", data = "<reminder_template>")]
|
||||
pub async fn create_reminder_template(
|
||||
pub async fn create_guild_reminder_template(
|
||||
id: u64,
|
||||
reminder_template: Json<ReminderTemplate>,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
mut transaction: Transaction<'_>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
// validate lengths
|
||||
check_length!(MAX_CONTENT_LENGTH, reminder_template.content);
|
||||
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder_template.embed_description);
|
||||
check_length!(MAX_EMBED_TITLE_LENGTH, reminder_template.embed_title);
|
||||
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder_template.embed_author);
|
||||
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder_template.embed_footer);
|
||||
check_length_opt!(MAX_EMBED_FIELDS, reminder_template.embed_fields);
|
||||
if let Some(fields) = &reminder_template.embed_fields {
|
||||
for field in &fields.0 {
|
||||
check_length!(MAX_EMBED_FIELD_VALUE_LENGTH, field.value);
|
||||
check_length!(MAX_EMBED_FIELD_TITLE_LENGTH, field.title);
|
||||
}
|
||||
}
|
||||
check_length_opt!(MAX_USERNAME_LENGTH, reminder_template.username);
|
||||
check_length_opt!(
|
||||
MAX_URL_LENGTH,
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.avatar
|
||||
);
|
||||
|
||||
// validate urls
|
||||
check_url_opt!(
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.avatar
|
||||
);
|
||||
|
||||
let name = if reminder_template.name.is_empty() {
|
||||
template_name_default()
|
||||
} else {
|
||||
reminder_template.name.clone()
|
||||
};
|
||||
|
||||
match sqlx::query!(
|
||||
"INSERT INTO reminder_template
|
||||
(guild_id,
|
||||
name,
|
||||
attachment,
|
||||
attachment_name,
|
||||
avatar,
|
||||
content,
|
||||
embed_author,
|
||||
embed_author_url,
|
||||
embed_color,
|
||||
embed_description,
|
||||
embed_footer,
|
||||
embed_footer_url,
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
interval_seconds,
|
||||
interval_days,
|
||||
interval_months,
|
||||
tts,
|
||||
username
|
||||
) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?)",
|
||||
id,
|
||||
name,
|
||||
reminder_template.attachment,
|
||||
reminder_template.attachment_name,
|
||||
reminder_template.avatar,
|
||||
reminder_template.content,
|
||||
reminder_template.embed_author,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_color,
|
||||
reminder_template.embed_description,
|
||||
reminder_template.embed_footer,
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_title,
|
||||
reminder_template.embed_fields,
|
||||
reminder_template.interval_seconds,
|
||||
reminder_template.interval_days,
|
||||
reminder_template.interval_months,
|
||||
reminder_template.tts,
|
||||
reminder_template.username,
|
||||
match create_reminder_template(
|
||||
ctx.inner(),
|
||||
&mut transaction,
|
||||
GuildId::new(id),
|
||||
reminder_template.into_inner(),
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(json!({})),
|
||||
Err(e) => {
|
||||
warn!("Could not create template for {}: {:?}", id, e);
|
||||
Ok(r) => match transaction.commit().await {
|
||||
Ok(_) => Ok(r),
|
||||
Err(e) => {
|
||||
warn!("Couldn't commit transaction: {:?}", e);
|
||||
json_err!("Couldn't commit transaction.")
|
||||
}
|
||||
},
|
||||
|
||||
json_err!("Could not create template")
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
|
3
src/web/routes/dashboard/export/mod.rs
Normal file
3
src/web/routes/dashboard/export/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod reminder_templates;
|
||||
pub mod reminders;
|
||||
pub mod todos;
|
181
src/web/routes/dashboard/export/reminder_templates.rs
Normal file
181
src/web/routes/dashboard/export/reminder_templates.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use crate::web::routes::dashboard::{create_reminder_template, ReminderTemplate};
|
||||
use crate::web::{
|
||||
check_authorization,
|
||||
guards::transaction::Transaction,
|
||||
routes::{
|
||||
dashboard::{
|
||||
create_reminder, CreateReminder, ImportBody, ReminderCsv, ReminderTemplateCsv, TodoCsv,
|
||||
},
|
||||
JsonResult,
|
||||
},
|
||||
};
|
||||
use crate::Database;
|
||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||
use csv::{QuoteStyle, WriterBuilder};
|
||||
use log::warn;
|
||||
use rocket::{
|
||||
get,
|
||||
http::CookieJar,
|
||||
put,
|
||||
serde::json::{json, Json},
|
||||
State,
|
||||
};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
model::id::{ChannelId, GuildId, UserId},
|
||||
};
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
#[get("/api/guild/<id>/export/reminder_templates")]
|
||||
pub async fn export(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
||||
|
||||
match sqlx::query_as_unchecked!(
|
||||
ReminderTemplateCsv,
|
||||
"SELECT
|
||||
name,
|
||||
attachment,
|
||||
attachment_name,
|
||||
avatar,
|
||||
content,
|
||||
embed_author,
|
||||
embed_author_url,
|
||||
embed_color,
|
||||
embed_description,
|
||||
embed_footer,
|
||||
embed_footer_url,
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
interval_seconds,
|
||||
interval_days,
|
||||
interval_months,
|
||||
tts,
|
||||
username
|
||||
FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||
id
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(templates) => {
|
||||
templates.iter().for_each(|template| {
|
||||
csv_writer.serialize(template).unwrap();
|
||||
});
|
||||
|
||||
match csv_writer.into_inner() {
|
||||
Ok(inner) => match String::from_utf8(inner) {
|
||||
Ok(encoded) => Ok(json!({ "body": encoded })),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to write UTF-8: {:?}", e);
|
||||
|
||||
json_err!("Failed to write UTF-8")
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to extract CSV: {:?}", e);
|
||||
|
||||
json_err!("Failed to extract CSV")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||
|
||||
json_err!("Failed to query templates")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/api/guild/<id>/export/reminder_templates", data = "<body>")]
|
||||
pub async fn import(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
body: Json<ImportBody>,
|
||||
ctx: &State<Context>,
|
||||
mut transaction: Transaction<'_>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
match BASE64_STANDARD.decode(&body.body) {
|
||||
Ok(body) => {
|
||||
let mut reader = csv::Reader::from_reader(body.as_slice());
|
||||
let mut count = 0;
|
||||
|
||||
for result in reader.deserialize::<ReminderTemplateCsv>() {
|
||||
match result {
|
||||
Ok(record) => {
|
||||
let reminder_template = ReminderTemplate {
|
||||
id: 0,
|
||||
guild_id: 0,
|
||||
name: record.name,
|
||||
attachment: record.attachment,
|
||||
attachment_name: record.attachment_name,
|
||||
avatar: record.avatar,
|
||||
content: record.content,
|
||||
embed_author: record.embed_author,
|
||||
embed_author_url: record.embed_author_url,
|
||||
embed_color: record.embed_color,
|
||||
embed_description: record.embed_description,
|
||||
embed_footer: record.embed_footer,
|
||||
embed_footer_url: record.embed_footer_url,
|
||||
embed_image_url: record.embed_image_url,
|
||||
embed_thumbnail_url: record.embed_thumbnail_url,
|
||||
embed_title: record.embed_title,
|
||||
embed_fields: record
|
||||
.embed_fields
|
||||
.map(|s| serde_json::from_str(&s).ok())
|
||||
.flatten(),
|
||||
interval_seconds: record.interval_seconds,
|
||||
interval_days: record.interval_days,
|
||||
interval_months: record.interval_months,
|
||||
tts: record.tts,
|
||||
username: record.username,
|
||||
};
|
||||
|
||||
create_reminder_template(
|
||||
ctx.inner(),
|
||||
&mut transaction,
|
||||
GuildId::new(id),
|
||||
reminder_template,
|
||||
)
|
||||
.await?;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't deserialize CSV row: {:?}", e);
|
||||
|
||||
return json_err!(format!("Deserialize error: {:?}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match transaction.commit().await {
|
||||
Ok(_) => Ok(json!({
|
||||
"message": format!("Imported {} reminder templates", count)
|
||||
})),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to commit transaction: {:?}", e);
|
||||
json_err!("Couldn't commit transaction")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
json_err!("Malformed base64")
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ use serenity::{
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
#[get("/api/guild/<id>/export/reminders")]
|
||||
pub async fn export_reminders(
|
||||
pub async fn export(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
@ -127,7 +127,7 @@ pub async fn export_reminders(
|
||||
}
|
||||
|
||||
#[put("/api/guild/<id>/export/reminders", data = "<body>")]
|
||||
pub(crate) async fn import_reminders(
|
||||
pub async fn import(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
body: Json<ImportBody>,
|
||||
@ -228,224 +228,3 @@ pub(crate) async fn import_reminders(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/guild/<id>/export/todos")]
|
||||
pub async fn export_todos(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
||||
|
||||
match sqlx::query_as_unchecked!(
|
||||
TodoCsv,
|
||||
"SELECT value, CONCAT('#', channels.channel) AS channel_id FROM todos
|
||||
LEFT JOIN channels ON todos.channel_id = channels.id
|
||||
INNER JOIN guilds ON todos.guild_id = guilds.id
|
||||
WHERE guilds.guild = ?",
|
||||
id
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(todos) => {
|
||||
todos.iter().for_each(|todo| {
|
||||
csv_writer.serialize(todo).unwrap();
|
||||
});
|
||||
|
||||
match csv_writer.into_inner() {
|
||||
Ok(inner) => match String::from_utf8(inner) {
|
||||
Ok(encoded) => Ok(json!({ "body": encoded })),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to write UTF-8: {:?}", e);
|
||||
|
||||
json_err!("Failed to write UTF-8")
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to extract CSV: {:?}", e);
|
||||
|
||||
json_err!("Failed to extract CSV")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||
|
||||
json_err!("Failed to query templates")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/api/guild/<id>/export/todos", data = "<body>")]
|
||||
pub async fn import_todos(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
body: Json<ImportBody>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||
|
||||
match channels_res {
|
||||
Ok(channels) => match BASE64_STANDARD.decode(&body.body) {
|
||||
Ok(body) => {
|
||||
let mut reader = csv::Reader::from_reader(body.as_slice());
|
||||
|
||||
let query_placeholder = "(?, (SELECT id FROM channels WHERE channel = ?), (SELECT id FROM guilds WHERE guild = ?))";
|
||||
let mut query_params = vec![];
|
||||
|
||||
for result in reader.deserialize::<TodoCsv>() {
|
||||
match result {
|
||||
Ok(record) => match record.channel_id {
|
||||
Some(channel_id) => {
|
||||
let channel_id = channel_id.split_at(1).1;
|
||||
|
||||
match channel_id.parse::<u64>() {
|
||||
Ok(channel_id) => {
|
||||
if channels.contains_key(&ChannelId::new(channel_id)) {
|
||||
query_params.push((record.value, Some(channel_id), id));
|
||||
} else {
|
||||
return json_err!(format!(
|
||||
"Invalid channel ID {}",
|
||||
channel_id
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
return json_err!(format!(
|
||||
"Invalid channel ID {}",
|
||||
channel_id
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
query_params.push((record.value, None, id));
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't deserialize CSV row: {:?}", e);
|
||||
|
||||
return json_err!("Deserialize error. Aborted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let query_str = format!(
|
||||
"INSERT INTO todos (value, channel_id, guild_id) VALUES {}",
|
||||
vec![query_placeholder].repeat(query_params.len()).join(",")
|
||||
);
|
||||
let mut query = sqlx::query(&query_str);
|
||||
|
||||
for param in query_params {
|
||||
query = query.bind(param.0).bind(param.1).bind(param.2);
|
||||
}
|
||||
|
||||
let res = query.execute(pool.inner()).await;
|
||||
|
||||
match res {
|
||||
Ok(_) => Ok(json!({})),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't execute todo query: {:?}", e);
|
||||
|
||||
json_err!("An unexpected error occured.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
json_err!("Malformed base64")
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't fetch channels for guild {}: {:?}", id, e);
|
||||
|
||||
json_err!("Couldn't fetch channels.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/guild/<id>/export/reminder_templates")]
|
||||
pub async fn export_reminder_templates(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
||||
|
||||
match sqlx::query_as_unchecked!(
|
||||
ReminderTemplateCsv,
|
||||
"SELECT
|
||||
name,
|
||||
attachment,
|
||||
attachment_name,
|
||||
avatar,
|
||||
content,
|
||||
embed_author,
|
||||
embed_author_url,
|
||||
embed_color,
|
||||
embed_description,
|
||||
embed_footer,
|
||||
embed_footer_url,
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
interval_seconds,
|
||||
interval_days,
|
||||
interval_months,
|
||||
tts,
|
||||
username
|
||||
FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||
id
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(templates) => {
|
||||
templates.iter().for_each(|template| {
|
||||
csv_writer.serialize(template).unwrap();
|
||||
});
|
||||
|
||||
match csv_writer.into_inner() {
|
||||
Ok(inner) => match String::from_utf8(inner) {
|
||||
Ok(encoded) => Ok(json!({ "body": encoded })),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to write UTF-8: {:?}", e);
|
||||
|
||||
json_err!("Failed to write UTF-8")
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to extract CSV: {:?}", e);
|
||||
|
||||
json_err!("Failed to extract CSV")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||
|
||||
json_err!("Failed to query templates")
|
||||
}
|
||||
}
|
||||
}
|
176
src/web/routes/dashboard/export/todos.rs
Normal file
176
src/web/routes/dashboard/export/todos.rs
Normal file
@ -0,0 +1,176 @@
|
||||
use crate::web::{
|
||||
check_authorization,
|
||||
guards::transaction::Transaction,
|
||||
routes::{
|
||||
dashboard::{
|
||||
create_reminder, CreateReminder, ImportBody, ReminderCsv, ReminderTemplateCsv, TodoCsv,
|
||||
},
|
||||
JsonResult,
|
||||
},
|
||||
};
|
||||
use crate::Database;
|
||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||
use csv::{QuoteStyle, WriterBuilder};
|
||||
use log::warn;
|
||||
use rocket::{
|
||||
get,
|
||||
http::CookieJar,
|
||||
put,
|
||||
serde::json::{json, Json},
|
||||
State,
|
||||
};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
model::id::{ChannelId, GuildId, UserId},
|
||||
};
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
#[get("/api/guild/<id>/export/todos")]
|
||||
pub async fn export(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
||||
|
||||
match sqlx::query_as_unchecked!(
|
||||
TodoCsv,
|
||||
"SELECT value, CONCAT('#', channels.channel) AS channel_id FROM todos
|
||||
LEFT JOIN channels ON todos.channel_id = channels.id
|
||||
INNER JOIN guilds ON todos.guild_id = guilds.id
|
||||
WHERE guilds.guild = ?",
|
||||
id
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(todos) => {
|
||||
todos.iter().for_each(|todo| {
|
||||
csv_writer.serialize(todo).unwrap();
|
||||
});
|
||||
|
||||
match csv_writer.into_inner() {
|
||||
Ok(inner) => match String::from_utf8(inner) {
|
||||
Ok(encoded) => Ok(json!({ "body": encoded })),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to write UTF-8: {:?}", e);
|
||||
|
||||
json_err!("Failed to write UTF-8")
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Failed to extract CSV: {:?}", e);
|
||||
|
||||
json_err!("Failed to extract CSV")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||
|
||||
json_err!("Failed to query templates")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/api/guild/<id>/export/todos", data = "<body>")]
|
||||
pub async fn import(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
body: Json<ImportBody>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||
|
||||
match channels_res {
|
||||
Ok(channels) => match BASE64_STANDARD.decode(&body.body) {
|
||||
Ok(body) => {
|
||||
let mut reader = csv::Reader::from_reader(body.as_slice());
|
||||
|
||||
let query_placeholder = "(?, (SELECT id FROM channels WHERE channel = ?), (SELECT id FROM guilds WHERE guild = ?))";
|
||||
let mut query_params = vec![];
|
||||
|
||||
for result in reader.deserialize::<TodoCsv>() {
|
||||
match result {
|
||||
Ok(record) => match record.channel_id {
|
||||
Some(channel_id) => {
|
||||
let channel_id = channel_id.split_at(1).1;
|
||||
|
||||
match channel_id.parse::<u64>() {
|
||||
Ok(channel_id) => {
|
||||
if channels.contains_key(&ChannelId::new(channel_id)) {
|
||||
query_params.push((record.value, Some(channel_id), id));
|
||||
} else {
|
||||
return json_err!(format!(
|
||||
"Invalid channel ID {}",
|
||||
channel_id
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
return json_err!(format!(
|
||||
"Invalid channel ID {}",
|
||||
channel_id
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
query_params.push((record.value, None, id));
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't deserialize CSV row: {:?}", e);
|
||||
|
||||
return json_err!("Deserialize error. Aborted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let query_str = format!(
|
||||
"INSERT INTO todos (value, channel_id, guild_id) VALUES {}",
|
||||
vec![query_placeholder].repeat(query_params.len()).join(",")
|
||||
);
|
||||
let mut query = sqlx::query(&query_str);
|
||||
|
||||
for param in query_params {
|
||||
query = query.bind(param.0).bind(param.1).bind(param.2);
|
||||
}
|
||||
|
||||
let res = query.execute(pool.inner()).await;
|
||||
|
||||
match res {
|
||||
Ok(_) => Ok(json!({})),
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't execute todo query: {:?}", e);
|
||||
|
||||
json_err!("An unexpected error occured.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
json_err!("Malformed base64")
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => {
|
||||
warn!("Couldn't fetch channels for guild {}: {:?}", id, e);
|
||||
|
||||
json_err!("Couldn't fetch channels.")
|
||||
}
|
||||
}
|
||||
}
|
@ -593,12 +593,113 @@ pub(crate) async fn create_reminder(
|
||||
}
|
||||
}
|
||||
|
||||
fn check_channel_matches_guild(ctx: &Context, channel_id: ChannelId, guild_id: GuildId) -> bool {
|
||||
return match ctx.cache.guild(guild_id) {
|
||||
Some(guild) => guild.channels.get(&channel_id).is_some(),
|
||||
pub(crate) async fn create_reminder_template(
|
||||
ctx: &Context,
|
||||
transaction: &mut Transaction<'_>,
|
||||
guild_id: GuildId,
|
||||
reminder_template: ReminderTemplate,
|
||||
) -> JsonResult {
|
||||
check_length!(MAX_CONTENT_LENGTH, reminder_template.content);
|
||||
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder_template.embed_description);
|
||||
check_length!(MAX_EMBED_TITLE_LENGTH, reminder_template.embed_title);
|
||||
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder_template.embed_author);
|
||||
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder_template.embed_footer);
|
||||
check_length_opt!(MAX_EMBED_FIELDS, reminder_template.embed_fields);
|
||||
if let Some(fields) = &reminder_template.embed_fields {
|
||||
for field in &fields.0 {
|
||||
check_length!(MAX_EMBED_FIELD_VALUE_LENGTH, field.value);
|
||||
check_length!(MAX_EMBED_FIELD_TITLE_LENGTH, field.title);
|
||||
}
|
||||
}
|
||||
check_length_opt!(MAX_USERNAME_LENGTH, reminder_template.username);
|
||||
check_length_opt!(
|
||||
MAX_URL_LENGTH,
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.avatar
|
||||
);
|
||||
|
||||
None => false,
|
||||
// validate urls
|
||||
check_url_opt!(
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.avatar
|
||||
);
|
||||
|
||||
let name = if reminder_template.name.is_empty() {
|
||||
template_name_default()
|
||||
} else {
|
||||
reminder_template.name.clone()
|
||||
};
|
||||
|
||||
match sqlx::query!(
|
||||
"INSERT INTO reminder_template
|
||||
(guild_id,
|
||||
name,
|
||||
attachment,
|
||||
attachment_name,
|
||||
avatar,
|
||||
content,
|
||||
embed_author,
|
||||
embed_author_url,
|
||||
embed_color,
|
||||
embed_description,
|
||||
embed_footer,
|
||||
embed_footer_url,
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
interval_seconds,
|
||||
interval_days,
|
||||
interval_months,
|
||||
tts,
|
||||
username
|
||||
) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?)",
|
||||
guild_id.get(),
|
||||
name,
|
||||
reminder_template.attachment,
|
||||
reminder_template.attachment_name,
|
||||
reminder_template.avatar,
|
||||
reminder_template.content,
|
||||
reminder_template.embed_author,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_color,
|
||||
reminder_template.embed_description,
|
||||
reminder_template.embed_footer,
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_title,
|
||||
reminder_template.embed_fields,
|
||||
reminder_template.interval_seconds,
|
||||
reminder_template.interval_days,
|
||||
reminder_template.interval_months,
|
||||
reminder_template.tts,
|
||||
reminder_template.username,
|
||||
)
|
||||
.fetch_all(transaction.executor())
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(json!({})),
|
||||
Err(e) => {
|
||||
warn!("Could not create template for {}: {:?}", guild_id.get(), e);
|
||||
|
||||
json_err!("Could not create template")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_channel_matches_guild(ctx: &Context, channel_id: ChannelId, guild_id: GuildId) -> bool {
|
||||
match ctx.cache.guild(guild_id) {
|
||||
Some(guild) => guild.channels.get(&channel_id).is_some(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_database_channel(
|
||||
|
Loading…
Reference in New Issue
Block a user