forked from jmug/stoatchat
Compare commits
19
Commits
0.5.5
...
20230103-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5b823ad13 | ||
|
|
27b4699299 | ||
|
|
ee1c4cc2d5 | ||
|
|
0e0daf86f7 | ||
|
|
572e08c733 | ||
|
|
9011939344 | ||
|
|
f4dcd091cc | ||
|
|
86ad72b426 | ||
|
|
044d82d566 | ||
|
|
deffe663cb | ||
|
|
e7f6433d77 | ||
|
|
345e4d5f13 | ||
|
|
81fc3e7760 | ||
|
|
ca8d2e6118 | ||
|
|
c1227e8e33 | ||
|
|
0e0c6f2c3a | ||
|
|
fa82be74b5 | ||
|
|
8491ced13d | ||
|
|
229d4e2e1d |
@@ -1,79 +0,0 @@
|
||||
# MongoDB URI
|
||||
MONGODB=mongodb://localhost
|
||||
|
||||
# URL to where the Revolt app is publicly accessible
|
||||
REVOLT_APP_URL=http://local.revolt.chat:5000
|
||||
|
||||
# URL to where the API is publicly accessible
|
||||
REVOLT_PUBLIC_URL=http://local.revolt.chat:8000
|
||||
VITE_API_URL=http://local.revolt.chat:8000
|
||||
|
||||
# URL to where the WebSocket server is publicly accessible
|
||||
REVOLT_EXTERNAL_WS_URL=ws://local.revolt.chat:9000
|
||||
|
||||
# URL to where Autumn is publicly available
|
||||
AUTUMN_PUBLIC_URL=http://local.revolt.chat:3000
|
||||
|
||||
# URL to where January is publicly available
|
||||
JANUARY_PUBLIC_URL=http://local.revolt.chat:7000
|
||||
|
||||
# URL to where Vortex is publicly available
|
||||
# VOSO_PUBLIC_URL=https://voso.revolt.chat
|
||||
|
||||
|
||||
##
|
||||
## hCaptcha Settings
|
||||
##
|
||||
|
||||
# If you are sure that you don't want to use hCaptcha, set to 1.
|
||||
REVOLT_UNSAFE_NO_CAPTCHA=1
|
||||
|
||||
# hCaptcha API key
|
||||
# REVOLT_HCAPTCHA_KEY=0x0000000000000000000000000000000000000000
|
||||
|
||||
# hCaptcha site key
|
||||
# REVOLT_HCAPTCHA_SITEKEY=10000000-ffff-ffff-ffff-000000000001
|
||||
|
||||
|
||||
##
|
||||
## Email Settings
|
||||
##
|
||||
|
||||
# If you are sure that you don't want to use email verification, set to 1.
|
||||
REVOLT_UNSAFE_NO_EMAIL=1
|
||||
|
||||
# SMTP host
|
||||
# REVOLT_SMTP_HOST=smtp.example.com
|
||||
|
||||
# SMTP username
|
||||
# REVOLT_SMTP_USERNAME=noreply@example.com
|
||||
|
||||
# SMTP password
|
||||
# REVOLT_SMTP_PASSWORD=CHANGEME
|
||||
|
||||
# SMTP From header
|
||||
# REVOLT_SMTP_FROM=Revolt <noreply@example.com>
|
||||
|
||||
|
||||
##
|
||||
## Application Settings
|
||||
##
|
||||
|
||||
# Whether to only allow users to sign up if they have an invite code
|
||||
REVOLT_INVITE_ONLY=0
|
||||
|
||||
# Maximum number of people that can be in a group chat
|
||||
REVOLT_MAX_GROUP_SIZE=150
|
||||
|
||||
# VAPID keys for push notifications
|
||||
# Generate using this guide: https://gitlab.insrt.uk/revolt/delta/-/wikis/vapid
|
||||
# --> Please replace these keys before going into production! <--
|
||||
REVOLT_VAPID_PRIVATE_KEY=LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJSUWpyTWxLRnBiVWhsUHpUbERvcEliYk1yeVNrNXpKYzVYVzIxSjJDS3hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnkrQkg2TGJQZ2hEa3pEempXOG0rUXVPM3pCajRXT1phdkR6ZU00c0pqbmFwd1psTFE0WAp1ZDh2TzVodU94QWhMQlU3WWRldVovWHlBdFpWZmNyQi9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
|
||||
REVOLT_VAPID_PUBLIC_KEY=BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQISwVO2HXrmf18gLWVX3Kwfw=
|
||||
|
||||
|
||||
##
|
||||
## Vortex configuration
|
||||
##
|
||||
|
||||
# VOSO_MANAGE_TOKEN=CHANGEME
|
||||
@@ -1,3 +1,6 @@
|
||||
Rocket.toml
|
||||
target
|
||||
.data
|
||||
.env
|
||||
|
||||
.vercel
|
||||
|
||||
Generated
+7
-5
@@ -1454,12 +1454,12 @@ checksum = "90f97a5f38dd3ccfbe7aa80f4a0c00930f21b922c74195be0201c51028f22dcf"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.2"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"hashbrown 0.11.2",
|
||||
"hashbrown 0.12.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -2505,7 +2505,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rauth"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/insertish/rauth?rev=9c3f9bb58a549a51546bd748274130ac4a667cb4#9c3f9bb58a549a51546bd748274130ac4a667cb4"
|
||||
source = "git+https://github.com/insertish/rauth?rev=cbdec69c684a4854fbedba9ed1f0e7ee466bf3f7#cbdec69c684a4854fbedba9ed1f0e7ee466bf3f7"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -2783,6 +2783,7 @@ dependencies = [
|
||||
"dotenv",
|
||||
"futures",
|
||||
"impl_ops",
|
||||
"indexmap",
|
||||
"iso8601-timestamp",
|
||||
"lazy_static",
|
||||
"linkify 0.8.1",
|
||||
@@ -2987,8 +2988,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocket_rauth"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/insertish/rauth?rev=9c3f9bb58a549a51546bd748274130ac4a667cb4#9c3f9bb58a549a51546bd748274130ac4a667cb4"
|
||||
source = "git+https://github.com/insertish/rauth?rev=cbdec69c684a4854fbedba9ed1f0e7ee466bf3f7#cbdec69c684a4854fbedba9ed1f0e7ee466bf3f7"
|
||||
dependencies = [
|
||||
"iso8601-timestamp",
|
||||
"okapi",
|
||||
"rauth",
|
||||
"rocket",
|
||||
|
||||
@@ -52,7 +52,7 @@ mobc-redis = { version = "0.7.0", default-features = false, features = ["async-s
|
||||
# web
|
||||
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
||||
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "9c3f9bb58a549a51546bd748274130ac4a667cb4" }
|
||||
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "cbdec69c684a4854fbedba9ed1f0e7ee466bf3f7" }
|
||||
|
||||
# spec generation
|
||||
schemars = "0.8.8"
|
||||
|
||||
@@ -2,13 +2,27 @@ use revolt_quark::{
|
||||
models::{channel::PartialChannel, Channel, User},
|
||||
perms, Db, EmptyResponse, Error, Permission, Ref, Result,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
/// # Query Parameters
|
||||
#[derive(Validate, Serialize, Deserialize, JsonSchema, FromForm)]
|
||||
pub struct OptionsChannelDelete {
|
||||
/// Whether to not send a leave message
|
||||
leave_silently: Option<bool>,
|
||||
}
|
||||
|
||||
/// # Close Channel
|
||||
///
|
||||
/// Deletes a server channel, leaves a group or closes a group.
|
||||
#[openapi(tag = "Channel Information")]
|
||||
#[delete("/<target>")]
|
||||
pub async fn req(db: &Db, user: User, target: Ref) -> Result<EmptyResponse> {
|
||||
#[delete("/<target>?<options..>")]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
user: User,
|
||||
target: Ref,
|
||||
options: OptionsChannelDelete,
|
||||
) -> Result<EmptyResponse> {
|
||||
let mut channel = target.as_channel(db).await?;
|
||||
let mut perms = perms(&user).channel(&channel);
|
||||
perms.throw_permission(db, Permission::ViewChannel).await?;
|
||||
@@ -27,7 +41,12 @@ pub async fn req(db: &Db, user: User, target: Ref) -> Result<EmptyResponse> {
|
||||
.await
|
||||
.map(|_| EmptyResponse),
|
||||
Channel::Group { .. } => channel
|
||||
.remove_user_from_group(db, &user.id, None)
|
||||
.remove_user_from_group(
|
||||
db,
|
||||
&user.id,
|
||||
None,
|
||||
options.leave_silently.unwrap_or_default(),
|
||||
)
|
||||
.await
|
||||
.map(|_| EmptyResponse),
|
||||
Channel::TextChannel { .. } | Channel::VoiceChannel { .. } => {
|
||||
|
||||
@@ -29,6 +29,8 @@ pub struct DataEditChannel {
|
||||
icon: Option<String>,
|
||||
/// Whether this channel is age-restricted
|
||||
nsfw: Option<bool>,
|
||||
/// Whether this channel is archived
|
||||
archived: Option<bool>,
|
||||
#[validate(length(min = 1))]
|
||||
remove: Option<Vec<FieldsChannel>>,
|
||||
}
|
||||
@@ -83,11 +85,12 @@ pub async fn req(
|
||||
}
|
||||
|
||||
// Transfer ownership
|
||||
*owner = new_owner.to_string();
|
||||
partial.owner = Some(new_owner.to_string());
|
||||
let old_owner = std::mem::replace(owner, new_owner.to_string());
|
||||
|
||||
// Notify clients
|
||||
SystemMessage::ChannelOwnershipChanged {
|
||||
from: owner.to_string(),
|
||||
from: old_owner,
|
||||
to: new_owner,
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -33,7 +33,7 @@ pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result<EmptyR
|
||||
}
|
||||
|
||||
channel
|
||||
.remove_user_from_group(db, &member.id, Some(&user.id))
|
||||
.remove_user_from_group(db, &member.id, Some(&user.id), false)
|
||||
.await
|
||||
.map(|_| EmptyResponse)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ pub async fn message_send(
|
||||
}
|
||||
|
||||
// 3. Ensure interactions information is correct
|
||||
message.interactions.validate(db).await?;
|
||||
message.interactions.validate(db, &mut permissions).await?;
|
||||
|
||||
// 4. Verify replies are valid.
|
||||
let mut replies = HashSet::new();
|
||||
|
||||
@@ -55,7 +55,7 @@ pub async fn create_emoji(
|
||||
// Check that there are no more than 100 emoji
|
||||
// ! FIXME: hardcoded upper limit
|
||||
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
||||
if emojis.len() > 100 {
|
||||
if emojis.len() > 99 {
|
||||
return Err(Error::TooManyEmoji);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ pub async fn req(
|
||||
) -> Result<EmptyResponse> {
|
||||
session.subscription = Some(data.into_inner());
|
||||
session
|
||||
.save(&rauth)
|
||||
.save(rauth)
|
||||
.await
|
||||
.map(|_| EmptyResponse)
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
|
||||
@@ -13,7 +13,7 @@ use rocket::State;
|
||||
pub async fn req(rauth: &State<RAuth>, mut session: Session) -> Result<EmptyResponse> {
|
||||
session.subscription = None;
|
||||
session
|
||||
.save(&rauth)
|
||||
.save(rauth)
|
||||
.await
|
||||
.map(|_| EmptyResponse)
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
|
||||
@@ -91,6 +91,16 @@ pub async fn req(
|
||||
required.push(Permission::AssignRoles);
|
||||
}
|
||||
|
||||
if data.timeout.is_some()
|
||||
|| data
|
||||
.remove
|
||||
.as_ref()
|
||||
.map(|x| x.contains(&FieldsMember::Timeout))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
required.push(Permission::TimeoutMembers);
|
||||
}
|
||||
|
||||
for permission in required {
|
||||
permissions.throw_permission(db, permission).await?;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use revolt_quark::{
|
||||
models::{Member, User},
|
||||
perms,
|
||||
presence::presence_filter_online,
|
||||
Db, Ref, Result,
|
||||
perms, Db, Ref, Result,
|
||||
};
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
@@ -47,16 +45,7 @@ pub async fn req(
|
||||
user_ids.push(member.id.user.clone());
|
||||
}
|
||||
|
||||
let online_ids = presence_filter_online(&user_ids).await;
|
||||
let mut users = db
|
||||
.fetch_users(&user_ids)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|mut user| {
|
||||
user.online = Some(online_ids.contains(&user.id));
|
||||
user.foreign()
|
||||
})
|
||||
.collect::<Vec<User>>();
|
||||
let mut users = User::fetch_foreign_users(db, &user_ids).await?;
|
||||
|
||||
// Ensure the lists match up exactly.
|
||||
members.sort_by(|a, b| a.id.user.cmp(&b.id.user));
|
||||
|
||||
@@ -34,7 +34,7 @@ pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result<EmptyR
|
||||
}
|
||||
|
||||
server
|
||||
.remove_member(db, member, RemovalIntention::Kick)
|
||||
.remove_member(db, member, RemovalIntention::Kick, false)
|
||||
.await
|
||||
.map(|_| EmptyResponse)
|
||||
}
|
||||
|
||||
@@ -2,13 +2,27 @@ use revolt_quark::{
|
||||
models::{server_member::RemovalIntention, User},
|
||||
Db, EmptyResponse, Ref, Result,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
/// # Query Parameters
|
||||
#[derive(Validate, Serialize, Deserialize, JsonSchema, FromForm)]
|
||||
pub struct OptionsServerDelete {
|
||||
/// Whether to not send a leave message
|
||||
leave_silently: Option<bool>,
|
||||
}
|
||||
|
||||
/// # Delete / Leave Server
|
||||
///
|
||||
/// Deletes a server if owner otherwise leaves.
|
||||
#[openapi(tag = "Server Information")]
|
||||
#[delete("/<target>")]
|
||||
pub async fn req(db: &Db, user: User, target: Ref) -> Result<EmptyResponse> {
|
||||
#[delete("/<target>?<options..>")]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
user: User,
|
||||
target: Ref,
|
||||
options: OptionsServerDelete,
|
||||
) -> Result<EmptyResponse> {
|
||||
let server = target.as_server(db).await?;
|
||||
let member = db.fetch_member(&target.id, &user.id).await?;
|
||||
|
||||
@@ -16,7 +30,12 @@ pub async fn req(db: &Db, user: User, target: Ref) -> Result<EmptyResponse> {
|
||||
server.delete(db).await
|
||||
} else {
|
||||
server
|
||||
.remove_member(db, member, RemovalIntention::Leave)
|
||||
.remove_member(
|
||||
db,
|
||||
member,
|
||||
RemovalIntention::Leave,
|
||||
options.leave_silently.unwrap_or_default(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
.map(|_| EmptyResponse)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use revolt_quark::{
|
||||
models::{
|
||||
server::{Category, FieldsServer, PartialServer, SystemMessageChannels},
|
||||
@@ -135,29 +137,44 @@ pub async fn req(
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Apply new icon
|
||||
// 2. Validate changes
|
||||
if let Some(system_messages) = &partial.system_messages {
|
||||
for id in system_messages.clone().into_channel_ids() {
|
||||
if !server.channels.contains(&id) {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(categories) = &mut partial.categories {
|
||||
let mut channel_ids = HashSet::new();
|
||||
for category in categories {
|
||||
for channel in &category.channels {
|
||||
if channel_ids.contains(channel) {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
channel_ids.insert(channel.to_string());
|
||||
}
|
||||
|
||||
category
|
||||
.channels
|
||||
.retain(|item| server.channels.contains(item));
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Apply new icon
|
||||
if let Some(icon) = icon {
|
||||
partial.icon = Some(File::use_server_icon(db, &icon, &server.id).await?);
|
||||
server.icon = partial.icon.clone();
|
||||
}
|
||||
|
||||
// 3. Apply new banner
|
||||
// 4. Apply new banner
|
||||
if let Some(banner) = banner {
|
||||
partial.banner = Some(File::use_banner(db, &banner, &server.id).await?);
|
||||
server.banner = partial.banner.clone();
|
||||
}
|
||||
|
||||
// 4. Validate changes
|
||||
if let Some(system_messages) = &partial.system_messages {
|
||||
let channels = system_messages.clone().into_channel_ids();
|
||||
if !db
|
||||
.check_channels_exist(&channels.into_iter().collect())
|
||||
.await?
|
||||
{
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
}
|
||||
|
||||
server
|
||||
.update(db, partial, remove.unwrap_or_default())
|
||||
.await?;
|
||||
|
||||
@@ -64,6 +64,7 @@ regex = "1.5.5"
|
||||
nanoid = "0.4.0"
|
||||
linkify = "0.8.1"
|
||||
dotenv = "0.15.0"
|
||||
indexmap = "1.9.1"
|
||||
impl_ops = "0.1.1"
|
||||
num_enum = "0.5.6"
|
||||
reqwest = "0.11.10"
|
||||
@@ -85,7 +86,7 @@ rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_emp
|
||||
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
||||
|
||||
# rAuth
|
||||
rauth = { git = "https://github.com/insertish/rauth", rev = "9c3f9bb58a549a51546bd748274130ac4a667cb4", features = [ "async-std-runtime" ] }
|
||||
rauth = { git = "https://github.com/insertish/rauth", rev = "cbdec69c684a4854fbedba9ed1f0e7ee466bf3f7", features = [ "async-std-runtime" ] }
|
||||
|
||||
# Sentry
|
||||
sentry = "0.25.0"
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<!-- Compiled with Bootstrap Email version: 1.3.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style type="text/css">
|
||||
body,table,td{font-family:Helvetica,Arial,sans-serif !important}.ExternalClass{width:100%}.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{line-height:150%}a{text-decoration:none}*{color:inherit}a[x-apple-data-detectors],u+#body a,#MessageViewBody a{color:inherit;text-decoration:none;font-size:inherit;font-family:inherit;font-weight:inherit;line-height:inherit}img{-ms-interpolation-mode:bicubic}table:not([class^=s-]){font-family:Helvetica,Arial,sans-serif;mso-table-lspace:0pt;mso-table-rspace:0pt;border-spacing:0px;border-collapse:collapse}table:not([class^=s-]) td{border-spacing:0px;border-collapse:collapse}@media screen and (max-width: 600px){.w-full,.w-full>tbody>tr>td{width:100% !important}.w-24,.w-24>tbody>tr>td{width:96px !important}.p-lg-10:not(table),.p-lg-10:not(.btn)>tbody>tr>td,.p-lg-10.btn td a{padding:0 !important}.p-3:not(table),.p-3:not(.btn)>tbody>tr>td,.p-3.btn td a{padding:12px !important}.p-6:not(table),.p-6:not(.btn)>tbody>tr>td,.p-6.btn td a{padding:24px !important}*[class*=s-lg-]>tbody>tr>td{font-size:0 !important;line-height:0 !important;height:0 !important}.s-4>tbody>tr>td{font-size:16px !important;line-height:16px !important;height:16px !important}.s-6>tbody>tr>td{font-size:24px !important;line-height:24px !important;height:24px !important}.s-10>tbody>tr>td{font-size:40px !important;line-height:40px !important;height:40px !important}}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||
<table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
|
||||
<table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600">
|
||||
<![endif]-->
|
||||
<table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px; margin: 0 auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="ax-center" role="presentation" align="center" border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||
<img alt="Revolt Logo" class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="card p-6 p-lg-10 space-y-4" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;" bgcolor="#ffffff">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 40px;" align="left" bgcolor="#ffffff">
|
||||
<h1 class="h3 fw-700" style="padding-top: 0; padding-bottom: 0; font-weight: 700 !important; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;" align="left">Account Deletion</h1>
|
||||
<table class="s-4 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 16px; font-size: 16px; width: 100%; height: 16px; margin: 0;" align="left" width="100%" height="16">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="" style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left">You requested to have your account deleted, if you did not perform this action please take measures to secure your account immediately.</p>
|
||||
<table class="s-4 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 16px; font-size: 16px; width: 100%; height: 16px; margin: 0;" align="left" width="100%" height="16">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="btn btn-primary p-3 fw-700" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; font-weight: 700 !important;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; border-radius: 6px; font-weight: 700 !important; margin: 0;" align="center" bgcolor="#0d6efd">
|
||||
<a href="{{url}}" style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: 700 !important; white-space: nowrap; background-color: #0d6efd; padding: 12px; border: 1px solid #0d6efd;">Confirm</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 24px; width: 100%; height: 24px; margin: 0;" align="left" width="100%" height="24">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||
This email is intended for {{email}}<br>
|
||||
Sent from Revolt<br>
|
||||
Made in the EU
|
||||
</div>
|
||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 24px; width: 100%; height: 24px; margin: 0;" align="left" width="100%" height="24">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Add custom classes and styles that you want inlined here */
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container">
|
||||
<img
|
||||
alt="Revolt Logo"
|
||||
class="ax-center my-10 w-24"
|
||||
src="https://app.revolt.chat/assets/logo_round.png"
|
||||
/>
|
||||
<div class="card p-6 p-lg-10 space-y-4">
|
||||
<h1 class="h3 fw-700">Account Deletion</h1>
|
||||
<p>
|
||||
You requested to have your account deleted, if you did not perform
|
||||
this action please take measures to secure your account immediately.
|
||||
</p>
|
||||
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Confirm</a>
|
||||
</div>
|
||||
<div class="text-muted text-center my-6">
|
||||
This email is intended for {{email}}<br />
|
||||
Sent from Revolt<br />
|
||||
Made in the EU
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,7 @@
|
||||
You requested to have your account deleted, if you did not perform this action please take measures to secure your account immediately.
|
||||
|
||||
Confirm here: {{url}}
|
||||
Please navigate to: {{url}}
|
||||
|
||||
Sent by Revolt.
|
||||
This email is intended for {{email}}
|
||||
Sent by Revolt
|
||||
Made in the EU
|
||||
|
||||
@@ -1,63 +1,138 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-GB">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Reset your password.</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
|
||||
<style type="text/css">
|
||||
a[x-apple-data-detectors] {color: inherit !important;}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 20px 0 30px 0;">
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border-collapse: collapse; border: 1px solid #cccccc;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#ff4654">
|
||||
<img src="https://revolt.chat/header.png" alt="Revolt logo" width="600" height="168" style="display: block;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="color: #153643; font-family: Arial, sans-serif;">
|
||||
<h1 style="font-size: 24px; margin: 0;">Reset your password!</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 24px; padding: 20px 0 0 0;">
|
||||
<p>
|
||||
You requested a password reset, if you didn't perform this action you can safely ignore this email.
|
||||
</p>
|
||||
<p style="margin: 0;">
|
||||
Reset your password by navigating to <a href="{{url}}">{{url}}</a>.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ff4654" style="padding: 30px 30px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="color: #ffffff; font-family: Arial, sans-serif; font-size: 14px;">
|
||||
<p style="margin: 0;">Sent by Revolt. · Website: <a style="color: white;" href="https://revolt.chat">https://revolt.chat</a></p>
|
||||
<p>Revolt is a user-first chat platform built with modern web technologies.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<!-- Compiled with Bootstrap Email version: 1.3.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style type="text/css">
|
||||
body,table,td{font-family:Helvetica,Arial,sans-serif !important}.ExternalClass{width:100%}.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{line-height:150%}a{text-decoration:none}*{color:inherit}a[x-apple-data-detectors],u+#body a,#MessageViewBody a{color:inherit;text-decoration:none;font-size:inherit;font-family:inherit;font-weight:inherit;line-height:inherit}img{-ms-interpolation-mode:bicubic}table:not([class^=s-]){font-family:Helvetica,Arial,sans-serif;mso-table-lspace:0pt;mso-table-rspace:0pt;border-spacing:0px;border-collapse:collapse}table:not([class^=s-]) td{border-spacing:0px;border-collapse:collapse}@media screen and (max-width: 600px){.w-full,.w-full>tbody>tr>td{width:100% !important}.w-24,.w-24>tbody>tr>td{width:96px !important}.p-lg-10:not(table),.p-lg-10:not(.btn)>tbody>tr>td,.p-lg-10.btn td a{padding:0 !important}.p-3:not(table),.p-3:not(.btn)>tbody>tr>td,.p-3.btn td a{padding:12px !important}.p-6:not(table),.p-6:not(.btn)>tbody>tr>td,.p-6.btn td a{padding:24px !important}*[class*=s-lg-]>tbody>tr>td{font-size:0 !important;line-height:0 !important;height:0 !important}.s-4>tbody>tr>td{font-size:16px !important;line-height:16px !important;height:16px !important}.s-6>tbody>tr>td{font-size:24px !important;line-height:24px !important;height:24px !important}.s-10>tbody>tr>td{font-size:40px !important;line-height:40px !important;height:40px !important}}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||
<table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
|
||||
<table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600">
|
||||
<![endif]-->
|
||||
<table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px; margin: 0 auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="ax-center" role="presentation" align="center" border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||
<img alt="Revolt Logo" class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="card p-6 p-lg-10 space-y-4" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;" bgcolor="#ffffff">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 40px;" align="left" bgcolor="#ffffff">
|
||||
<h1 class="h3 fw-700" style="padding-top: 0; padding-bottom: 0; font-weight: 700 !important; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;" align="left">Password Reset</h1>
|
||||
<table class="s-4 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 16px; font-size: 16px; width: 100%; height: 16px; margin: 0;" align="left" width="100%" height="16">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="" style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left">You requested a password reset, click below to continue.</p>
|
||||
<table class="s-4 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 16px; font-size: 16px; width: 100%; height: 16px; margin: 0;" align="left" width="100%" height="16">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="btn btn-primary p-3 fw-700" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; font-weight: 700 !important;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; border-radius: 6px; font-weight: 700 !important; margin: 0;" align="center" bgcolor="#0d6efd">
|
||||
<a href="{{url}}" style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: 700 !important; white-space: nowrap; background-color: #0d6efd; padding: 12px; border: 1px solid #0d6efd;">Reset</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 24px; width: 100%; height: 24px; margin: 0;" align="left" width="100%" height="24">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||
This email is intended for {{email}}<br>
|
||||
Sent from Revolt<br>
|
||||
Made in the EU
|
||||
</div>
|
||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 24px; width: 100%; height: 24px; margin: 0;" align="left" width="100%" height="24">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Add custom classes and styles that you want inlined here */
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container">
|
||||
<img
|
||||
class="ax-center my-10 w-24"
|
||||
src="https://app.revolt.chat/assets/logo_round.png"
|
||||
/>
|
||||
<div class="card p-6 p-lg-10 space-y-4">
|
||||
<h1 class="h3 fw-700">Password Reset</h1>
|
||||
<p>You requested a password reset, click below to continue.</p>
|
||||
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Reset</a>
|
||||
</div>
|
||||
<div class="text-muted text-center my-6">
|
||||
This email is intended for {{email}}<br />
|
||||
Sent from Revolt<br />
|
||||
Made in the EU
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,7 @@
|
||||
You requested a password reset, if you did not perform this action you can safely ignore this email.
|
||||
|
||||
Reset your password here: {{url}}
|
||||
Please navigate to: {{url}}
|
||||
|
||||
Sent by Revolt.
|
||||
This email is intended for {{email}}
|
||||
Sent by Revolt
|
||||
Made in the EU
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<h2>Reset your password.</h2>
|
||||
<p>
|
||||
You requested a password reset, if you did not perform this action you can safely ignore this email.
|
||||
</p>
|
||||
<p>
|
||||
Reset your password here: <a href="{{url}}">{{url}}</a>
|
||||
</p>
|
||||
<br/>
|
||||
<p>
|
||||
Sent by Revolt. · Website: <a href="https://revolt.chat">https://revolt.chat</a>
|
||||
</p>
|
||||
<p>
|
||||
Revolt is a user-first chat platform built with modern web technologies.
|
||||
</p>
|
||||
@@ -1,63 +1,138 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-GB">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Verify your account.</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
|
||||
<style type="text/css">
|
||||
a[x-apple-data-detectors] {color: inherit !important;}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 20px 0 30px 0;">
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border-collapse: collapse; border: 1px solid #cccccc;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#ff4654">
|
||||
<img src="https://revolt.chat/header.png" alt="Revolt logo" width="600" height="168" style="display: block;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="color: #153643; font-family: Arial, sans-serif;">
|
||||
<h1 style="font-size: 24px; margin: 0;">You're almost there!</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 24px; padding: 20px 0 0 0;">
|
||||
<p>
|
||||
Verify your account to be able to log into the platform. If you didn't perform this action you can safely ignore this email.
|
||||
</p>
|
||||
<p style="margin: 0;">
|
||||
Please verify your account by navigating to <a href="{{url}}">{{url}}</a>.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ff4654" style="padding: 30px 30px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="color: #ffffff; font-family: Arial, sans-serif; font-size: 14px;">
|
||||
<p style="margin: 0;">Sent by Revolt. · Website: <a style="color: white;" href="https://revolt.chat">https://revolt.chat</a></p>
|
||||
<p>Revolt is a user-first chat platform built with modern web technologies.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<!-- Compiled with Bootstrap Email version: 1.3.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style type="text/css">
|
||||
body,table,td{font-family:Helvetica,Arial,sans-serif !important}.ExternalClass{width:100%}.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{line-height:150%}a{text-decoration:none}*{color:inherit}a[x-apple-data-detectors],u+#body a,#MessageViewBody a{color:inherit;text-decoration:none;font-size:inherit;font-family:inherit;font-weight:inherit;line-height:inherit}img{-ms-interpolation-mode:bicubic}table:not([class^=s-]){font-family:Helvetica,Arial,sans-serif;mso-table-lspace:0pt;mso-table-rspace:0pt;border-spacing:0px;border-collapse:collapse}table:not([class^=s-]) td{border-spacing:0px;border-collapse:collapse}@media screen and (max-width: 600px){.w-full,.w-full>tbody>tr>td{width:100% !important}.w-24,.w-24>tbody>tr>td{width:96px !important}.p-lg-10:not(table),.p-lg-10:not(.btn)>tbody>tr>td,.p-lg-10.btn td a{padding:0 !important}.p-3:not(table),.p-3:not(.btn)>tbody>tr>td,.p-3.btn td a{padding:12px !important}.p-6:not(table),.p-6:not(.btn)>tbody>tr>td,.p-6.btn td a{padding:24px !important}*[class*=s-lg-]>tbody>tr>td{font-size:0 !important;line-height:0 !important;height:0 !important}.s-4>tbody>tr>td{font-size:16px !important;line-height:16px !important;height:16px !important}.s-6>tbody>tr>td{font-size:24px !important;line-height:24px !important;height:24px !important}.s-10>tbody>tr>td{font-size:40px !important;line-height:40px !important;height:40px !important}}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||
<table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
|
||||
<table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600">
|
||||
<![endif]-->
|
||||
<table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px; margin: 0 auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="ax-center" role="presentation" align="center" border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||
<img alt="Revolt Logo" class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="card p-6 p-lg-10 space-y-4" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;" bgcolor="#ffffff">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 40px;" align="left" bgcolor="#ffffff">
|
||||
<h1 class="h3 fw-700" style="padding-top: 0; padding-bottom: 0; font-weight: 700 !important; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;" align="left">Almost there!</h1>
|
||||
<table class="s-4 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 16px; font-size: 16px; width: 100%; height: 16px; margin: 0;" align="left" width="100%" height="16">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="" style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left">To complete your sign up, we just need to verify your email.</p>
|
||||
<table class="s-4 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 16px; font-size: 16px; width: 100%; height: 16px; margin: 0;" align="left" width="100%" height="16">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="btn btn-primary p-3 fw-700" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; font-weight: 700 !important;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 16px; border-radius: 6px; font-weight: 700 !important; margin: 0;" align="center" bgcolor="#0d6efd">
|
||||
<a href="{{url}}" style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: 700 !important; white-space: nowrap; background-color: #0d6efd; padding: 12px; border: 1px solid #0d6efd;">Confirm</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 24px; width: 100%; height: 24px; margin: 0;" align="left" width="100%" height="24">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||
This email is intended for {{email}}<br>
|
||||
Sent from Revolt<br>
|
||||
Made in the EU
|
||||
</div>
|
||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="line-height: 24px; font-size: 24px; width: 100%; height: 24px; margin: 0;" align="left" width="100%" height="24">
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Add custom classes and styles that you want inlined here */
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container">
|
||||
<img
|
||||
alt="Revolt Logo"
|
||||
class="ax-center my-10 w-24"
|
||||
src="https://app.revolt.chat/assets/logo_round.png"
|
||||
/>
|
||||
<div class="card p-6 p-lg-10 space-y-4">
|
||||
<h1 class="h3 fw-700">Almost there!</h1>
|
||||
<p>To complete your sign up, we just need to verify your email.</p>
|
||||
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Confirm</a>
|
||||
</div>
|
||||
<div class="text-muted text-center my-6">
|
||||
This email is intended for {{email}}<br />
|
||||
Sent from Revolt<br />
|
||||
Made in the EU
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,8 @@
|
||||
You're almost there!
|
||||
If you did not perform this action you can safely ignore this email.
|
||||
Almost there!
|
||||
To complete your sign up, we just need to verify your email.
|
||||
|
||||
Please verify your account here: {{url}}
|
||||
Please navigate to: {{url}}
|
||||
|
||||
Sent by Revolt.
|
||||
This email is intended for {{email}}
|
||||
Sent by Revolt
|
||||
Made in the EU
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<h2>You're almost there!</h2>
|
||||
<p>
|
||||
Verify your account to be able to log into the platform.<br/>
|
||||
If you did not perform this action you can safely ignore this email.
|
||||
</p>
|
||||
<p>
|
||||
Please verify your account here: <a href="{{url}}">{{url}}</a>
|
||||
</p>
|
||||
<br/>
|
||||
<p>
|
||||
Sent by Revolt. · Website: <a href="https://revolt.chat">https://revolt.chat</a>
|
||||
</p>
|
||||
<p>
|
||||
Revolt is a user-first chat platform built with modern web technologies.
|
||||
</p>
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::models::channel::{Channel, FieldsChannel, PartialChannel};
|
||||
use crate::{AbstractAttachment, AbstractChannel, Error, OverrideField, Result};
|
||||
|
||||
@@ -83,8 +81,4 @@ impl AbstractChannel for DummyDb {
|
||||
info!("Updating permissions for role {role} in {channel} with {permissions:?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_channels_exist(&self, _channels: &HashSet<String>) -> Result<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ impl Channel {
|
||||
db: &Database,
|
||||
user: &str,
|
||||
by: Option<&str>,
|
||||
silent: bool,
|
||||
) -> Result<()> {
|
||||
match &self {
|
||||
Channel::Group {
|
||||
@@ -329,20 +330,22 @@ impl Channel {
|
||||
.p(id.to_string())
|
||||
.await;
|
||||
|
||||
if let Some(by) = by {
|
||||
SystemMessage::UserRemove {
|
||||
id: user.to_string(),
|
||||
by: by.to_string(),
|
||||
}
|
||||
} else {
|
||||
SystemMessage::UserLeft {
|
||||
id: user.to_string(),
|
||||
if !silent {
|
||||
if let Some(by) = by {
|
||||
SystemMessage::UserRemove {
|
||||
id: user.to_string(),
|
||||
by: by.to_string(),
|
||||
}
|
||||
} else {
|
||||
SystemMessage::UserLeft {
|
||||
id: user.to_string(),
|
||||
}
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.create(db, self, None)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.create(db, self, None)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -13,13 +13,14 @@ use crate::{
|
||||
},
|
||||
Channel, Emoji, Message, User,
|
||||
},
|
||||
permissions::PermissionCalculator,
|
||||
presence::presence_filter_online,
|
||||
tasks::ack::AckEvent,
|
||||
types::{
|
||||
january::{Embed, Text},
|
||||
push::PushNotification,
|
||||
},
|
||||
Database, Error, Result,
|
||||
Database, Error, Permission, Result,
|
||||
};
|
||||
|
||||
impl Message {
|
||||
@@ -195,6 +196,11 @@ impl Message {
|
||||
|
||||
/// Add a reaction to a message
|
||||
pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> {
|
||||
// Check how many reactions are already on the message
|
||||
if self.reactions.len() >= 20 {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
// Check if the emoji is whitelisted
|
||||
if !self.interactions.can_use(emoji) {
|
||||
return Err(Error::InvalidOperation);
|
||||
@@ -377,7 +383,7 @@ impl BulkMessageResponse {
|
||||
) -> Result<BulkMessageResponse> {
|
||||
if let Some(true) = include_users {
|
||||
let user_ids = messages.get_user_ids();
|
||||
let users = db.fetch_users(&user_ids).await?;
|
||||
let users = User::fetch_foreign_users(db, &user_ids).await?;
|
||||
|
||||
Ok(match channel {
|
||||
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
|
||||
@@ -401,8 +407,14 @@ impl BulkMessageResponse {
|
||||
|
||||
impl Interactions {
|
||||
/// Validate interactions info is correct
|
||||
pub async fn validate(&self, db: &Database) -> Result<()> {
|
||||
pub async fn validate(
|
||||
&self,
|
||||
db: &Database,
|
||||
permissions: &mut PermissionCalculator<'_>,
|
||||
) -> Result<()> {
|
||||
if let Some(reactions) = &self.reactions {
|
||||
permissions.throw_permission(db, Permission::React).await?;
|
||||
|
||||
if reactions.len() > 20 {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use ulid::Ulid;
|
||||
|
||||
@@ -254,6 +256,7 @@ impl Server {
|
||||
db: &Database,
|
||||
member: Member,
|
||||
intention: RemovalIntention,
|
||||
silent: bool,
|
||||
) -> Result<()> {
|
||||
db.delete_member(&member.id).await?;
|
||||
|
||||
@@ -264,20 +267,22 @@ impl Server {
|
||||
.p(member.id.server)
|
||||
.await;
|
||||
|
||||
if let Some(id) = self.system_messages.as_ref().and_then(|x| match intention {
|
||||
RemovalIntention::Leave => x.user_left.as_ref(),
|
||||
RemovalIntention::Kick => x.user_kicked.as_ref(),
|
||||
RemovalIntention::Ban => x.user_banned.as_ref(),
|
||||
}) {
|
||||
match intention {
|
||||
RemovalIntention::Leave => SystemMessage::UserLeft { id: member.id.user },
|
||||
RemovalIntention::Kick => SystemMessage::UserKicked { id: member.id.user },
|
||||
RemovalIntention::Ban => SystemMessage::UserBanned { id: member.id.user },
|
||||
if !silent {
|
||||
if let Some(id) = self.system_messages.as_ref().and_then(|x| match intention {
|
||||
RemovalIntention::Leave => x.user_left.as_ref(),
|
||||
RemovalIntention::Kick => x.user_kicked.as_ref(),
|
||||
RemovalIntention::Ban => x.user_banned.as_ref(),
|
||||
}) {
|
||||
match intention {
|
||||
RemovalIntention::Leave => SystemMessage::UserLeft { id: member.id.user },
|
||||
RemovalIntention::Kick => SystemMessage::UserKicked { id: member.id.user },
|
||||
RemovalIntention::Ban => SystemMessage::UserBanned { id: member.id.user },
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.create_no_web_push(db, id, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.create_no_web_push(db, id, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -302,7 +307,7 @@ impl Server {
|
||||
member: Member,
|
||||
reason: Option<String>,
|
||||
) -> Result<ServerBan> {
|
||||
self.remove_member(db, member.clone(), RemovalIntention::Ban)
|
||||
self.remove_member(db, member.clone(), RemovalIntention::Ban, false)
|
||||
.await?;
|
||||
|
||||
self.ban_user(db, member.id, reason).await
|
||||
@@ -310,23 +315,23 @@ impl Server {
|
||||
}
|
||||
|
||||
impl SystemMessageChannels {
|
||||
pub fn into_channel_ids(self) -> Vec<String> {
|
||||
let mut ids = vec![];
|
||||
pub fn into_channel_ids(self) -> HashSet<String> {
|
||||
let mut ids = HashSet::new();
|
||||
|
||||
if let Some(id) = self.user_joined {
|
||||
ids.push(id);
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
if let Some(id) = self.user_left {
|
||||
ids.push(id);
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
if let Some(id) = self.user_kicked {
|
||||
ids.push(id);
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
if let Some(id) = self.user_banned {
|
||||
ids.push(id);
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
ids
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::models::user::{
|
||||
};
|
||||
use crate::permissions::defn::UserPerms;
|
||||
use crate::permissions::r#impl::user::get_relationship;
|
||||
use crate::presence::presence_filter_online;
|
||||
use crate::{perms, Database, Error, Result};
|
||||
|
||||
use futures::try_join;
|
||||
@@ -67,6 +68,7 @@ impl User {
|
||||
}
|
||||
|
||||
/// Mutate the user object to remove redundant information
|
||||
#[must_use]
|
||||
pub fn foreign(mut self) -> User {
|
||||
self.profile = None;
|
||||
self.relations = None;
|
||||
@@ -94,6 +96,21 @@ impl User {
|
||||
self
|
||||
}
|
||||
|
||||
/// Fetch foreign users by a list of IDs
|
||||
pub async fn fetch_foreign_users(db: &Database, user_ids: &[String]) -> Result<Vec<User>> {
|
||||
let online_ids = presence_filter_online(user_ids).await;
|
||||
|
||||
Ok(db
|
||||
.fetch_users(user_ids)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|mut user| {
|
||||
user.online = Some(online_ids.contains(&user.id));
|
||||
user.foreign()
|
||||
})
|
||||
.collect::<Vec<User>>())
|
||||
}
|
||||
|
||||
/// Mutate the user object to include relationship (if it does not already exist)
|
||||
#[must_use]
|
||||
pub fn with_relationship(self, perspective: &User) -> User {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bson::{Bson, Document};
|
||||
|
||||
use crate::models::channel::{Channel, FieldsChannel, PartialChannel};
|
||||
@@ -291,25 +289,6 @@ impl AbstractChannel for MongoDb {
|
||||
with: "channel",
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_channels_exist(&self, channels: &HashSet<String>) -> Result<bool> {
|
||||
let count = channels.len() as u64;
|
||||
self.col::<Document>(COL)
|
||||
.count_documents(
|
||||
doc! {
|
||||
"_id": {
|
||||
"$in": channels.iter().cloned().collect::<Vec<String>>()
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|x| x == count)
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "count_documents",
|
||||
with: "channel",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDocumentPath for FieldsChannel {
|
||||
|
||||
@@ -161,7 +161,7 @@ pub struct PartialChannel {
|
||||
}
|
||||
|
||||
/// Optional fields on channel object
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum FieldsChannel {
|
||||
Description,
|
||||
Icon,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::util::regex::RE_COLOUR;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
use iso8601_timestamp::Timestamp;
|
||||
|
||||
#[cfg(feature = "rocket_impl")]
|
||||
use rocket::FromFormField;
|
||||
|
||||
@@ -87,10 +86,8 @@ pub struct Masquerade {
|
||||
/// Replace the display role colour shown on this message
|
||||
///
|
||||
/// Must have `ManageRole` permission to use
|
||||
///
|
||||
/// This can be any valid CSS colour
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[validate(length(min = 1, max = 32))]
|
||||
#[validate(length(min = 1, max = 128), regex = "RE_COLOUR")]
|
||||
pub colour: Option<String>,
|
||||
}
|
||||
|
||||
@@ -99,7 +96,7 @@ pub struct Masquerade {
|
||||
pub struct Interactions {
|
||||
/// Reactions which should always appear and be distinct
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub reactions: Option<HashSet<String>>,
|
||||
pub reactions: Option<IndexSet<String>>,
|
||||
/// Whether reactions should be restricted to the given list
|
||||
#[serde(skip_serializing_if = "if_false", default)]
|
||||
pub restrict_reactions: bool,
|
||||
@@ -145,8 +142,8 @@ pub struct Message {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub replies: Option<Vec<String>>,
|
||||
/// Hashmap of emoji IDs to array of user IDs
|
||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
||||
pub reactions: HashMap<String, HashSet<String>>,
|
||||
#[serde(skip_serializing_if = "IndexMap::is_empty", default)]
|
||||
pub reactions: IndexMap<String, IndexSet<String>>,
|
||||
/// Information about how this message should be interacted with
|
||||
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
||||
pub interactions: Interactions,
|
||||
|
||||
@@ -134,7 +134,7 @@ pub struct Server {
|
||||
}
|
||||
|
||||
/// Optional fields on server object
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum FieldsServer {
|
||||
Description,
|
||||
Categories,
|
||||
@@ -144,7 +144,7 @@ pub enum FieldsServer {
|
||||
}
|
||||
|
||||
/// Optional fields on server object
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum FieldsRole {
|
||||
Colour,
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ pub struct Member {
|
||||
}
|
||||
|
||||
/// Optional fields on server member object
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum FieldsMember {
|
||||
Nickname,
|
||||
Avatar,
|
||||
|
||||
@@ -56,7 +56,7 @@ pub struct Bot {
|
||||
}
|
||||
|
||||
/// Optional fields on bot object
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq)]
|
||||
pub enum FieldsBot {
|
||||
Token,
|
||||
InteractionsURL,
|
||||
|
||||
@@ -10,7 +10,7 @@ pub fn if_false(t: &bool) -> bool {
|
||||
}
|
||||
|
||||
/// User's relationship with another user (or themselves)
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum RelationshipStatus {
|
||||
None,
|
||||
User,
|
||||
@@ -30,11 +30,17 @@ pub struct Relationship {
|
||||
}
|
||||
|
||||
/// Presence status
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Presence {
|
||||
/// User is online
|
||||
Online,
|
||||
/// User is not currently available
|
||||
Idle,
|
||||
/// User is focusing / will only receive mentions
|
||||
Focus,
|
||||
/// User is busy / will not receive any notifications
|
||||
Busy,
|
||||
/// User appears to be offline
|
||||
Invisible,
|
||||
}
|
||||
|
||||
@@ -157,7 +163,7 @@ pub struct User {
|
||||
}
|
||||
|
||||
/// Optional fields on user object
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum FieldsUser {
|
||||
Avatar,
|
||||
StatusText,
|
||||
|
||||
@@ -119,23 +119,28 @@ async fn calculate_channel_permission(
|
||||
}
|
||||
}
|
||||
Channel::DirectMessage { recipients, .. } => {
|
||||
// 2. Fetch user.
|
||||
let other_user = recipients
|
||||
.iter()
|
||||
.find(|x| x != &&data.perspective.id)
|
||||
.unwrap();
|
||||
// 2. Ensure we are a recipient.
|
||||
if recipients.contains(&data.perspective.id) {
|
||||
// 3. Fetch user.
|
||||
let other_user = recipients
|
||||
.iter()
|
||||
.find(|x| x != &&data.perspective.id)
|
||||
.unwrap();
|
||||
|
||||
let user = db.fetch_user(other_user).await?;
|
||||
data.user.set(user);
|
||||
let user = db.fetch_user(other_user).await?;
|
||||
data.user.set(user);
|
||||
|
||||
// 3. Calculate user permissions.
|
||||
let perms = data.calc_user(db).await;
|
||||
// 4. Calculate user permissions.
|
||||
let perms = data.calc_user(db).await;
|
||||
|
||||
// 4. Check if the user can send messages.
|
||||
if perms.get_send_message() {
|
||||
(*DEFAULT_PERMISSION_DIRECT_MESSAGE).into()
|
||||
// 5. Check if the user can send messages.
|
||||
if perms.get_send_message() {
|
||||
(*DEFAULT_PERMISSION_DIRECT_MESSAGE).into()
|
||||
} else {
|
||||
(*DEFAULT_PERMISSION_VIEW_ONLY).into()
|
||||
}
|
||||
} else {
|
||||
(*DEFAULT_PERMISSION_VIEW_ONLY).into()
|
||||
0_u64.into()
|
||||
}
|
||||
}
|
||||
Channel::Group {
|
||||
|
||||
@@ -147,7 +147,7 @@ pub async fn presence_clear_region(region_id: Option<&str>) {
|
||||
// also send out any relevant events.
|
||||
for session in sessions {
|
||||
let parts = session.split(':').collect::<Vec<&str>>();
|
||||
if let (Some(user_id), Some(session_id)) = (parts.get(0), parts.get(1)) {
|
||||
if let (Some(user_id), Some(session_id)) = (parts.first(), parts.get(1)) {
|
||||
if let Ok(session_id) = session_id.parse() {
|
||||
presence_delete_session_internal(user_id, session_id, true).await;
|
||||
}
|
||||
|
||||
@@ -53,5 +53,5 @@ pub async fn __get_set_sessions(conn: &mut Conn, region_id: &str) -> Vec<String>
|
||||
|
||||
/// Delete region session set
|
||||
pub async fn __delete_set_sessions(conn: &mut Conn, region_id: &str) {
|
||||
let _: () = conn.del(region_id).await.unwrap();
|
||||
conn.del::<_, ()>(region_id).await.unwrap();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::models::channel::{Channel, FieldsChannel, PartialChannel};
|
||||
use crate::{OverrideField, Result};
|
||||
|
||||
@@ -55,7 +53,4 @@ pub trait AbstractChannel: Sync + Send {
|
||||
role: &str,
|
||||
permissions: OverrideField,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Validate existence of channels
|
||||
async fn check_channels_exist(&self, channels: &HashSet<String>) -> Result<bool>;
|
||||
}
|
||||
|
||||
@@ -26,19 +26,19 @@ pub fn config() -> Config {
|
||||
title: "Verify your Revolt account.".into(),
|
||||
text: include_str!(crate::asset!("templates/verify.txt")).into(),
|
||||
url: format!("{}/login/verify/", *APP_URL),
|
||||
html: None,
|
||||
html: Some(include_str!(crate::asset!("templates/verify.html")).into()),
|
||||
},
|
||||
reset: Template {
|
||||
title: "Reset your Revolt password.".into(),
|
||||
text: include_str!(crate::asset!("templates/reset.txt")).into(),
|
||||
url: format!("{}/login/reset/", *APP_URL),
|
||||
html: None,
|
||||
html: Some(include_str!(crate::asset!("templates/reset.html")).into()),
|
||||
},
|
||||
deletion: Template {
|
||||
title: "Confirm account deletion.".into(),
|
||||
text: include_str!(crate::asset!("templates/deletion.txt")).into(),
|
||||
url: format!("{}/delete/", *APP_URL),
|
||||
html: None,
|
||||
html: Some(include_str!(crate::asset!("templates/deletion.html")).into()),
|
||||
},
|
||||
welcome: None,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user