You've already forked AstralRinth
forked from didirus/AstralRinth
Merge commit '510a6977bec3c122382ed45788b7f6690770b56b' into feature-clean
This commit is contained in:
@@ -12,17 +12,21 @@ function hCaptchaReady() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.hCaptchaReady = hCaptchaReady;
|
if (window.hcaptcha) {
|
||||||
|
hCaptchaReady();
|
||||||
|
} else {
|
||||||
|
window.hCaptchaReady = hCaptchaReady;
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
script: [
|
script: [
|
||||||
{
|
{
|
||||||
src: "https://js.hcaptcha.com/1/api.js?render=explicit&onload=hCaptchaReady",
|
src: "https://js.hcaptcha.com/1/api.js?render=explicit&onload=hCaptchaReady",
|
||||||
async: true,
|
async: true,
|
||||||
defer: true,
|
defer: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
<span class="font-semibold"> Backup #{{ newBackupAmount }}</span>
|
<span class="font-semibold"> Backup #{{ newBackupAmount }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="isRateLimited" class="mt-2 text-sm text-red">
|
||||||
|
You're creating backups too fast. Please wait a moment before trying again.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-1 mt-4 flex justify-start gap-4">
|
<div class="mb-1 mt-4 flex justify-start gap-4">
|
||||||
<ButtonStyled color="brand">
|
<ButtonStyled color="brand">
|
||||||
@@ -36,10 +39,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, nextTick } from "vue";
|
import { ref, nextTick, computed } from "vue";
|
||||||
import { ButtonStyled, NewModal } from "@modrinth/ui";
|
import { ButtonStyled, NewModal } from "@modrinth/ui";
|
||||||
import { PlusIcon, XIcon, InfoIcon } from "@modrinth/assets";
|
import { PlusIcon, XIcon, InfoIcon } from "@modrinth/assets";
|
||||||
import type { Server } from "~/composables/pyroServers";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
server: Server<["general", "mods", "backups", "network", "startup", "ws", "fs"]>;
|
server: Server<["general", "mods", "backups", "network", "startup", "ws", "fs"]>;
|
||||||
@@ -50,6 +52,7 @@ const emit = defineEmits(["backupCreated"]);
|
|||||||
const modal = ref<InstanceType<typeof NewModal>>();
|
const modal = ref<InstanceType<typeof NewModal>>();
|
||||||
const input = ref<HTMLInputElement>();
|
const input = ref<HTMLInputElement>();
|
||||||
const isCreating = ref(false);
|
const isCreating = ref(false);
|
||||||
|
const isRateLimited = ref(false);
|
||||||
const backupError = ref<string | null>(null);
|
const backupError = ref<string | null>(null);
|
||||||
const backupName = ref("");
|
const backupName = ref("");
|
||||||
const newBackupAmount = computed(() =>
|
const newBackupAmount = computed(() =>
|
||||||
@@ -75,14 +78,20 @@ const createBackup = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isCreating.value = true;
|
isCreating.value = true;
|
||||||
|
isRateLimited.value = false;
|
||||||
try {
|
try {
|
||||||
await props.server.backups?.create(backupName.value);
|
await props.server.backups?.create(backupName.value);
|
||||||
await props.server.refresh();
|
await props.server.refresh();
|
||||||
hideModal();
|
hideModal();
|
||||||
emit("backupCreated", { success: true, message: "Backup created successfully" });
|
emit("backupCreated", { success: true, message: "Backup created successfully" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
backupError.value = error instanceof Error ? error.message : String(error);
|
if (error instanceof PyroFetchError && error.statusCode === 429) {
|
||||||
emit("backupCreated", { success: false, message: backupError.value });
|
isRateLimited.value = true;
|
||||||
|
backupError.value = "You're creating backups too fast.";
|
||||||
|
} else {
|
||||||
|
backupError.value = error instanceof Error ? error.message : String(error);
|
||||||
|
emit("backupCreated", { success: false, message: backupError.value });
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isCreating.value = false;
|
isCreating.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,6 +239,7 @@
|
|||||||
:is-server-running="isServerRunning"
|
:is-server-running="isServerRunning"
|
||||||
:stats="stats"
|
:stats="stats"
|
||||||
:server-power-state="serverPowerState"
|
:server-power-state="serverPowerState"
|
||||||
|
:power-state-details="powerStateDetails"
|
||||||
:console-output="throttledConsoleOutput"
|
:console-output="throttledConsoleOutput"
|
||||||
:socket="socket"
|
:socket="socket"
|
||||||
:server="server"
|
:server="server"
|
||||||
@@ -312,6 +313,8 @@ const ramData = ref<number[]>([]);
|
|||||||
const isActioning = ref(false);
|
const isActioning = ref(false);
|
||||||
const isServerRunning = computed(() => serverPowerState.value === "running");
|
const isServerRunning = computed(() => serverPowerState.value === "running");
|
||||||
const serverPowerState = ref<ServerState>("stopped");
|
const serverPowerState = ref<ServerState>("stopped");
|
||||||
|
const powerStateDetails = ref<{ oom_killed?: boolean; exit_code?: number }>();
|
||||||
|
|
||||||
const uptimeSeconds = ref(0);
|
const uptimeSeconds = ref(0);
|
||||||
const firstConnect = ref(true);
|
const firstConnect = ref(true);
|
||||||
const copied = ref(false);
|
const copied = ref(false);
|
||||||
@@ -488,7 +491,14 @@ const handleWebSocketMessage = (data: WSEvent) => {
|
|||||||
reauthenticate();
|
reauthenticate();
|
||||||
break;
|
break;
|
||||||
case "power-state":
|
case "power-state":
|
||||||
updatePowerState(data.state);
|
if (data.state === "crashed") {
|
||||||
|
updatePowerState(data.state, {
|
||||||
|
oom_killed: data.oom_killed,
|
||||||
|
exit_code: data.exit_code,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updatePowerState(data.state);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "installation-result":
|
case "installation-result":
|
||||||
handleInstallationResult(data);
|
handleInstallationResult(data);
|
||||||
@@ -606,9 +616,19 @@ const updateStats = (currentStats: Stats["current"]) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatePowerState = (state: ServerState) => {
|
const updatePowerState = (
|
||||||
console.log("Power state:", state);
|
state: ServerState,
|
||||||
|
details?: { oom_killed?: boolean; exit_code?: number },
|
||||||
|
) => {
|
||||||
|
console.log("Power state:", state, details);
|
||||||
serverPowerState.value = state;
|
serverPowerState.value = state;
|
||||||
|
|
||||||
|
if (state === "crashed") {
|
||||||
|
powerStateDetails.value = details;
|
||||||
|
} else {
|
||||||
|
powerStateDetails.value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (state === "stopped" || state === "crashed") {
|
if (state === "stopped" || state === "crashed") {
|
||||||
stopUptimeUpdates();
|
stopUptimeUpdates();
|
||||||
uptimeSeconds.value = 0;
|
uptimeSeconds.value = 0;
|
||||||
|
|||||||
@@ -66,7 +66,11 @@
|
|||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
class="w-full sm:w-fit"
|
class="w-full sm:w-fit"
|
||||||
:disabled="isServerRunning && !userPreferences.backupWhileRunning"
|
:disabled="
|
||||||
|
(isServerRunning && !userPreferences.backupWhileRunning) ||
|
||||||
|
data.used_backup_quota >= data.backup_quota ||
|
||||||
|
backups.some((backup) => backup.ongoing)
|
||||||
|
"
|
||||||
@click="showCreateModel"
|
@click="showCreateModel"
|
||||||
>
|
>
|
||||||
<PlusIcon class="h-5 w-5" />
|
<PlusIcon class="h-5 w-5" />
|
||||||
@@ -77,6 +81,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="backups.some((backup) => backup.ongoing)"
|
||||||
|
data-pyro-server-backup-ongoing
|
||||||
|
class="flex w-full flex-row items-center gap-4 rounded-2xl bg-bg-orange p-4 text-contrast"
|
||||||
|
>
|
||||||
|
A backup is currently being created. This may take a few minutes. This page will
|
||||||
|
automatically refresh when the backup is complete.
|
||||||
|
</div>
|
||||||
|
|
||||||
<li
|
<li
|
||||||
v-for="(backup, index) in backups"
|
v-for="(backup, index) in backups"
|
||||||
:key="backup.id"
|
:key="backup.id"
|
||||||
@@ -246,6 +259,8 @@ const backupSettingsModal = ref<typeof NewModal>();
|
|||||||
const renameBackupName = ref("");
|
const renameBackupName = ref("");
|
||||||
const currentBackup = ref("");
|
const currentBackup = ref("");
|
||||||
|
|
||||||
|
const refreshInterval = ref<ReturnType<typeof setInterval>>();
|
||||||
|
|
||||||
const currentBackupDetails = computed(() => {
|
const currentBackupDetails = computed(() => {
|
||||||
return backups.value.find((backup) => backup.id === currentBackup.value);
|
return backups.value.find((backup) => backup.id === currentBackup.value);
|
||||||
});
|
});
|
||||||
@@ -319,6 +334,29 @@ const initiateDownload = async (backupId: string) => {
|
|||||||
console.error("Download failed:", error);
|
console.error("Download failed:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watchEffect(() => {
|
||||||
|
const hasOngoingBackups = backups.value.some((backup) => backup.ongoing);
|
||||||
|
|
||||||
|
if (refreshInterval.value) {
|
||||||
|
clearInterval(refreshInterval.value);
|
||||||
|
refreshInterval.value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasOngoingBackups) {
|
||||||
|
refreshInterval.value = setInterval(() => {
|
||||||
|
props.server.refresh(["backups"]);
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (refreshInterval.value) {
|
||||||
|
clearInterval(refreshInterval.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -35,6 +35,31 @@
|
|||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="props.serverPowerState === 'crashed'" class="flex flex-row gap-4">
|
||||||
|
<IssuesIcon class="hidden h-8 w-8 text-red sm:block" />
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="font-semibold">{{ serverData?.name }} shut down unexpectedly.</div>
|
||||||
|
<div class="font-normal">
|
||||||
|
<template v-if="props.powerStateDetails?.oom_killed">
|
||||||
|
The server stopped because it ran out of memory. There may be a memory leak caused
|
||||||
|
by a mod or plugin, or you may need to upgrade your Modrinth Server.
|
||||||
|
</template>
|
||||||
|
<template v-else-if="props.powerStateDetails?.exit_code !== undefined">
|
||||||
|
We could not automatically determine the specific cause of the crash, but your
|
||||||
|
server exited with code
|
||||||
|
{{ props.powerStateDetails.exit_code }}.
|
||||||
|
{{
|
||||||
|
props.powerStateDetails.exit_code === 1
|
||||||
|
? "There may be a mod or plugin causing the issue, or an issue with your server configuration."
|
||||||
|
: ""
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template v-else> We could not determine the specific cause of the crash. </template>
|
||||||
|
<div class="mt-2">You can try restarting the server.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-else class="flex flex-row gap-4">
|
<div v-else class="flex flex-row gap-4">
|
||||||
<IssuesIcon class="hidden h-8 w-8 text-red sm:block" />
|
<IssuesIcon class="hidden h-8 w-8 text-red sm:block" />
|
||||||
|
|
||||||
@@ -169,6 +194,10 @@ type ServerProps = {
|
|||||||
stats: Stats;
|
stats: Stats;
|
||||||
consoleOutput: string[];
|
consoleOutput: string[];
|
||||||
serverPowerState: ServerState;
|
serverPowerState: ServerState;
|
||||||
|
powerStateDetails?: {
|
||||||
|
oom_killed?: boolean;
|
||||||
|
exit_code?: number;
|
||||||
|
};
|
||||||
isServerRunning: boolean;
|
isServerRunning: boolean;
|
||||||
server: Server<["general", "mods", "backups", "network", "startup", "ws", "fs"]>;
|
server: Server<["general", "mods", "backups", "network", "startup", "ws", "fs"]>;
|
||||||
};
|
};
|
||||||
|
|||||||
84
apps/frontend/src/public/promo-frame-rev-iq.html
Normal file
84
apps/frontend/src/public/promo-frame-rev-iq.html
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>Modrinth App Ad</title>
|
||||||
|
<script type="module" src="//js.rev.iq" data-domain="modrinth.com"></script>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ads-container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#plus-link {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modrinth-rail-1 {
|
||||||
|
border-radius: 1rem;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="ads-container">
|
||||||
|
<div id="plus-link"></div>
|
||||||
|
<div id="modrinth-rail-1" data-ad="app-rail" />
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// window.addEventListener(
|
||||||
|
// "message",
|
||||||
|
// (event) => {
|
||||||
|
// if (event.data.modrinthAdClick && window.__TAURI_INTERNALS__) {
|
||||||
|
// window.__TAURI_INTERNALS__.invoke("plugin:ads|record_ads_click", {});
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (event.data.modrinthOpenUrl && window.__TAURI_INTERNALS__) {
|
||||||
|
// window.__TAURI_INTERNALS__.invoke("plugin:ads|open_link", {
|
||||||
|
// path: event.data.modrinthOpenUrl,
|
||||||
|
// origin: event.origin,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// false,
|
||||||
|
// );
|
||||||
|
|
||||||
|
window.addEventListener("mousewheel", (event) => {
|
||||||
|
if (window.__TAURI_INTERNALS__) {
|
||||||
|
window.__TAURI_INTERNALS__.invoke("plugin:ads|scroll_ads_window", {
|
||||||
|
scroll: event.deltaY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("contextmenu", (event) => event.preventDefault());
|
||||||
|
|
||||||
|
const plusLink = document.getElementById("plus-link");
|
||||||
|
plusLink.addEventListener("click", function () {
|
||||||
|
window.__TAURI_INTERNALS__.invoke("plugin:ads|record_ads_click", {});
|
||||||
|
window.__TAURI_INTERNALS__.invoke("plugin:ads|open_link", {
|
||||||
|
path: "https://modrinth.com/plus",
|
||||||
|
origin: "https://modrinth.com",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -167,6 +167,9 @@ export interface WSAuthExpiringEvent {
|
|||||||
export interface WSPowerStateEvent {
|
export interface WSPowerStateEvent {
|
||||||
event: "power-state";
|
event: "power-state";
|
||||||
state: ServerState;
|
state: ServerState;
|
||||||
|
// if state "crashed"
|
||||||
|
oom_killed?: boolean;
|
||||||
|
exit_code?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WSAuthIncorrectEvent {
|
export interface WSAuthIncorrectEvent {
|
||||||
|
|||||||
@@ -86,8 +86,9 @@ SITE_VERIFY_EMAIL_PATH=none
|
|||||||
SITE_RESET_PASSWORD_PATH=none
|
SITE_RESET_PASSWORD_PATH=none
|
||||||
SITE_BILLING_PATH=none
|
SITE_BILLING_PATH=none
|
||||||
|
|
||||||
BEEHIIV_PUBLICATION_ID=none
|
SENDY_URL=none
|
||||||
BEEHIIV_API_KEY=none
|
SENDY_LIST_ID=none
|
||||||
|
SENDY_API_KEY=none
|
||||||
|
|
||||||
ANALYTICS_ALLOWED_ORIGINS='["http://127.0.0.1:3000", "http://localhost:3000", "https://modrinth.com", "https://www.modrinth.com", "*"]'
|
ANALYTICS_ALLOWED_ORIGINS='["http://127.0.0.1:3000", "http://localhost:3000", "https://modrinth.com", "https://www.modrinth.com", "*"]'
|
||||||
|
|
||||||
|
|||||||
@@ -460,8 +460,9 @@ pub fn check_env_vars() -> bool {
|
|||||||
failed |= check_var::<String>("SITE_RESET_PASSWORD_PATH");
|
failed |= check_var::<String>("SITE_RESET_PASSWORD_PATH");
|
||||||
failed |= check_var::<String>("SITE_BILLING_PATH");
|
failed |= check_var::<String>("SITE_BILLING_PATH");
|
||||||
|
|
||||||
failed |= check_var::<String>("BEEHIIV_PUBLICATION_ID");
|
failed |= check_var::<String>("SENDY_URL");
|
||||||
failed |= check_var::<String>("BEEHIIV_API_KEY");
|
failed |= check_var::<String>("SENDY_LIST_ID");
|
||||||
|
failed |= check_var::<String>("SENDY_API_KEY");
|
||||||
|
|
||||||
if parse_strings_from_var("ANALYTICS_ALLOWED_ORIGINS").is_none() {
|
if parse_strings_from_var("ANALYTICS_ALLOWED_ORIGINS").is_none() {
|
||||||
warn!(
|
warn!(
|
||||||
|
|||||||
@@ -1430,23 +1430,23 @@ pub async fn delete_auth_provider(
|
|||||||
Ok(HttpResponse::NoContent().finish())
|
Ok(HttpResponse::NoContent().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn sign_up_beehiiv(email: &str) -> Result<(), AuthenticationError> {
|
pub async fn sign_up_sendy(email: &str) -> Result<(), AuthenticationError> {
|
||||||
let id = dotenvy::var("BEEHIIV_PUBLICATION_ID")?;
|
let url = dotenvy::var("SENDY_URL")?;
|
||||||
let api_key = dotenvy::var("BEEHIIV_API_KEY")?;
|
let id = dotenvy::var("SENDY_LIST_ID")?;
|
||||||
|
let api_key = dotenvy::var("SENDY_API_KEY")?;
|
||||||
let site_url = dotenvy::var("SITE_URL")?;
|
let site_url = dotenvy::var("SITE_URL")?;
|
||||||
|
|
||||||
|
let mut form = HashMap::new();
|
||||||
|
|
||||||
|
form.insert("api_key", &*api_key);
|
||||||
|
form.insert("email", email);
|
||||||
|
form.insert("list", &*id);
|
||||||
|
form.insert("referrer", &*site_url);
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
client
|
client
|
||||||
.post(format!(
|
.post(format!("{url}/subscribe"))
|
||||||
"https://api.beehiiv.com/v2/publications/{id}/subscriptions"
|
.form(&form)
|
||||||
))
|
|
||||||
.header(AUTHORIZATION, format!("Bearer {}", api_key))
|
|
||||||
.json(&serde_json::json!({
|
|
||||||
"email": email,
|
|
||||||
"utm_source": "modrinth",
|
|
||||||
"utm_medium": "account_creation",
|
|
||||||
"referring_site": site_url,
|
|
||||||
}))
|
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status()?
|
||||||
@@ -1578,7 +1578,7 @@ pub async fn create_account_with_password(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
if new_account.sign_up_newsletter.unwrap_or(false) {
|
if new_account.sign_up_newsletter.unwrap_or(false) {
|
||||||
sign_up_beehiiv(&new_account.email).await?;
|
sign_up_sendy(&new_account.email).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.commit().await?;
|
transaction.commit().await?;
|
||||||
@@ -1678,26 +1678,26 @@ async fn validate_2fa_code(
|
|||||||
.map_err(|_| AuthenticationError::InvalidCredentials)?,
|
.map_err(|_| AuthenticationError::InvalidCredentials)?,
|
||||||
)
|
)
|
||||||
.map_err(|_| AuthenticationError::InvalidCredentials)?;
|
.map_err(|_| AuthenticationError::InvalidCredentials)?;
|
||||||
let token = totp
|
|
||||||
.generate_current()
|
|
||||||
.map_err(|_| AuthenticationError::InvalidCredentials)?;
|
|
||||||
|
|
||||||
const TOTP_NAMESPACE: &str = "used_totp";
|
const TOTP_NAMESPACE: &str = "used_totp";
|
||||||
let mut conn = redis.connect().await?;
|
let mut conn = redis.connect().await?;
|
||||||
|
|
||||||
// Check if TOTP has already been used
|
// Check if TOTP has already been used
|
||||||
if conn
|
if conn
|
||||||
.get(TOTP_NAMESPACE, &format!("{}-{}", token, user_id.0))
|
.get(TOTP_NAMESPACE, &format!("{}-{}", input, user_id.0))
|
||||||
.await?
|
.await?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
return Err(AuthenticationError::InvalidCredentials);
|
return Err(AuthenticationError::InvalidCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
if input == token {
|
if totp
|
||||||
|
.check_current(input.as_str())
|
||||||
|
.map_err(|_| AuthenticationError::InvalidCredentials)?
|
||||||
|
{
|
||||||
conn.set(
|
conn.set(
|
||||||
TOTP_NAMESPACE,
|
TOTP_NAMESPACE,
|
||||||
&format!("{}-{}", token, user_id.0),
|
&format!("{}-{}", input, user_id.0),
|
||||||
"",
|
"",
|
||||||
Some(60),
|
Some(60),
|
||||||
)
|
)
|
||||||
@@ -2453,7 +2453,7 @@ pub async fn subscribe_newsletter(
|
|||||||
.1;
|
.1;
|
||||||
|
|
||||||
if let Some(email) = user.email {
|
if let Some(email) = user.email {
|
||||||
sign_up_beehiiv(&email).await?;
|
sign_up_sendy(&email).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::NoContent().finish())
|
Ok(HttpResponse::NoContent().finish())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user