This commit is contained in:
count-null 2025-02-25 19:21:31 -05:00
parent 27df1a73b5
commit a0cb5fb6b0
36 changed files with 1886 additions and 187 deletions

View file

@ -3,6 +3,7 @@ namespace app\controllers;
use app\models\addresses;
use app\models\users;
use app\models\emails;
use app\models\user_addresses;
use app\models\magic_links;
@ -138,6 +139,49 @@ class account
}
}
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/page/index.twig', array_merge($defaults, [
'child_template' => 'account/verify.twig',
'page_title' => $_ENV['APP_NAME'],
'breadcrumbs' => [
[
'url' => '/account',
'title' => 'My Account'
],
[
'url' => null,
'title' => 'Verify'
]
]
]));
}
public static function login($defaults)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@ -148,7 +192,7 @@ class account
exit;
} else {
$token = magic_links::add($email, null);
header('Location: /account/login');
header('Location: /account/verify');
exit;
}
}
@ -339,6 +383,7 @@ class account
$verified,
$dark_theme
);
emails::updateUserIdByEmail($email, $user_id);
user_addresses::add(
user_id: $user_id,
address_id: $ship_id

184
src/controllers/admin.php Normal file
View file

@ -0,0 +1,184 @@
<?php
namespace app\controllers;
use app\models\transactions;
use app\models\emails;
use app\models\users;
class admin
{
public static function index($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/index.twig',
'page_title' => 'Dashboard',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
]
],
]));
}
public static function users($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/users.twig',
'page_title' => 'Users',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/users',
'title' => 'Users'
]
],
]));
}
public static function orders($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/orders.twig',
'page_title' => 'Orders',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/orders',
'title' => 'Orders'
]
],
]));
}
public static function returns($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/returns.twig',
'page_title' => 'Returns',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/returns',
'title' => 'Returns'
]
],
]));
}
public static function transactions($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/transactions/index.twig',
'page_title' => 'Transactions',
'recent_sats' => transactions::getRecent(10, 'sats'),
'recent_cents' => transactions::getRecent(10, 'cents'),
'whales_sats' => transactions::getWhales(10, 'sats'),
'whales_cents' => transactions::getWhales(10, 'cents'),
'sats_liability' => transactions::getLiabilities('sats'),
'cents_liability' => transactions::getLiabilities('cents'),
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/transactions',
'title' => 'Transactions'
]
],
]));
}
public static function transactions_add($defaults)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$amount = $_POST['amount'] ?? null;
$currency = $_POST['currency'];
$user_identifier = $_POST['user_identifier'] ?? null;
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['last_post'] = $_POST;
} else {
if (strpos($user_identifier, '@') !== false && strpos($user_identifier, '.') !== false) {
$user = users::getByEmail($user_identifier);
} elseif (is_numeric($user_identifier)) {
$user = users::getById((int)$user_identifier);
} else {
$_SESSION['error'] = "Invalid user identifier. Please enter a valid email or user ID.";
$_SESSION['last_post'] = $_POST;
}
if (!$user) {
$_SESSION['error'] = "User not found. Please enter a valid email or user ID.";
$_SESSION['last_post'] = $_POST;
} else {
if($_POST['confirm']){
// create the transaction
$txid = transactions::add($user['id'], $amount > 0 ? 'CREDIT' : 'REVOKE', $currency == 'cents' ? $amount : 0, $currency == 'sats' ? $amount : 0);
header('Location: /transaction/' . $txid);
exit;
} else {
$_SESSION['last_post'] = $_POST;
$_SESSION['last_post']['confirm'] = true;
$_SESSION['last_post']['email'] = $user['email'];
$_SESSION['last_post']['id'] = $user['id'];
}
}
}
}
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/transactions/add.twig',
'page_title' => 'Add a Transaction',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/transactions',
'title' => 'Transactions'
],
[
'url' => '/admin/transactions/add',
'title' => 'Add'
]
],
]));
}
public static function emails($defaults)
{
$recent_emails = emails::getRecent(20);
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/emails.twig',
'page_title' => 'Emails',
'recent_emails' => $recent_emails,
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/emails',
'title' => 'Emails'
]
],
]));
}
public static function transactions_reset($defaults)
{
$_SESSION['last_post'] = null;
header('Location: /admin/transactions/add');
exit;
}
}

View file

@ -2,13 +2,18 @@
namespace app\controllers;
use app\app;
use app\models\invoices;
$invoice = 'lnbc234340n1pnm5uh2pp5pwekmjzj3hgadahfeprtc6f2ppmhnqjzh556q4fvve8z953v3t0sdqqcqzysxqrrsssp5uq25yjnmvdpnglmv42nf64wk0pugrynq549f3wgghgtkfapwdfhq9qxpqysgqyjq2ewqkm6s2dlhuruuc4k9md22wraz829tlhfeuxrsnwmephfkjz8e9g7j6373989mfccajy3cxexac8xu6yen4qfs4947fkrg9ynsq7x72ze';
use Jorijn\Bitcoin\Bolt11\Encoder\PaymentRequestDecoder;
use Jorijn\Bitcoin\Bolt11\Model\Tag;
use Jorijn\Bitcoin\Bolt11\Normalizer\PaymentRequestDenormalizer;
class lnurlp
{
public static function index()
public static function index($defaults)
{
header(header: 'Content-Type: application/json');
$host = $_SERVER['HTTP_HOST'];
$user = $_GET["username"] ?? false;
$username = $_GET["username"] ?? false;
$paymentRequest = $_GET["pay"] ?? false;
$verify = $_GET["verify"] ?? false;
@ -25,25 +30,29 @@ class lnurlp
'reason' => 'invalid value for `pay` param (set `pay=1` or exclude `pay` from the url)',
]);
}
// for when the user is missing
if ($user == false && $verify == false) {
// for when the user is not specified
if ($username == false && $verify == false) {
returnJson([
'status' => 'ERROR',
'reason' => 'no user specified (set `username=<name>` in the url)',
]);
}
list($proxy_user, $proxy_host) = explode("@", $_ENV['LN_ADDRESS']);
// for when the user is not registered
$user = users::getByNpub($username);
if (!$user){
returnJson([
'status' => 'ERROR',
'reason' => "@$username is not registered"
]);
}
// for when the client makes it's first call (querying the lightning address)
$metadata = "[[\"text/plain\",\"Funding @$user on $host\"],[\"text/identifier\",\"$user@$host\"]]";
list($proxy_user, $proxy_host) = explode("@", $_ENV['LN_ADDRESS']);
$metadata = "[[\"text/plain\",\"Funding @$username on $host\"],[\"text/identifier\",\"$username@$host\"]]";
if ($paymentRequest == false && $verify == false) {
$res = json_decode(file_get_contents("https://$proxy_host/.well-known/lnurlp/$proxy_user"), true);
returnJson(
[
'callback' => "https://$host/lnurlp?pay=1&username=$user",
'callback' => "https://$host/lnurlp?pay=1&username=$username",
'maxSendable' => $res['maxSendable'],
'minSendable' => $res['minSendable'],
'metadata' => $metadata,
@ -54,19 +63,34 @@ class lnurlp
);
}
// for when the client makes it's second call (callback)
// for when the client makes it's second call (callback) to get an invoice
if ($paymentRequest == "1") {
$proxy_url = "https://$proxy_host/lnurlp?pay=1&username=$proxy_user";
$res = json_decode(file_get_contents("https://$proxy_host/.well-known/lnurlp/$proxy_user"), true);
$proxy_url = $res['callback'];
if (isset($_GET["amount"])) {
$proxy_url .= "&amount=" . urlencode($_GET["amount"]);
}
$res = json_decode(file_get_contents($proxy_url), true);
if ($res['status'] === 'OK'){
// subscribe to this invoice by adding to our db
$invoice = $res['pr'];
$decoder = new PaymentRequestDecoder();
$denormalizer = new PaymentRequestDenormalizer();
$paymentRequest = $denormalizer->denormalize($decoder->decode($invoice));
invoices::add(
$user['id'],
null,
$invoice,
$res['verify'],
$paymentRequest->getMilliSatoshis(),
$paymentRequest->getExpiryDateTime()['date']
);
$boom = explode("=", $res['verify']);
$proxy_verify = end($boom);
returnJson([
'status' => 'OK',
'pr' => $res['pr'],
'pr' => $invoice,
'routes' => $res['routes'],
'verify' => "https://$host/lnurlp?verify=$proxy_verify"
]);
@ -77,8 +101,10 @@ class lnurlp
// for when they want to verify the payment succeeded
if ($verify) {
$res = json_decode(file_get_contents("https://$proxy_host/lnurlp?verify=$verify"), true);
returnJson($res);
$res = json_decode(file_get_contents("https://$proxy_host/.well-known/lnurlp/$proxy_user"), true);
$proxy_url = $res['verify'];
$prox_res = json_decode(file_get_contents($proxy_url), true);
returnJson($prox_res);
}
// for when none of the above conditions are met

View file

@ -0,0 +1,172 @@
<?php
namespace app\controllers;
use app\models\transactions;
use app\models\users;
use app\controllers\lost;
class transaction
{
public static function view($defaults, $txid)
{
$tx = transactions::getById($txid);
if (!$tx) {
lost::index($defaults);
}
$user = users::getById($tx['user_id']);
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'transaction.twig',
'page_title' => 'Transaction Reciept #' . $txid,
'tx' => $tx,
'user' => $user,
'breadcrumbs' => [
[
'url' => "/transaction/" . $txid,
'title' => 'Transaction Reciept'
]
],
]));
}
public static function users($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/users.twig',
'page_title' => $_ENV['APP_NAME'] . ' Users',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/users',
'title' => 'Users'
]
],
]));
}
public static function orders($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/orders.twig',
'page_title' => $_ENV['APP_NAME'] . ' Orders',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/orders',
'title' => 'Orders'
]
],
]));
}
public static function returns($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/returns.twig',
'page_title' => $_ENV['APP_NAME'] . ' Returns',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/returns',
'title' => 'Returns'
]
],
]));
}
public static function transactions($defaults)
{
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/transactions/index.twig',
'page_title' => $_ENV['APP_NAME'] . ' Transactions',
'recent_sats' => transactions::getRecent(10, 'sats'),
'recent_cents' => transactions::getRecent(10, 'cents'),
'whales_sats' => transactions::getWhales(10, 'sats'),
'whales_cents' => transactions::getWhales(10, 'cents'),
'sats_liability' => transactions::getLiabilities('sats'),
'cents_liability' => transactions::getLiabilities('cents'),
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/transactions',
'title' => 'Transactions'
]
],
]));
}
public static function transactions_add($defaults)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$amount = $_POST['amount'] ?? null;
$currency = $_POST['currency'];
$user_identifier = $_POST['user_identifier'] ?? null;
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['last_post'] = $_POST;
} else {
if (strpos($user_identifier, '@') !== false && strpos($user_identifier, '.') !== false) {
$user = users::getByEmail($user_identifier);
} elseif (is_numeric($user_identifier)) {
$user = users::getById((int)$user_identifier);
} else {
$_SESSION['error'] = "Invalid user identifier. Please enter a valid email or user ID.";
$_SESSION['last_post'] = $_POST;
}
if (!$user) {
$_SESSION['error'] = "User not found. Please enter a valid email or user ID.";
$_SESSION['last_post'] = $_POST;
} else {
if($_POST['confirm']){
// create the transaction
$txid = transactions::add($user['id'], 'CREDIT', $currency == 'cents' ? $amount : 0, $currency == 'sats' ? $amount : 0);
header('Location: /transaction/' . $txid);
exit;
} else {
$_SESSION['last_post'] = $_POST;
$_SESSION['last_post']['confirm'] = true;
$_SESSION['last_post']['email'] = $user['email'];
$_SESSION['last_post']['id'] = $user['id'];
}
}
}
}
echo $GLOBALS['twig']->render('lib/page/index.twig', array_merge($defaults, [
'child_template' => 'admin/transactions/add.twig',
'page_title' => 'Add a Transaction',
'breadcrumbs' => [
[
'url' => '/admin',
'title' => 'Admin'
],
[
'url' => '/admin/transactions',
'title' => 'Transactions'
],
[
'url' => '/admin/transactions/add',
'title' => 'Add'
]
],
]));
}
public static function transactions_reset($defaults)
{
$_SESSION['last_post'] = null;
header('Location: /admin/transactions/add');
exit;
}
}