This commit is contained in:
count-null 2025-03-06 11:54:23 -05:00
parent 35971fd696
commit 1ed14b5549
7 changed files with 118 additions and 162 deletions

View file

@ -90,7 +90,7 @@ class account
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
$token = magic_links::add($email, $user_id); $token = magic_links::add($email, $user_id);
users::updateReplaceEmailTokenById($user_id, $token); users::updateReplaceEmailTokenById($user_id, $token);
header('Location: /account'); header('Location: /account/verify');
exit; exit;
} }
} }
@ -98,30 +98,6 @@ class account
public static function verify($defaults) public static function verify($defaults)
{ {
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$code = $_POST['code'];
$link = magic_links::validateCode($code);
if ($link) {
$user = $link['user_id'] ? users::getById($link['user_id']) : users::getByEmail($link['email']);
if ($user) {
$_SESSION['user_email'] = $link['email'];
$_SESSION['user_id'] = $user['id'];
if (! $user['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit;
} else {
$_SESSION['user_email'] = $link['email'];
header('Location: /account/signup');
exit;
}
} else {
$_SESSION['error'] = "Invalid or expired verification code.";
header('Location: /account/verify');
exit;
}
}
echo $GLOBALS['twig']->render('lib/pages/index.twig', array_merge($defaults, [ echo $GLOBALS['twig']->render('lib/pages/index.twig', array_merge($defaults, [
'child_template' => 'account/verify.twig', 'child_template' => 'account/verify.twig',
'page_title' => $_ENV['APP_NAME'], 'page_title' => $_ENV['APP_NAME'],
@ -303,9 +279,8 @@ class account
} }
$existingUser = users::getByEmail($email); $existingUser = users::getByEmail($email);
if ($existingUser) { if ($existingUser) {
$_SESSION['error'] = 'Email already exists. Please choose a different email or log in.'; $token = magic_links::add($email, $user_id);
$_SESSION['last_post'] = $_POST; header("Location: /account/verify");
header('Location: /account/signup');
exit; exit;
} }
$useShipping = $_POST['use_shipping'] ?? false; $useShipping = $_POST['use_shipping'] ?? false;
@ -368,7 +343,8 @@ class account
); );
$_SESSION['user_id'] = $user_id; $_SESSION['user_id'] = $user_id;
if (! $verified) { if (! $verified) {
header("Location: /magic-link?email=$email&signup=1"); $token = magic_links::add($email, $user_id);
header("Location: /account/verify");
exit; exit;
} }
header('Location: /account'); header('Location: /account');

View file

@ -106,32 +106,22 @@ class admin
$currency = $_POST['currency']; $currency = $_POST['currency'];
$user_identifier = $_POST['user_identifier'] ?? null; $user_identifier = $_POST['user_identifier'] ?? null;
if (! $amount || ! $user_identifier) { if (! $amount || ! $user_identifier) {
$_SESSION['error'] = ! $amount ? "Please enter an amount for the transaction." : "Please enter a user email or id for the transaction."; $_SESSION['error'] = ! $amount ? "Please enter an amount for the transaction." : "Please enter a user email or id for the transaction.";
$_SESSION['last_post'] = $_POST;
} else { } else {
if (strpos($user_identifier, '@') !== false && strpos($user_identifier, '.') !== false) { if (strpos($user_identifier, '@') !== false && strpos($user_identifier, '.') !== false) {
$user = users::getByEmail($user_identifier); $user = users::getByEmail($user_identifier);
} elseif (is_numeric($user_identifier)) { } elseif (is_numeric($user_identifier)) {
$user = users::getById((int) $user_identifier); $user = users::getById((int) $user_identifier);
} else { } else {
$_SESSION['error'] = "Invalid user identifier. Please enter a valid email or user ID."; $_SESSION['error'] = "Invalid user identifier. Please enter a valid email or user ID.";
$_SESSION['last_post'] = $_POST;
} }
if (! $user) { if (! $user) {
$_SESSION['error'] = "User not found. Please enter a valid email or user ID."; $_SESSION['error'] = "User not found. Please enter a valid email or user ID.";
$_SESSION['last_post'] = $_POST;
} else { } else {
if ($_POST['confirm']) { // create the transaction
// create the transaction $txid = transactions::add($user['id'], $amount > 0 ? 'CREDIT' : 'REVOKE', $currency == 'cents' ? $amount : 0, $currency == 'sats' ? $amount : 0);
$txid = transactions::add($user['id'], $amount > 0 ? 'CREDIT' : 'REVOKE', $currency == 'cents' ? $amount : 0, $currency == 'sats' ? $amount : 0); header('Location: /transaction/' . $txid);
header('Location: /transaction/' . $txid); exit;
exit;
} else {
$_SESSION['last_post'] = $_POST;
$_SESSION['last_post']['confirm'] = true;
$_SESSION['last_post']['email'] = $user['email'];
$_SESSION['last_post']['id'] = $user['id'];
}
} }
} }
} }
@ -153,6 +143,7 @@ class admin
], ],
], ],
])); ]));
exit;
} }
public static function emails($defaults) public static function emails($defaults)

View file

@ -9,39 +9,81 @@ class magic_link
{ {
public static function index() public static function index()
{ {
$token = $_GET['token'] ?? null; if ($_SERVER['REQUEST_METHOD'] == 'POST') { // using a 6-digit code
if (! $token) { $code = $_POST['code'] ?? null;
$_SESSION['error'] = "Invalid or expired link."; if (! $code) {
header('Location: /account/login'); $_SESSION['error'] = "Invalid or expired code.";
exit; header('Location: /account/verify');
} else { exit;
$link = magic_links::validateToken(token: $token); } else {
if (! $link) { $link = magic_links::validateCode($code);
if ($link) { // the code is valid
$user = $link['user_id'] ? users::getById($link['user_id']) : users::getByEmail($link['email']);
if ($user) { // this email is registered or associated with a user
$user_replacing = users::getByReplaceEmailToken($link['token']);
if ($user_replacing) { // user is replacing their email
users::updateEmailById($user_replacing['id'], $link['email']);
$_SESSION['user_email'] = $link['email'];
$_SESSION['user_id'] = $user_replacing['id'];
if (! $user_replacing['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit;
} else { // user is logging in
$_SESSION['user_email'] = $link['email'];
$_SESSION['user_id'] = $user['id'];
if (! $user['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit;
}
} else { // new user signup
$_SESSION['user_email'] = $link['email'];
header('Location: /account/signup');
exit;
}
} else { // the code is not valid
$_SESSION['error'] = "Invalid or expired verification code.";
header('Location: /account/verify');
exit;
}
}
} else { // using a link with a token
$token = $_GET['token'] ?? null;
if (! $token) {
$_SESSION['error'] = "Invalid or expired link."; $_SESSION['error'] = "Invalid or expired link.";
header('Location: /account/login'); header('Location: /account/login');
exit; exit;
} } else {
$user = $link['user_id'] ? users::getById($link['user_id']) : users::getByEmail($link['email']); $link = magic_links::validateToken(token: $token);
if ($user) { // user with this email exists, log them in if (! $link) {
$_SESSION['user_email'] = $link['email']; $_SESSION['error'] = "Invalid or expired link.";
$_SESSION['user_id'] = $user['id']; header('Location: /account/login');
if (! $user['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit;
} else { // no users with this email
$user_replacing_email = users::getByReplaceEmailToken($token);
if ($user_replacing_email) { // user is replacing their email
$user_id = $user_replacing_email['id'];
users::updateEmailById($user_id, $link['email']);
$_SESSION['user_email'] = $link['email'];
$_SESSION['user_id'] = $user_id;
if (! $user['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit; exit;
}
$user = $link['user_id'] ? users::getById($link['user_id']) : users::getByEmail($link['email']);
if ($user) { // this email is registered or is associated with a user
$user_replacing = users::getByReplaceEmailToken($token);
if ($user_replacing) { // user is replacing their email
users::updateEmailById($user_replacing['id'], $link['email']);
$_SESSION['user_email'] = $link['email'];
$_SESSION['user_id'] = $user_replacing['id'];
if (! $user_replacing['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit;
} else { // user is logging in
$_SESSION['user_email'] = $link['email'];
$_SESSION['user_id'] = $user['id'];
if (! $user['verified']) {
users::verify($link['email']);
}
header('Location: /account');
exit;
}
} else { // new user signup } else { // new user signup
$_SESSION['user_email'] = $link['email']; $_SESSION['user_email'] = $link['email'];
header('Location: /account/signup'); header('Location: /account/signup');

View file

@ -72,12 +72,11 @@ class users
public static function updateEmailById($user_id, $email) public static function updateEmailById($user_id, $email)
{ {
$query = "UPDATE users SET email = :email WHERE id = :user_id"; $query = "UPDATE users SET email = :email, replace_email_token = NULL WHERE id = :user_id";
$stmt = app::$db->prepare($query); $stmt = app::$db->prepare($query);
$stmt->bindParam(':email', $email); $stmt->bindParam(':email', $email);
$stmt->bindParam(':user_id', $user_id); $stmt->bindParam(':user_id', $user_id);
$stmt->execute(); $stmt->execute();
users::updateReplaceEmailTokenById($user_id, null);
} }
public static function getByReplaceEmailToken($token) public static function getByReplaceEmailToken($token)

View file

@ -11,7 +11,7 @@
</div> </div>
{% include 'lib/alert.twig' %} {% include 'lib/alert.twig' %}
<form action="/account/verify" method="post" class="flex flex-col items-center gap-4"> <form action="/magic-link" method="post" class="flex flex-col items-center gap-4">
{% include 'lib/inputs/text.twig' with { {% include 'lib/inputs/text.twig' with {
type: 'tel', type: 'tel',
name: 'code', name: 'code',

View file

@ -1,67 +1,32 @@
<section class="flex flex-col gap-4"> <section class="flex flex-col gap-4">
{% include 'lib/alert.twig' %} {% include 'lib/alert.twig' %}
<form action="/admin/transactions/add" method="post" class="flex flex-col gap-4"> <form action="/admin/transactions/add" method="post" class="flex flex-col gap-4">
{% if session.last_post.confirm %} {% include 'lib/inputs/number.twig' with {
Confirm id: 'amount',
<input type="hidden" name="confirm" id="confirm" value="{{ session.last_post.confirm }}"> name: 'amount',
{% endif %} label: 'Amount',
{% if session.last_post.amount is defined %} placeholder: 'Enter the amount',
{{ session.last_post.amount }} required: true
{{ session.last_post.currency }} } %}
<input {% include 'lib/inputs/select.twig' with {
type="hidden" name="amount" id="amount" value="{{ session.last_post.amount }}" /> id: 'currency',
{% else %} name: 'currency',
{% include 'lib/inputs/number.twig' with { label: 'Currency',
id: 'amount', options: [
name: 'amount', { 'value': 'sats', 'text': 'Sats' },
label: 'Amount', { 'value': 'cents', 'text': 'Cents' }
placeholder: 'Enter the amount', ],
value: session.last_post.amount, required: true
required: true } %}
} %} {% include 'lib/inputs/text.twig' with {
{% endif %} type: 'text',
{% if session.last_post.currency %} name: 'user_identifier',
<input label: 'User Identifier',
type="hidden" name="currency" id="currency" value="{{ session.last_post.currency }}" /> placeholder: 'Enter email or user index',
{% else %} } %}
{% include 'lib/inputs/select.twig' with { {% include 'lib/buttons/submit.twig' with {
id: 'currency', label: 'Submit',
name: 'currency', } %}
label: 'Currency', </form>
value: session.last_post.currency,
options: [
{ 'value': 'sats', 'text': 'Sats' },
{ 'value': 'cents', 'text': 'Cents' }
],
required: true
} %}
{% endif %}
{% if session.last_post.user_identifier is defined %}
{% if session.last_post.email is defined %}
{{ session.last_post.id }}
{{ session.last_post.email }}
{% endif %}
<input
type="hidden" name="user_identifier" id="user_identifier" value="{{ session.last_post.user_identifier }}" />
{% else %}
{% include 'lib/inputs/text.twig' with {
type: 'text',
name: 'user_identifier',
label: 'User Identifier',
placeholder: 'Enter email or user index',
value: session.last_post.user_identifier
} %}
{% endif %}
{% include 'lib/buttons/submit.twig' with {
label: 'Submit',
} %}
</form>
{% if session.last_post %}
<a href="/admin/transactions/reset">
{% include 'lib/buttons/default.twig' with {
label: 'Cancel'
} %}
</a>
{% endif %}
</section>
</section>

View file

@ -1,27 +1,10 @@
<section class="flex flex-col gap-4"> <section class="flex flex-col gap-4">
{% include 'lib/alert.twig' %} {% include 'lib/alert.twig' %}
<form action="/admin/transactions/add" method="post" class="flex flex-col gap-4"> <a href="/admin/transactions/add">
{% include 'lib/inputs/number.twig' with {
id: 'amount',
name: 'amount',
label: 'Amount',
required: true
} %}
{% include 'lib/inputs/select.twig' with {
id: 'currency',
name: 'currency',
label: 'Currency',
options: [
{ value: 'cents', text: 'Cents' },
{ value: 'sats', text: 'Sats' }
],
required: true
} %}
{% include 'lib/buttons/submit.twig' with { {% include 'lib/buttons/submit.twig' with {
label: 'Submit', label: 'Create a New Transaction',
} %} } %}
</form> </a>
<h3 class="text-2xl font-semibold"> <h3 class="text-2xl font-semibold">
Liabilities Liabilities
</h3> </h3>