feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
99
f_modules/m_frontend/m_acct/account.php
Normal file
99
f_modules/m_frontend/m_acct/account.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_recaptcha/autoload.php';
|
||||
include_once 'f_core/f_classes/class_thumb/ThumbLib.inc.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.account');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('paid_memberships,backend_email,backend_username,signup_domain_restriction,list_email_domains,signup_min_password,signup_max_password,email_change_captcha,keep_entries_open,user_image_max_size,user_image_allowed_extensions,user_image_width,user_image_height,activity_logging,file_favorites,file_rating,file_comments,channel_comments,file_respnses,approve_friends,file_counts,numeric_delimiter,channel_views,recaptcha_site_key,recaptcha_secret_key,affiliate_module,affiliate_tracking_id,affiliate_view_id,affiliate_maps_api_key,affiliate_token_script,affiliate_payout_figure,affiliate_payout_currency,affiliate_payout_units,affiliate_payout_share,affiliate_requirements_type,affiliate_requirements_min');
|
||||
$logged_in = VLogin::checkFrontend(VHref::getKey('account'));
|
||||
$membership_check = ($cfg["paid_memberships"] == 1 and $_SESSION["USER_ID"] > 0) ? VLogin::checkSubscription() : null;
|
||||
$notice_message = ($_POST and $_GET["do"] == '') ? VUseraccount::doChanges() : null;
|
||||
$user_key = $class_filter->clr_str($_SESSION["USER_KEY"]);
|
||||
$files = new VFiles;
|
||||
|
||||
$smarty->assign('page_display', 'tpl_account');
|
||||
|
||||
switch ($_GET["s"]) {
|
||||
case "account-menu-entry1":
|
||||
case "account-menu-entry13":
|
||||
$tpl_page = 'tpl_overview.tpl';
|
||||
switch ($_GET["do"]) {
|
||||
case "loading":$smarty->display('tpl_frontend/tpl_acct/tpl_overview_image.tpl');
|
||||
break;
|
||||
case "cancel":VUseraccount::cancelProfileImage();
|
||||
break;
|
||||
case "upload":VUseraccount::changeProfileImage($user_key);
|
||||
break;
|
||||
case "save":VUseraccount::saveProfileImage($user_key);
|
||||
break;
|
||||
case "make-affiliate":echo VAffiliate::affiliateRequest();
|
||||
break;
|
||||
case "make-affiliate-email":$html = $_POST ? VAffiliate::affiliateRequestEmail() : null;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "account-menu-entry2":
|
||||
$tpl_page = 'tpl_profile_setup.tpl';
|
||||
break;
|
||||
case "account-menu-entry3":
|
||||
$tpl_page = '';
|
||||
break;
|
||||
case "account-menu-entry4":
|
||||
$tpl_page = 'tpl_email_opts.tpl';
|
||||
if ($_POST) {
|
||||
switch ($_GET["do"]) {
|
||||
case "emchange":VUseraccount::changeEmail();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "account-menu-entry5":$tpl_page = 'tpl_activity.tpl';
|
||||
break;
|
||||
case "account-menu-entry6":
|
||||
$tpl_page = 'tpl_manage_acct.tpl';
|
||||
if ($_POST) {
|
||||
switch ($_GET["do"]) {
|
||||
case "cpass":VUseraccount::changePassword();
|
||||
break;
|
||||
case "purge":VUseraccount::purgeAccount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!isset($_GET["s"]) and !isset($_GET["do"])) {
|
||||
VAffiliate::allowRequest();
|
||||
$smarty->assign('c_section', VHref::getKey("account"));
|
||||
}
|
||||
|
||||
$section_menus = (intval($_SESSION["USER_ID"]) > 0) ? $smarty->assign('keep_entries_open', $_SESSION[$_SESSION["USER_KEY"] . '_list']) : null;
|
||||
$display_section = ($_GET["s"] != '' and !isset($_GET["do"])) ? $smarty->display('tpl_frontend/tpl_acct/' . $tpl_page) : null;
|
||||
$display_page = (!isset($_GET["s"]) and !isset($_GET["do"])) ? $class_smarty->displayPage('frontend', 'tpl_account', $error_message, $notice_message) : null;
|
||||
8
f_modules/m_frontend/m_acct/affiliate.php
Normal file
8
f_modules/m_frontend/m_acct/affiliate.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
define("_ISVALID", true);
|
||||
include_once "f_core/config.core.php";
|
||||
// Affiliate system - basic implementation
|
||||
echo "<h1>Affiliate Program</h1>";
|
||||
echo "<p>Affiliate program functionality coming soon...</p>";
|
||||
echo "<a href=\"/account\">← Back to Account</a>";
|
||||
?>
|
||||
83
f_modules/m_frontend/m_acct/channel.php
Normal file
83
f_modules/m_frontend/m_acct/channel.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.account');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.view');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.files.menu');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.browse');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.channel');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.manage.channel');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.files');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.userpage');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('image_player,video_player,audio_player,document_player,paid_memberships,file_favorites,file_playlists,file_watchlist,file_responses,user_subscriptions,public_channels,channel_bulletins,event_map,user_friends,user_blocking,approve_friends,activity_logging,channel_comments,ucc_limit,comment_min_length,comment_max_length,channel_backgrounds,channel_bg_allowed_extensions,channel_bg_max_size,file_favorites,file_rating,file_comments,file_views,channel_views,guest_view_channel,file_promo,comment_emoji');
|
||||
$guest_chk = $_SESSION["USER_ID"] == '' ? VHref::guestPermissions('guest_view_channel', VHref::getKey("channels")) : null;
|
||||
$membership_check = ($cfg["paid_memberships"] == 1 and $_SESSION["USER_ID"] > 0) ? VLogin::checkSubscription() : null;
|
||||
|
||||
$channel = new VChannel;
|
||||
if (isset($_GET["a"])) {
|
||||
switch ($_GET["a"]) {
|
||||
case "postbulletin":
|
||||
echo $ht = VChannel::postBulletin();
|
||||
break;
|
||||
|
||||
case "hideuseractivity":
|
||||
echo $ht = VChannel::hideActivity();
|
||||
break;
|
||||
|
||||
case "cb-addfr": //add friends
|
||||
case "cb-remfr": //remove friend
|
||||
case "cb-block": //block friend
|
||||
case "cb-unblock": //unblock friend
|
||||
$notifier = new VNotify;
|
||||
echo $do = $_POST ? VChannel::userActions($_GET["a"]) : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["do"])) {
|
||||
$vview = new VView;
|
||||
|
||||
switch ($_GET["do"]) {
|
||||
case "sub-option":
|
||||
echo $do_load = VView::subHtml('', 'channel');
|
||||
break;
|
||||
case "unsub-option":
|
||||
echo $do_load = VView::subHtml(1, 'channel');
|
||||
break;
|
||||
case "sub-continue":
|
||||
echo $do_load = VView::subContinue('channel');
|
||||
break;
|
||||
case "user-unsubscribe":
|
||||
echo $do_load = VSubscriber::unsub_request((int) $_POST["uf_vuid"]);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_GET["s"]) and !isset($_GET["do"]) and !isset($_GET["a"]) and $error_message == '' and isset($_SESSION["q"])) {$_SESSION["q"] = null;}
|
||||
|
||||
$update_views = (!isset($_GET["s"]) and !isset($_GET["do"]) and !isset($_GET["a"]) and $error_message == '') ? VChannel::updateViews() : null;
|
||||
$display_page = (!isset($_GET["a"]) and !isset($_GET["do"])) ? $class_smarty->displayPage('frontend', 'tpl_channel', $error_message, $notice_message) : null;
|
||||
105
f_modules/m_frontend/m_acct/channels.php
Normal file
105
f_modules/m_frontend/m_acct/channels.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.home');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.files.menu');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.account');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.userpage');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.view');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('paid_memberships,backend_email,backend_username,channel_comments,file_counts,numeric_delimiter,channel_views,user_subscriptions,user_friends,user_blocking,internal_messaging,approve_friends,channel_promo');
|
||||
$guest_chk = $_SESSION["USER_ID"] == '' ? VHref::guestPermissions('guest_browse_channel', VHref::getKey("channels")) : null;
|
||||
$membership_check = ($cfg["paid_memberships"] == 1 and $_SESSION["USER_ID"] > 0) ? VLogin::checkSubscription() : null;
|
||||
$section = VHref::getKey('channels');
|
||||
$channels = new VChannels;
|
||||
|
||||
if (isset($_GET["p"]) and (int) $_GET["p"] >= 0) {
|
||||
//viewmode changer/loader
|
||||
$view_mode = (int) $_GET["m"];
|
||||
|
||||
switch ($view_mode) {
|
||||
case "1":
|
||||
case "2":
|
||||
echo $html = VChannels::viewMode_loader($view_mode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["a"])) {
|
||||
switch ($_GET["a"]) {
|
||||
case "sub":
|
||||
$act = VChannels::chSubscribe();
|
||||
break;
|
||||
case "unsub":
|
||||
$act = VChannels::chSubscribe(1);
|
||||
break;
|
||||
case "cb-addfr":
|
||||
case "cb-remfr":
|
||||
case "cb-block":
|
||||
case "cb-unblock":
|
||||
$act = VChannels::contactActions($_GET["a"]);
|
||||
break;
|
||||
case "cb-msg":
|
||||
$act = VChannels::sessionMessageName();
|
||||
break;
|
||||
case "vm":
|
||||
echo $ct = VChannels::viewMode();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (isset($_GET["do"])) {
|
||||
if ($_GET["do"] == 'sub-option' or $_GET["do"] == 'unsub-option' or $_GET["do"] == 'sub-continue' or $_GET["do"] == 'user-sub' or $_GET["do"] == 'user-unsub') {
|
||||
$vview = new VView;
|
||||
}
|
||||
|
||||
switch ($_GET["do"]) {
|
||||
case "subscribe":break;
|
||||
case "user-unsubscribe":
|
||||
echo $do_load = VSubscriber::unsub_request((int) $_POST["uf_vuid"]);
|
||||
break;
|
||||
case "sub-option":
|
||||
echo $do_load = VView::subHtml(0, 'home');
|
||||
break;
|
||||
case "unsub-option":
|
||||
echo $do_load = VView::subHtml(1, 'home');
|
||||
break;
|
||||
case "sub-continue":
|
||||
echo $do_load = VView::subContinue('channels');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_GET["sort"]) and !isset($_GET["s"]) and !isset($_GET["a"]) and !isset($_GET["p"]) and !isset($_GET["do"])) {
|
||||
$smarty->assign('c_section', VHref::getKey("channels"));
|
||||
$_SESSION["q"] = null;
|
||||
}
|
||||
|
||||
echo $display_page = (!isset($_GET["sort"]) and !isset($_GET["s"]) and !isset($_GET["a"]) and !isset($_GET["p"]) and !isset($_GET["do"])) ? $class_smarty->displayPage('frontend', 'tpl_channels', $error_message, $notice_message) : null;
|
||||
23
f_modules/m_frontend/m_acct/live_viewers.php
Normal file
23
f_modules/m_frontend/m_acct/live_viewers.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$act = VSubscriber::get_live_viewers();
|
||||
63
f_modules/m_frontend/m_acct/manage_channel.php
Normal file
63
f_modules/m_frontend/m_acct/manage_channel.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.account');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.manage.channel');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('paid_memberships,backend_email,backend_username,channel_backgrounds,channel_comments,file_counts,numeric_delimiter,channel_views,channel_bg_max_size,channel_bg_allowed_extensions,user_subscriptions,user_friends,user_blocking,internal_messaging,approve_friends');
|
||||
$logged_in = VLogin::checkFrontend(VHref::getKey('manage_channel'));
|
||||
$membership_check = ($cfg["paid_memberships"] == 1 and $_SESSION["USER_ID"] > 0) ? VLogin::checkSubscription() : null;
|
||||
|
||||
$channel = new VChannel;
|
||||
|
||||
switch ($_GET["s"]) {
|
||||
case "channel-menu-entry1": //general setup
|
||||
echo ($_POST ? VChannel::postChanges('ch_setup') : VChannel::manage_general());
|
||||
break;
|
||||
case "channel-menu-entry2": //channel modules
|
||||
echo ($_POST ? VChannel::postChanges('ch_modules') : VChannel::manage_modules());
|
||||
break;
|
||||
case "channel-menu-entry3": //channel art
|
||||
if (isset($_GET["do"])) {
|
||||
switch ($_GET["do"]) {
|
||||
case "edit-crop":$method = 'edit_crop';
|
||||
break;
|
||||
case "edit-gcrop":$method = 'edit_crop';
|
||||
break;
|
||||
case "delete-crop":$method = 'html_delete_crop';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$method = 'manage_art';
|
||||
}
|
||||
echo ($_POST ? VChannel::postChanges('ch_art') : VChannel::$method());
|
||||
break;
|
||||
case "channel-menu-entry4":
|
||||
break;
|
||||
}
|
||||
if (!isset($_GET["s"]) and !isset($_GET["do"])) {
|
||||
$smarty->assign('c_section', VHref::getKey("manage_channel"));
|
||||
}
|
||||
|
||||
echo $display_page = (!isset($_GET["s"])) ? $class_smarty->displayPage('frontend', 'tpl_manage_channel', $error_message, $notice_message) : null;
|
||||
74
f_modules/m_frontend/m_acct/subscribers.php
Normal file
74
f_modules/m_frontend/m_acct/subscribers.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
define('_ISADMIN', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('backend', 'language.dashboard');
|
||||
include_once $class_language->setLanguageFile('backend', 'language.settings.entries');
|
||||
include_once $class_language->setLanguageFile('backend', 'language.subscriber');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.account');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
$ipn_check = false;
|
||||
$cfg[] = $class_database->getConfigurations('paypal_test,paypal_test_email,affiliate_module,affiliate_tracking_id,affiliate_view_id,affiliate_maps_api_key,affiliate_token_script,affiliate_payout_figure,affiliate_payout_units,affiliate_payout_currency,affiliate_payout_share,sub_shared_revenue,subscription_payout_currency,channel_views,sub_threshold,partner_requirements_min,partner_requirements_type');
|
||||
$logged_in = !$ipn_check ? VLogin::checkFrontend(VHref::getKey("subscribers")) : null;
|
||||
$analytics = false;
|
||||
|
||||
if (isset($_GET["do"])) {
|
||||
switch ($_GET["do"]) {
|
||||
case "save-subscriber":
|
||||
echo $ht = VAffiliate::affiliateProfile(1);
|
||||
break;
|
||||
case "showstats":
|
||||
echo $ht = VSubscriber::userStats();
|
||||
break;
|
||||
case "make-partner":
|
||||
case "clear-partner":
|
||||
echo $ht = VAffiliate::affiliateRequest();
|
||||
break;
|
||||
case "make-partner-email":
|
||||
case "clear-partner-email":
|
||||
$ht = $_POST ? VAffiliate::affiliateRequestEmail() : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_GET["s"]) and !isset($_GET["do"])) {
|
||||
VAffiliate::allowRequest('partner');
|
||||
$smarty->assign('c_section', VHref::getKey("subscribers"));
|
||||
}
|
||||
|
||||
if (!isset($_GET["do"])) {
|
||||
$smarty->assign('file_type', 'sub');
|
||||
|
||||
if (isset($_GET["rg"]) and isset($_SESSION["USER_PARTNER"]) and (int) $_SESSION["USER_PARTNER"] == 1) {
|
||||
$smarty->assign('html_payouts', VSubscriber::html_payouts(1));
|
||||
} else if (isset($_GET["rp"]) and isset($_SESSION["USER_PARTNER"]) and (int) $_SESSION["USER_PARTNER"] == 1) {
|
||||
$smarty->assign('html_payouts', VSubscriber::html_payouts());
|
||||
} else if ((isset($_GET["rg"]) and (!isset($_SESSION["USER_PARTNER"]) or (int) $_SESSION["USER_PARTNER"] == 0)) or (isset($_GET["rp"]) and (!isset($_SESSION["USER_PARTNER"]) or (int) $_SESSION["USER_PARTNER"] == 0))) {
|
||||
header("Location: " . $cfg["main_url"] . '/' . VHref::getKey("subscribers"));
|
||||
exit;
|
||||
}
|
||||
|
||||
$class_smarty->displayPage('frontend', 'tpl_subscribers', $error_message, $notice_message);
|
||||
}
|
||||
26
f_modules/m_frontend/m_acct/sync_df.php
Normal file
26
f_modules/m_frontend/m_acct/sync_df.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$do_sync = VSubscriber::sync_df();
|
||||
26
f_modules/m_frontend/m_acct/sync_subs.php
Normal file
26
f_modules/m_frontend/m_acct/sync_subs.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$do_sync = VSubscriber::sync_subs();
|
||||
26
f_modules/m_frontend/m_acct/sync_vods.php
Normal file
26
f_modules/m_frontend/m_acct/sync_vods.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$do_sync = VSubscriber::sync_vods();
|
||||
148
f_modules/m_frontend/m_acct/token_donate.php
Normal file
148
f_modules/m_frontend/m_acct/token_donate.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
if (isset($_SERVER["HTTP_ORIGIN"]) === true) {
|
||||
$origin = $_SERVER["HTTP_ORIGIN"];
|
||||
|
||||
$allowed_origins = array(
|
||||
"http://192.168.100.77",
|
||||
"http://192.168.100.77:3000",
|
||||
);
|
||||
|
||||
if (in_array($origin, $allowed_origins, true) === true) {
|
||||
header('Access-Control-Allow-Origin: ' . $origin);
|
||||
header('Access-Control-Allow-Methods: GET,POST');
|
||||
header('Access-Control-Allow-Headers: VS-Custom-Header');
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") {
|
||||
exit; // OPTIONS request wants only the policy, we can stop here
|
||||
}
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once $class_language->setLanguageFile('backend', 'language.members.entries');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$cfg = $class_database->getConfigurations('paypal_log_file,paypal_logging,paypal_test,paypal_email,paypal_test_email,backend_notification_payment,backend_email,backend_username');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$out = array("valid" => 0);
|
||||
|
||||
$cc = $class_filter->clr_str($_POST["a"]);
|
||||
$ff = $class_filter->clr_str($_POST["b"]);
|
||||
$estr = $class_filter->clr_str($_POST["c"]);
|
||||
$amount = $class_filter->clr_str($_POST["d"]);
|
||||
|
||||
$rs = $db->execute(sprintf("SELECT `db_id`, `channel_owner`, `channel_id`, `usr_id`, `usr_key`, `chat_user` FROM `db_livechat` WHERE `chat_id`='%s' AND `stream_id`='%s' LIMIT 1;", $cc, $ff));
|
||||
|
||||
if ($rs->fields["db_id"]) {
|
||||
$ch_owner = $rs->fields["channel_owner"];
|
||||
$ch_user = $rs->fields["chat_user"];
|
||||
$ch_id = $rs->fields["channel_id"];
|
||||
$usr_id = $rs->fields["usr_id"];
|
||||
$usr_key = $rs->fields["usr_key"];
|
||||
|
||||
$p = $db->execute(sprintf("SELECT `usr_key`, `usr_photo`, `usr_profileinc`, `usr_tokencount` FROM `db_accountuser` WHERE `usr_id`='%s' LIMIT 1;", $usr_id));
|
||||
if ($p->fields['usr_key']) {
|
||||
$tokens = $p->fields['usr_tokencount'];
|
||||
$pimg = VUseraccount::getProfileImage_inc($p->fields['usr_key'], $p->fields['usr_photo'], $p->fields['usr_profileinc']);
|
||||
} else {
|
||||
echo json_encode($out); return;
|
||||
}
|
||||
|
||||
$cstr = md5($ff . $cc . $ch_user . $amount . $cfg["live_chat_salt"]);
|
||||
|
||||
if ($estr == $cstr and $amount <= $tokens) {
|
||||
$db->execute(sprintf("UPDATE `db_accountuser` SET `usr_tokencount`=`usr_tokencount`-%s WHERE `usr_id`='%s' LIMIT 1;", $amount, $usr_id));
|
||||
|
||||
if ($db->Affected_Rows() > 0) {
|
||||
$ins = array(
|
||||
"tk_from" => $usr_id,
|
||||
"tk_to" => $ch_id,
|
||||
"tk_from_user" => $ch_user,
|
||||
"tk_to_user" => $ch_owner,
|
||||
"tk_amount" => $amount,
|
||||
"tk_date" => date("Y-m-d H:i:s"),
|
||||
);
|
||||
$class_database->doInsert('db_tokendonations', $ins);
|
||||
|
||||
if ($db->Affected_Rows() > 0) {
|
||||
/* mail notifications */
|
||||
$notifier = new VNotify;
|
||||
$website_logo = $smarty->fetch($cfg["templates_dir"] . '/tpl_frontend/tpl_header/tpl_headerlogo.tpl');
|
||||
$user_data = VUserinfo::getUserInfo($ch_id);
|
||||
/* user notification */
|
||||
$_replace = array(
|
||||
'##TITLE##' => $language["payment.notification.donate.subj"],
|
||||
'##LOGO##' => $website_logo,
|
||||
'##H2##' => $language["recovery.forgot.password.h2"] . $user_data["uname"] . ',',
|
||||
'##NR##' => '<b>' . $amount . '</b>',
|
||||
'##USER##' => '<a href="' . VHref::channelURL(["username" => $ch_user]) . '" target="_blank">' . $ch_user . '</a>',
|
||||
'##YEAR##' => date('Y'),
|
||||
);
|
||||
$notifier->dst_mail = VUserinfo::getUserEmail($ch_id);
|
||||
$notifier->dst_name = $user_data["uname"];
|
||||
$notifier->Mail('frontend', 'token_donation_fe', $_replace);
|
||||
|
||||
$_output[] = $user_data["uname"] . ' -> token_donation_fe -> ' . $notifier->dst_mail . ' -> ' . date("Y-m-d H:i:s");
|
||||
/* admin notification */
|
||||
if ($cfg["backend_notification_payment"] == 1) {
|
||||
include 'f_core/config.backend.php';
|
||||
$main_url = $cfg["main_url"] . '/' . $backend_access_url;
|
||||
|
||||
$notifier->msg_subj = $language["payment.notification.donate.subj"];
|
||||
$notifier->dst_mail = $cfg["backend_email"];
|
||||
$notifier->dst_name = $cfg["backend_username"];
|
||||
$user_data2 = VUserinfo::getUserInfo($ch_id);
|
||||
|
||||
$_replace = array(
|
||||
'##TITLE##' => $notifier->msg_subj,
|
||||
'##LOGO##' => $website_logo,
|
||||
'##SUBJ##' => str_replace(array('##USER1##', '##USER2##', '##NR##'), array($ch_user, $user_data2["uname"], $amount), $language["payment.notification.donate.subj.be"]),
|
||||
'##H2##' => $language["recovery.forgot.password.h2"] . $cfg["backend_username"] . ',',
|
||||
'##USER1##' => '<a href="' . $main_url . '/' . VHref::getKey('be_members') . '?u=' . $usr_key . '" target="_blank">' . $ch_user . '</a>',
|
||||
'##USER2##' => '<a href="' . $main_url . '/' . VHref::getKey('be_members') . '?u=' . $user_data2["key"] . '" target="_blank">' . $user_data2["uname"] . '</a>',
|
||||
'##NR##' => '<b>' . $amount . '</b>',
|
||||
'##YEAR##' => date('Y'),
|
||||
);
|
||||
$notifier->Mail('backend', 'token_donation_be', $_replace);
|
||||
|
||||
$_output[] = $cfg["backend_username"] . ' -> token_donation_be -> ' . $notifier->dst_mail . ' -> ' . date("Y-m-d H:i:s");
|
||||
}
|
||||
|
||||
$log_mail = '.mailer.log';
|
||||
VServer::logToFile($log_mail, implode("\n", $_output));
|
||||
|
||||
$out["pimg"] = $pimg; $out["valid"] = 1;
|
||||
echo json_encode($out);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
131
f_modules/m_frontend/m_acct/token_list.php
Normal file
131
f_modules/m_frontend/m_acct/token_list.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
if (isset($_SERVER["HTTP_ORIGIN"]) === true) {
|
||||
$origin = $_SERVER["HTTP_ORIGIN"];
|
||||
|
||||
$allowed_origins = array(
|
||||
"http://192.168.100.77",
|
||||
"http://192.168.100.77:3000",
|
||||
);
|
||||
|
||||
if (in_array($origin, $allowed_origins, true) === true) {
|
||||
header('Access-Control-Allow-Origin: ' . $origin);
|
||||
header('Access-Control-Allow-Methods: GET,POST');
|
||||
header('Access-Control-Allow-Headers: VS-Custom-Header');
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") {
|
||||
exit; // OPTIONS request wants only the policy, we can stop here
|
||||
}
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once $class_language->setLanguageFile('backend', 'language.members.entries');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$rs = array();
|
||||
$us = array();
|
||||
$ls = array();
|
||||
$cn = explode(',', $language["supported_currency_names"]);
|
||||
$cu = explode(',', $language["supported_currency_codes"]);
|
||||
|
||||
$cid = $class_filter->clr_str($_POST["a"]);
|
||||
$file_key = $class_filter->clr_str($_POST["b"]);
|
||||
$get_token_nr = isset($_POST["c"]);
|
||||
|
||||
$ch = $db->execute(sprintf("SELECT A.`db_id`, A.`usr_id`, A.`usr_key`, B.`usr_tokencount` FROM `db_livechat` A, `db_accountuser` B WHERE A.`chat_id`='%s' AND A.`stream_id`='%s' AND A.`usr_id`=B.`usr_id` LIMIT 1;", $cid, $file_key));
|
||||
|
||||
if (!$ch->fields["db_id"] or ($ch->fields["db_id"] and $ch->fields["usr_id"] == 0)) {
|
||||
$ls["l"] = "auth";
|
||||
|
||||
echo json_encode($ls);
|
||||
return;
|
||||
}
|
||||
$usr_id = $ch->fields["usr_id"];
|
||||
$us[0] = (int) $ch->fields["usr_tokencount"];
|
||||
|
||||
if ($get_token_nr) {
|
||||
echo json_encode($us);
|
||||
return;
|
||||
}
|
||||
|
||||
$pp = $class_database->getConfigurations('paypal_log_file,paypal_logging,paypal_test,paypal_email,paypal_test_email');
|
||||
$paypal_url = $pp["paypal_test"] == 0 ? 'https://www.paypal.com/cgi-bin/webscr' : 'https://www.sandbox.paypal.com/cgi-bin/webscr';
|
||||
$paypal_mail = $pp["paypal_test"] == 0 ? $pp["paypal_email"] : $pp["paypal_test_email"];
|
||||
$paypal_return = rawurlencode($cfg["main_url"] . '/' . VHref::getKey('watch') . '?l=' . $file_key . '&fst=1');
|
||||
$paypal_cancel = rawurlencode($cfg["main_url"] . '/' . VHref::getKey('watch') . '?l=' . $file_key);
|
||||
$paypal_ipn = rawurlencode($cfg["main_url"] . '/' . VHref::getKey('tokenpayment') . '?do=ipn');
|
||||
|
||||
$paypal_param = array(
|
||||
"cmd" => "_xclick",
|
||||
"rm" => 2,
|
||||
"business" => $paypal_mail,
|
||||
"return" => $paypal_return,
|
||||
"cancel_return" => $paypal_cancel,
|
||||
"notify_url" => $paypal_ipn,
|
||||
"item_name" => "##ITEM_NAME##",
|
||||
"item_number" => "##PP_ITEM##",
|
||||
"currency_code" => "##PP_CURRENCY##",
|
||||
"amount" => "##PP_AMOUNT##",
|
||||
"custom" => "",
|
||||
);
|
||||
$params = array();
|
||||
foreach ($paypal_param as $pk => $pv) {
|
||||
$params[] = sprintf("%s=%s", $pk, $pv);
|
||||
}
|
||||
|
||||
$paypal_link = sprintf("%s?%s", $paypal_url, implode("&", $params));
|
||||
|
||||
$tks = $db->execute(sprintf("SELECT `tk_id`, `tk_name`, `tk_slug`, `tk_price`, `tk_currency`, `tk_amount` FROM `db_livetoken` WHERE `tk_active`='1';"));
|
||||
if ($tks->fields["tk_id"]) {
|
||||
$i = 0;
|
||||
|
||||
while (!$tks->EOF) {
|
||||
$rs[$i][0] = $tks->fields["tk_id"];
|
||||
$rs[$i][1] = $tks->fields["tk_name"];
|
||||
$rs[$i][2] = $tks->fields["tk_price"];
|
||||
$rs[$i][3] = $tks->fields["tk_currency"];
|
||||
$rs[$i][4] = $tks->fields["tk_amount"];
|
||||
|
||||
$paypal_string = $usr_id . '|' . $rs[$i][0] . '|' . $rs[$i][4];
|
||||
$paypal_item = rawurlencode($paypal_string . '|' . md5($paypal_string . $cfg["global_salt_key"]));
|
||||
$paypal_name = rawurlencode($cfg["website_shortname"] . ' - ' . $rs[$i][1]);
|
||||
$paypal_search = array("##ITEM_NAME##", "##PP_ITEM##", "##PP_CURRENCY##", "##PP_AMOUNT##");
|
||||
$paypal_replace = array($paypal_name, $paypal_item, $rs[$i][3], $rs[$i][2]);
|
||||
|
||||
$rs[$i][5] = str_replace($paypal_search, $paypal_replace, $paypal_link);
|
||||
|
||||
$ak = array_search($rs[$i][3], $cn);
|
||||
$rs[$i][3] = $cu[$ak];
|
||||
|
||||
$i += 1;
|
||||
$tks->MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
$ls["u"] = $us;
|
||||
$ls["l"] = $rs;
|
||||
|
||||
echo json_encode($ls);
|
||||
}
|
||||
}
|
||||
109
f_modules/m_frontend/m_acct/token_payment.php
Normal file
109
f_modules/m_frontend/m_acct/token_payment.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('backend', 'language.members.entries');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$cfg = $class_database->getConfigurations('paypal_log_file,paypal_logging,paypal_test,paypal_email,paypal_test_email,backend_notification_payment,backend_email,backend_username');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
if ($_POST and isset($_GET["do"]) and $_GET["do"] == "ipn") {
|
||||
$p = new VPaypalToken;
|
||||
$p->paypal_url = $cfg["paypal_test"] == 1 ? 'https://www.sandbox.paypal.com/cgi-bin/webscr' : 'https://www.paypal.com/cgi-bin/webscr';
|
||||
|
||||
if ($p->validate_ipn()) {
|
||||
$ipn_info = explode('|', urldecode($p->ipn_data["item_number"]));
|
||||
$usr_id = (int) $ipn_info[0];
|
||||
$item_id = (int) $ipn_info[1];
|
||||
$item_amt = (int) $ipn_info[2];
|
||||
$item_price = $class_filter->clr_str($p->ipn_data["mc_gross"]);
|
||||
$txn_id = $class_filter->clr_str($p->ipn_data["txn_id"]);
|
||||
$hash = $ipn_info[3];
|
||||
$pps = $usr_id . '|' . $item_id . '|' . $item_amt;
|
||||
|
||||
if ($hash == md5($pps . $cfg["global_salt_key"])) {
|
||||
$db->execute(sprintf("UPDATE `db_accountuser` SET `usr_tokencount`=`usr_tokencount`+%s WHERE `usr_id`='%s' LIMIT 1;", $item_amt, $usr_id));
|
||||
|
||||
if ($db->Affected_Rows() > 0) {
|
||||
$receipt = null;
|
||||
foreach ($p->ipn_data as $key => $value) {$receipt .= $key . ': ' . $value . '<br>';}
|
||||
|
||||
$ins_array = array(
|
||||
'usr_id' => $usr_id,
|
||||
'tk_id' => $item_id,
|
||||
'tk_amount' => $item_amt,
|
||||
'tk_price' => $item_price,
|
||||
'tk_date' => date("Y-m-d H:i:s"),
|
||||
'txn_id' => $txn_id,
|
||||
'txn_receipt' => str_replace('<br>', "\n", $receipt),
|
||||
);
|
||||
$class_database->doInsert('db_tokenpayments', $ins_array);
|
||||
|
||||
/* mail notifications */
|
||||
$notifier = new VNotify;
|
||||
$website_logo = $smarty->fetch($cfg["templates_dir"] . '/tpl_frontend/tpl_header/tpl_headerlogo.tpl');
|
||||
$user_data = VUserinfo::getUserInfo($usr_id);
|
||||
/* user notification */
|
||||
$_replace = array(
|
||||
'##TITLE##' => $language["payment.notification.token.subj"],
|
||||
'##LOGO##' => $website_logo,
|
||||
'##H2##' => $language["recovery.forgot.password.h2"] . $user_data["uname"] . ',',
|
||||
'##NR##' => $item_amt,
|
||||
'##YEAR##' => date('Y'),
|
||||
);
|
||||
$notifier->dst_mail = VUserinfo::getUserEmail($usr_id);
|
||||
$notifier->dst_name = $user_data["uname"];
|
||||
$notifier->Mail('frontend', 'token_notification_fe', $_replace);
|
||||
|
||||
$_output[] = $user_data["uname"] . ' -> token_notification_fe -> ' . $notifier->dst_mail . ' -> ' . date("Y-m-d H:i:s");
|
||||
/* admin notification */
|
||||
if ($cfg["backend_notification_payment"] == 1) {
|
||||
include 'f_core/config.backend.php';
|
||||
$main_url = $cfg["main_url"] . '/' . $backend_access_url;
|
||||
|
||||
$notifier->msg_subj = $language["payment.notification.token.subj.be"] . urldecode($p->ipn_data["payer_email"]);
|
||||
$notifier->dst_mail = $cfg["backend_email"];
|
||||
$notifier->dst_name = $cfg["backend_username"];
|
||||
|
||||
$_replace = array(
|
||||
'##TITLE##' => $notifier->msg_subj,
|
||||
'##LOGO##' => $website_logo,
|
||||
'##H2##' => $language["recovery.forgot.password.h2"] . $cfg["backend_username"] . ',',
|
||||
'##USER##' => '<a href="' . $main_url . '/' . VHref::getKey('be_members') . '?u=' . $user_data["key"] . '" target="_blank">' . $user_data["uname"] . '</a>',
|
||||
'##NR##' => $item_amt,
|
||||
'##PAID##' => $p->ipn_data["mc_gross"] . $p->ipn_data["mc_currency"],
|
||||
'##PAID_RECEIPT##' => urldecode($receipt),
|
||||
'##YEAR##' => date('Y'),
|
||||
);
|
||||
$notifier->Mail('backend', 'token_notification_be', $_replace);
|
||||
|
||||
$_output[] = $cfg["backend_username"] . ' -> token_notification_be -> ' . $notifier->dst_mail . ' -> ' . date("Y-m-d H:i:s");
|
||||
}
|
||||
|
||||
$log_mail = '.mailer.log';
|
||||
VServer::logToFile($log_mail, implode("\n", $_output));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
f_modules/m_frontend/m_acct/tokens.php
Normal file
71
f_modules/m_frontend/m_acct/tokens.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
define('_ISADMIN', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('backend', 'language.dashboard');
|
||||
include_once $class_language->setLanguageFile('backend', 'language.settings.entries');
|
||||
include_once $class_language->setLanguageFile('backend', 'language.subscriber');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.account');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
$ipn_check = false;
|
||||
$cfg[] = $class_database->getConfigurations('paypal_test,paypal_test_email,affiliate_module,affiliate_tracking_id,affiliate_view_id,affiliate_maps_api_key,affiliate_token_script,affiliate_payout_figure,affiliate_payout_units,affiliate_payout_currency,affiliate_payout_share,sub_shared_revenue,subscription_payout_currency,channel_views,sub_threshold,partner_requirements_min,partner_requirements_type,token_threshold');
|
||||
$logged_in = !$ipn_check ? VLogin::checkFrontend(VHref::getKey("tokens")) : null;
|
||||
$analytics = false;
|
||||
|
||||
if (isset($_GET["do"])) {
|
||||
switch ($_GET["do"]) {
|
||||
case "save-subscriber":
|
||||
break;
|
||||
case "showstats":
|
||||
echo $ht = VToken::userStats();
|
||||
break;
|
||||
case "make-partner":
|
||||
case "clear-partner":
|
||||
break;
|
||||
case "make-partner-email":
|
||||
case "clear-partner-email":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_GET["s"]) and !isset($_GET["do"])) {
|
||||
VAffiliate::allowRequest('partner');
|
||||
$smarty->assign('c_section', VHref::getKey("subscribers"));
|
||||
}
|
||||
|
||||
if (!isset($_GET["do"])) {
|
||||
$smarty->assign('file_type', 'sub');
|
||||
|
||||
if (isset($_GET["rg"]) and isset($_SESSION["USER_PARTNER"]) and (int) $_SESSION["USER_PARTNER"] == 1) {
|
||||
$smarty->assign('html_payouts', VToken::html_payouts(1));
|
||||
} else if (isset($_GET["rp"]) and isset($_SESSION["USER_PARTNER"]) and (int) $_SESSION["USER_PARTNER"] == 1) {
|
||||
$smarty->assign('html_payouts', VToken::html_payouts());
|
||||
} else if ((isset($_GET["rg"]) and (!isset($_SESSION["USER_PARTNER"]) or (int) $_SESSION["USER_PARTNER"] == 0)) or (isset($_GET["rp"]) and (!isset($_SESSION["USER_PARTNER"]) or (int) $_SESSION["USER_PARTNER"] == 0))) {
|
||||
header("Location: " . $cfg["main_url"] . '/' . VHref::getKey("tokens"));
|
||||
exit;
|
||||
}
|
||||
|
||||
$class_smarty->displayPage('frontend', 'tpl_tokens', $error_message, $notice_message);
|
||||
}
|
||||
34
f_modules/m_frontend/m_auth/auth.php
Normal file
34
f_modules/m_frontend/m_auth/auth.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
defined('_ISVALID') or exit;
|
||||
|
||||
class VAuth {
|
||||
public static function loginForm() {
|
||||
global $smarty;
|
||||
return $smarty->fetch('tpl_login.tpl');
|
||||
}
|
||||
|
||||
public static function registerForm() {
|
||||
global $smarty;
|
||||
return $smarty->fetch('tpl_register.tpl');
|
||||
}
|
||||
|
||||
public static function processLogin() {
|
||||
$username = VSecurity::postParam('username', 'string');
|
||||
$password = VSecurity::postParam('password', 'string');
|
||||
|
||||
if ($username && $password) {
|
||||
// Basic authentication logic
|
||||
global $class_database;
|
||||
$sql = "SELECT * FROM db_accountuser WHERE usr_user = ? AND usr_password = ?";
|
||||
$user = $class_database->execute($sql, [$username, md5($password)]);
|
||||
|
||||
if ($user && count($user) > 0) {
|
||||
VSession::setUserSession($user[0]);
|
||||
return ['success' => 'Login successful'];
|
||||
}
|
||||
}
|
||||
|
||||
return ['error' => 'Invalid credentials'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
35
f_modules/m_frontend/m_auth/captcha.php
Normal file
35
f_modules/m_frontend/m_auth/captcha.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$cfg = $class_database->getConfigurations('email_change_captcha_level,signup_captcha_level,frontend_username_recovery_captcha_level,frontend_password_recovery_captcha_level,backend_username_recovery_captcha_level,backend_password_recovery_captcha_level');
|
||||
|
||||
switch ($_GET["extra"]) {
|
||||
case '':$_c = new VCaptcha('', $cfg["signup_captcha_level"]);
|
||||
break;
|
||||
case '1':$_c = new VCaptcha('recover_left', $cfg["frontend_password_recovery_captcha_level"]);
|
||||
break;
|
||||
case '2':$_c = new VCaptcha('recover_right', $cfg["frontend_username_recovery_captcha_level"]);
|
||||
break;
|
||||
case '3':$_c = new VCaptcha('recover_left', $cfg["backend_password_recovery_captcha_level"]);
|
||||
break;
|
||||
case '4':$_c = new VCaptcha('recover_right', $cfg["backend_username_recovery_captcha_level"]);
|
||||
break;
|
||||
case '5':$_c = new VCaptcha('change_email', $cfg["email_change_captcha_level"]);
|
||||
break;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"critical","message":"Uncaught Exception: Non-static method VLogin::isLoggedIn() cannot be called statically in /srv/easystream/f_modules/m_frontend/m_auth/signin.php on line 81","context":{"exception_class":"Error","file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":81,"trace":"#0 {main}","previous":null},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.logger.php","line":395,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":117,"function":"critical","class":"VLogger"},{"file":"unknown","line":0,"function":"handleException","class":"VErrorHandler"}]}
|
||||
@@ -0,0 +1,26 @@
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"session_lifetime\" in /srv/easystream/f_core/f_classes/class.session.php on line 26","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.session.php","line":26,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":26,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/config.core.php","line":61,"function":"init","class":"VSession"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"session_name\" in /srv/easystream/f_core/f_classes/class.session.php on line 40","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.session.php","line":40,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":40,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/config.core.php","line":61,"function":"init","class":"VSession"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: ini_set(): session.name "" cannot be numeric or empty in /srv/easystream/f_core/f_classes/class.session.php on line 40","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.session.php","line":40,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"unknown","line":0,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":40,"function":"ini_set","class":null},{"file":"/srv/easystream/f_core/config.core.php","line":61,"function":"init","class":"VSession"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"session_lifetime\" in /srv/easystream/f_core/f_classes/class.session.php on line 63","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.session.php","line":63,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":63,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":50,"function":"_sessionInit","class":"VSession"},{"file":"/srv/easystream/f_core/config.core.php","line":61,"function":"init","class":"VSession"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"fe_lang\" in /srv/easystream/f_core/f_classes/class.session.php on line 83","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.session.php","line":83,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":83,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":50,"function":"_sessionInit","class":"VSession"},{"file":"/srv/easystream/f_core/config.core.php","line":61,"function":"init","class":"VSession"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined variable $backend_access_url in /srv/easystream/f_core/config.core.php on line 68","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/config.core.php","line":68,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/config.core.php","line":68,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined property: VFilter::$_allowDOMEvents in /srv/easystream/f_core/f_classes/class.filter.php on line 212","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":212,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":212,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":318,"function":"removeEvilAttributes","class":"VFilter"},{"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":332,"function":"sanitize","class":"VFilter"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":141,"function":"clr_str","class":"VFilter"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":56,"function":"getUserIP","class":"VIPaccess"},{"file":"/srv/easystream/f_core/config.core.php","line":68,"function":"sectionAccess","class":"VIPaccess"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined property: VFilter::$_allowDOMEvents in /srv/easystream/f_core/f_classes/class.filter.php on line 212","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":212,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":212,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":318,"function":"removeEvilAttributes","class":"VFilter"},{"file":"/srv/easystream/f_core/f_classes/class.filter.php","line":332,"function":"sanitize","class":"VFilter"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":56,"function":"clr_str","class":"VFilter"},{"file":"/srv/easystream/f_core/config.core.php","line":68,"function":"sectionAccess","class":"VIPaccess"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Trying to access array offset on value of type bool in /srv/easystream/f_core/f_classes/class.ipaccess.php on line 27","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":27,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":27,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":58,"function":"banIPrange_db","class":"VIPaccess"},{"file":"/srv/easystream/f_core/config.core.php","line":68,"function":"sectionAccess","class":"VIPaccess"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_ip_based_access\" in /srv/easystream/f_core/f_classes/class.ipaccess.php on line 69","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":69,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":69,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/config.core.php","line":68,"function":"sectionAccess","class":"VIPaccess"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"backend_ip_based_access\" in /srv/easystream/f_core/f_classes/class.ipaccess.php on line 70","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":70,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.ipaccess.php","line":70,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/config.core.php","line":68,"function":"sectionAccess","class":"VIPaccess"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"USER_ID\" in /srv/easystream/f_core/f_classes/class.session.php on line 139","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_core/f_classes/class.session.php","line":139,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_core/f_classes/class.session.php","line":139,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_core/config.core.php","line":71,"function":"isLoggedIn","class":"VSession"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":21,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php on line 199","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":199,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":199,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":24,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php on line 204","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":204,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":204,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":24,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php on line 205","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":205,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":205,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":24,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"custom_tagline\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php on line 383","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":383,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":383,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":24,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php on line 402","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":402,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":402,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":24,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php on line 465","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":465,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.global.php","line":465,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":24,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php on line 31","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php","line":31,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php","line":31,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":25,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php on line 38","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php","line":38,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php","line":38,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":25,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"website_shortname\" in /srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php on line 62","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php","line":62,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_data/data_languages/en_US/lang_frontend/language.signin.php","line":62,"function":"handleError","class":"VErrorHandler"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":25,"function":"include_once","class":null}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"paid_memberships\" in /srv/easystream/f_modules/m_frontend/m_auth/signin.php on line 30","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":30,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":30,"function":"handleError","class":"VErrorHandler"}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"gp_auth\" in /srv/easystream/f_modules/m_frontend/m_auth/signin.php on line 32","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":32,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":32,"function":"handleError","class":"VErrorHandler"}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"fb_auth\" in /srv/easystream/f_modules/m_frontend/m_auth/signin.php on line 56","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":56,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":56,"function":"handleError","class":"VErrorHandler"}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"frontend_global_submit\" in /srv/easystream/f_modules/m_frontend/m_auth/signin.php on line 74","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":74,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":74,"function":"handleError","class":"VErrorHandler"}]}
|
||||
{"timestamp":"2025-10-18 05:10:14","request_id":"req_68f32136711d68.26462734","ip":"172.18.0.1","user_agent":"curl/8.14.1","request_uri":"/f_modules/m_frontend/m_auth/signin.php","request_method":"GET","user_id":null,"session_id":null,"memory_usage":2097152,"peak_memory":2097152,"execution_time":0.1830599308013916,"level":"warning","message":"Warning: Undefined array key \"login_remember\" in /srv/easystream/f_modules/m_frontend/m_auth/signin.php on line 80","context":{"error_type":"Warning","severity":2,"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":80,"context":[]},"backtrace":[{"file":"/srv/easystream/f_core/f_classes/class.errorhandler.php","line":95,"function":"log","class":"VLogger"},{"file":"/srv/easystream/f_modules/m_frontend/m_auth/signin.php","line":80,"function":"handleError","class":"VErrorHandler"}]}
|
||||
164
f_modules/m_frontend/m_auth/fb_callback_login.php
Normal file
164
f_modules/m_frontend/m_auth/fb_callback_login.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_facebook/Facebook/autoload.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.recovery');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('fb_auth,fb_app_id,fb_app_secret');
|
||||
|
||||
$_SESSION['FBRLH_' . 'state'] = $class_filter->clr_str($_GET['state']);
|
||||
|
||||
$fb = new Facebook\Facebook([
|
||||
'app_id' => $cfg['fb_app_id'],
|
||||
'app_secret' => $cfg['fb_app_secret'],
|
||||
'default_graph_version' => 'v2.7',
|
||||
'default_access_token' => '1061711193887319|fc3a99ba0d42b98b51ac3fa124268422',
|
||||
]);
|
||||
|
||||
$helper = $fb->getRedirectLoginHelper();
|
||||
|
||||
try {
|
||||
$u = $cfg["main_url"] . '/f_modules/m_frontend/m_auth/fb_callback_login.php';
|
||||
$accessToken = $helper->getAccessToken($u);
|
||||
} catch (Facebook\Exceptions\FacebookResponseException $e) {
|
||||
// When Graph returns an error
|
||||
echo 'Graph returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
} catch (Facebook\Exceptions\FacebookSDKException $e) {
|
||||
// When validation fails or other local issues
|
||||
echo 'Facebook SDK returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($accessToken)) {
|
||||
if ($helper->getError()) {
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo "Error: " . $helper->getError() . "\n";
|
||||
echo "Error Code: " . $helper->getErrorCode() . "\n";
|
||||
echo "Error Reason: " . $helper->getErrorReason() . "\n";
|
||||
echo "Error Description: " . $helper->getErrorDescription() . "\n";
|
||||
} else {
|
||||
header('HTTP/1.0 400 Bad Request');
|
||||
echo 'Bad request';
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
// Logged in
|
||||
|
||||
// The OAuth 2.0 client handler helps us manage access tokens
|
||||
$oAuth2Client = $fb->getOAuth2Client();
|
||||
// Get the access token metadata from /debug_token
|
||||
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
|
||||
// Validation (these will throw FacebookSDKException's when they fail)
|
||||
$tokenMetadata->validateAppId($cfg['fb_app_id']); // Replace {app-id} with your app id
|
||||
// If you know the user ID this access token belongs to, you can validate it here
|
||||
$tokenMetadata->validateExpiration();
|
||||
|
||||
$user_id = $tokenMetadata->getUserId();
|
||||
|
||||
if (!$accessToken->isLongLived()) {
|
||||
// Exchanges a short-lived access token for a long-lived one
|
||||
try {
|
||||
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
|
||||
} catch (Facebook\Exceptions\FacebookSDKException $e) {
|
||||
echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$_SESSION['fb_access_token'] = (string) $accessToken;
|
||||
|
||||
try {
|
||||
// Returns a `Facebook\FacebookResponse` object
|
||||
$response = $fb->get('/me?fields=id,about,cover,email,first_name,picture.width(300).height(300),last_name,name', $accessToken);
|
||||
} catch (Facebook\Exceptions\FacebookResponseException $e) {
|
||||
echo 'Graph returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
} catch (Facebook\Exceptions\FacebookSDKException $e) {
|
||||
echo 'Facebook SDK returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = $response->getGraphUser();
|
||||
|
||||
$go = true;
|
||||
|
||||
if ($user_id and $go) {
|
||||
$rs = $db->execute(sprintf("SELECT `usr_id` FROM `db_accountuser` WHERE `oauth_provider`='facebook' AND `oauth_uid`='%s' LIMIT 1;", $user_id));
|
||||
|
||||
if ($rs->fields['usr_id']) {
|
||||
$usr_id = $rs->fields['usr_id'];
|
||||
$reguser = VUserinfo::getUserInfo($usr_id);
|
||||
} else {
|
||||
if (isset($user['birthday']->date)) {
|
||||
$a = $user['birthday']->date;
|
||||
$b = explode(' ', $a);
|
||||
$birthday = $b[0];
|
||||
} else {
|
||||
$birthday = '0000-00-00';
|
||||
}
|
||||
$uu = explode('@', $user['email']);
|
||||
$_SESSION["fb_user"] = array(
|
||||
'id' => $user_id,
|
||||
'email' => $user['email'],
|
||||
'name' => $user['name'],
|
||||
'picture' => $user['picture']['url'],
|
||||
'username' => $uu[0],
|
||||
'gender' => 'M',
|
||||
'country' => (isset($user['user_location']->name) ? $user['user_location']->name : ''),
|
||||
'city' => (isset($user['user_location']->hometown) ? $user['user_location']->hometown : ''),
|
||||
'birth_date' => $birthday,
|
||||
'status' => 1,
|
||||
);
|
||||
|
||||
echo VSignup::auth_register_ajax();
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($reguser['key'] != '') {
|
||||
$_SESSION["USER_ID"] = $usr_id;
|
||||
$_SESSION["USER_NAME"] = $reguser['uname'];
|
||||
$_SESSION["USER_KEY"] = $reguser['key'];
|
||||
$_SESSION["USER_DNAME"] = $reguser['dname'];
|
||||
|
||||
$login_update = VLogin::updateOnLogin($usr_id);
|
||||
$log_activity = ($cfg["activity_logging"] == 1 and $action = new VActivity($usr_id, 0)) ? $action->addTo('log_signin') : null;
|
||||
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('account');
|
||||
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
} else {
|
||||
die('Account suspended!');
|
||||
}
|
||||
}
|
||||
152
f_modules/m_frontend/m_auth/fb_callback_register.php
Normal file
152
f_modules/m_frontend/m_auth/fb_callback_register.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_facebook/Facebook/autoload.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.recovery');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$class_database->getConfigurations('fb_auth,fb_app_id,fb_app_secret');
|
||||
|
||||
$_SESSION['FBRLH_' . 'state'] = $class_filter->clr_str($_GET['state']);
|
||||
|
||||
$fb = new Facebook\Facebook([
|
||||
'app_id' => $cfg['fb_app_id'],
|
||||
'app_secret' => $cfg['fb_app_secret'],
|
||||
'default_graph_version' => 'v2.7',
|
||||
'default_access_token' => '1061711193887319|fc3a99ba0d42b98b51ac3fa124268422',
|
||||
]);
|
||||
|
||||
$helper = $fb->getRedirectLoginHelper();
|
||||
|
||||
try {
|
||||
$u = $cfg["main_url"] . '/f_modules/m_frontend/m_auth/fb_callback_register.php';
|
||||
$accessToken = $helper->getAccessToken($u);
|
||||
} catch (Facebook\Exceptions\FacebookResponseException $e) {
|
||||
// When Graph returns an error
|
||||
echo 'Graph returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
} catch (Facebook\Exceptions\FacebookSDKException $e) {
|
||||
// When validation fails or other local issues
|
||||
echo 'Facebook SDK returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($accessToken)) {
|
||||
if ($helper->getError()) {
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo "Error: " . $helper->getError() . "\n";
|
||||
echo "Error Code: " . $helper->getErrorCode() . "\n";
|
||||
echo "Error Reason: " . $helper->getErrorReason() . "\n";
|
||||
echo "Error Description: " . $helper->getErrorDescription() . "\n";
|
||||
} else {
|
||||
header('HTTP/1.0 400 Bad Request');
|
||||
echo 'Bad request';
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Logged in
|
||||
|
||||
// The OAuth 2.0 client handler helps us manage access tokens
|
||||
$oAuth2Client = $fb->getOAuth2Client();
|
||||
// Get the access token metadata from /debug_token
|
||||
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
|
||||
// Validation (these will throw FacebookSDKException's when they fail)
|
||||
$tokenMetadata->validateAppId($cfg['fb_app_id']); // Replace {app-id} with your app id
|
||||
// If you know the user ID this access token belongs to, you can validate it here
|
||||
$tokenMetadata->validateExpiration();
|
||||
|
||||
$user_id = $tokenMetadata->getUserId();
|
||||
|
||||
if (!$accessToken->isLongLived()) {
|
||||
// Exchanges a short-lived access token for a long-lived one
|
||||
try {
|
||||
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
|
||||
} catch (Facebook\Exceptions\FacebookSDKException $e) {
|
||||
echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$_SESSION['fb_access_token'] = (string) $accessToken;
|
||||
|
||||
try {
|
||||
// Returns a `Facebook\FacebookResponse` object
|
||||
$response = $fb->get('/me?fields=id,about,birthday,context,cover,currency,education,email,first_name,gender,hometown,picture.width(300).height(300),last_name,link,locale,location,middle_name,name,political,quotes,relationship_status,religion,significant_other,website', $accessToken);
|
||||
} catch (Facebook\Exceptions\FacebookResponseException $e) {
|
||||
echo 'Graph returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
} catch (Facebook\Exceptions\FacebookSDKException $e) {
|
||||
echo 'Facebook SDK returned an error: ' . $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = $response->getGraphUser();
|
||||
|
||||
if (isset($user['birthday']->date)) {
|
||||
$a = $user['birthday']->date;
|
||||
$b = explode(' ', $a);
|
||||
$birthday = $b[0];
|
||||
} else {
|
||||
$birthday = '0000-00-00';
|
||||
}
|
||||
|
||||
$go = true;
|
||||
|
||||
if ($user_id and $go) {
|
||||
$rs = $db->execute(sprintf("SELECT `usr_id` FROM `db_accountuser` WHERE `oauth_provider`='facebook' AND `oauth_uid`='%s' LIMIT 1;", $user_id));
|
||||
|
||||
if ($rs->fields['usr_id']) {
|
||||
$usr_id = $rs->fields['usr_id'];
|
||||
$_SESSION["USER_ERROR"] = $language["notif.error.accout.linked"];
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('signin') . '?f=';
|
||||
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION["fb_user"] = array(
|
||||
'id' => $user_id,
|
||||
'email' => $user['email'],
|
||||
'name' => $user['name'],
|
||||
'gender' => ($user['gender'] == 'male' ? 'M' : 'F'),
|
||||
'country' => (isset($user['user_location']->name) ? $user['user_location']->name : ''),
|
||||
'city' => (isset($user['user_location']->hometown) ? $user['user_location']->hometown : ''),
|
||||
'birth_date' => $birthday,
|
||||
'status' => 1,
|
||||
);
|
||||
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('signup') . '?u=' . $user_id;
|
||||
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
108
f_modules/m_frontend/m_auth/gp_callback_login.php
Normal file
108
f_modules/m_frontend/m_auth/gp_callback_login.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_google/Google_Client.php';
|
||||
include_once 'f_core/f_classes/class_google/contrib/Google_Oauth2Service.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.recovery');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('gp_auth,gp_app_id,gp_app_secret,list_reserved_users');
|
||||
|
||||
$clientId = $cfg['gp_app_id'];
|
||||
$clientSecret = $cfg['gp_app_secret'];
|
||||
$redirectUrl = $cfg['main_url'] . '/f_modules/m_frontend/m_auth/gp_callback_login.php';
|
||||
$homeUrl = $cfg['main_url'];
|
||||
|
||||
$gClient = new Google_Client();
|
||||
$gClient->setClientId($clientId);
|
||||
$gClient->setClientSecret($clientSecret);
|
||||
$gClient->setRedirectUri($redirectUrl);
|
||||
$google_oauthV2 = new Google_Oauth2Service($gClient);
|
||||
|
||||
if (isset($_GET['code'])) {
|
||||
$gClient->authenticate();
|
||||
$_SESSION['token'] = $gClient->getAccessToken();
|
||||
header('Location: ' . filter_var($redirectUrl, FILTER_SANITIZE_URL));
|
||||
}
|
||||
|
||||
if (isset($_SESSION['token'])) {
|
||||
$gClient->setAccessToken($_SESSION['token']);
|
||||
}
|
||||
|
||||
if ($gClient->getAccessToken()) {
|
||||
//logged in
|
||||
$userProfile = $google_oauthV2->userinfo->get();
|
||||
$user_id = $userProfile['id'];
|
||||
|
||||
if ($user_id) {
|
||||
$rs = $db->execute(sprintf("SELECT `usr_id` FROM `db_accountuser` WHERE `oauth_provider`='google' AND `oauth_uid`='%s' LIMIT 1;", $user_id));
|
||||
|
||||
if ($rs->fields['usr_id']) {
|
||||
$usr_id = $rs->fields['usr_id'];
|
||||
$reguser = VUserinfo::getUserInfo($usr_id);
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('account');
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
} else {
|
||||
$uu = explode('@', $userProfile['email']);
|
||||
$_SESSION["gp_user"] = array(
|
||||
'id' => $user_id,
|
||||
'email' => $userProfile['email'],
|
||||
'name' => $userProfile['name'],
|
||||
'picture' => $userProfile['picture'],
|
||||
'username' => $uu[0],
|
||||
'gender' => 'M',
|
||||
'country' => '',
|
||||
'city' => '',
|
||||
'birth_date' => '0000-00-00',
|
||||
'status' => 1,
|
||||
);
|
||||
|
||||
echo VSignup::auth_register_ajax();
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
if ($reguser['key'] != '') {
|
||||
$_SESSION["USER_ID"] = $usr_id;
|
||||
$_SESSION["USER_NAME"] = $reguser['uname'];
|
||||
$_SESSION["USER_KEY"] = $reguser['key'];
|
||||
$_SESSION["USER_DNAME"] = $reguser['dname'];
|
||||
|
||||
$login_update = VLogin::updateOnLogin($usr_id);
|
||||
$log_activity = ($cfg["activity_logging"] == 1 and $action = new VActivity($usr_id, 0)) ? $action->addTo('log_signin') : null;
|
||||
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('account');
|
||||
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
} else {
|
||||
die('Account suspended!');
|
||||
}
|
||||
}
|
||||
}
|
||||
96
f_modules/m_frontend/m_auth/gp_callback_register.php
Normal file
96
f_modules/m_frontend/m_auth/gp_callback_register.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_google/Google_Client.php';
|
||||
include_once 'f_core/f_classes/class_google/contrib/Google_Oauth2Service.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.recovery');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('gp_auth,gp_app_id,gp_app_secret');
|
||||
|
||||
$clientId = $cfg['gp_app_id'];
|
||||
$clientSecret = $cfg['gp_app_secret'];
|
||||
$redirectUrl = $cfg['main_url'] . '/f_modules/m_frontend/m_auth/gp_callback_register.php';
|
||||
$homeUrl = $cfg['main_url'];
|
||||
|
||||
$gClient = new Google_Client();
|
||||
$gClient->setClientId($clientId);
|
||||
$gClient->setClientSecret($clientSecret);
|
||||
$gClient->setRedirectUri($redirectUrl);
|
||||
$google_oauthV2 = new Google_Oauth2Service($gClient);
|
||||
|
||||
if (isset($_GET['code'])) {
|
||||
$gClient->authenticate();
|
||||
$_SESSION['token'] = $gClient->getAccessToken();
|
||||
|
||||
header('Location: ' . filter_var($redirectUrl, FILTER_SANITIZE_URL));
|
||||
}
|
||||
|
||||
if (isset($_SESSION['token'])) {
|
||||
$gClient->setAccessToken($_SESSION['token']);
|
||||
}
|
||||
|
||||
if ($gClient->getAccessToken()) {
|
||||
//logged in
|
||||
$user = $google_oauthV2->userinfo->get();
|
||||
$user_id = $user['id'];
|
||||
$user_email = $user['email'];
|
||||
$user_name = $user['name'];
|
||||
$user_gender = $user['gender'][0];
|
||||
$user_gender = $user_gender == '' ? 'M' : $user_gender;
|
||||
$user_bday = '0000-00-00';
|
||||
|
||||
$rs = $db->execute(sprintf("SELECT `usr_id` FROM `db_accountuser` WHERE `oauth_provider`='google' AND `oauth_uid`='%s' LIMIT 1;", $user_id));
|
||||
|
||||
if ($rs->fields['usr_id']) {
|
||||
$usr_id = $rs->fields['usr_id'];
|
||||
$_SESSION["USER_ERROR"] = $language["notif.error.accout.linked"];
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('signin') . '?f=';
|
||||
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION["gp_user"] = array(
|
||||
'id' => $user_id,
|
||||
'email' => $user_email,
|
||||
'name' => $user_name,
|
||||
'gender' => $user_gender,
|
||||
'birth_date' => $user_bday,
|
||||
'status' => 1,
|
||||
);
|
||||
|
||||
$loc = $cfg["main_url"] . '/' . VHref::getKey('signup') . '?u=' . $user_id;
|
||||
|
||||
echo '<script type="text/javascript">opener.location="' . $loc . '"; window.close();</script>';
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
28
f_modules/m_frontend/m_auth/login.php
Normal file
28
f_modules/m_frontend/m_auth/login.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
if (!defined('_ISVALID')) define('_ISVALID', true);
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
// Handle login
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($username && $password) {
|
||||
$sql = "SELECT * FROM db_accountuser WHERE usr_user = ? AND usr_password = MD5(?) AND usr_active = 1";
|
||||
$user = $class_database->execute($sql, [$username, $password]);
|
||||
|
||||
if ($user && count($user) > 0) {
|
||||
$_SESSION['usr_id'] = $user[0]['usr_id'];
|
||||
$_SESSION['usr_user'] = $user[0]['usr_user'];
|
||||
$_SESSION['usr_key'] = $user[0]['usr_key'];
|
||||
|
||||
echo "<script>alert('Login successful! Redirecting...'); window.location.href='/';</script>";
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Invalid credentials';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo $class_smarty->displayPage('frontend', 'tpl_auth_login');
|
||||
?>
|
||||
116
f_modules/m_frontend/m_auth/recovery.php
Normal file
116
f_modules/m_frontend/m_auth/recovery.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_recaptcha/autoload.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.recovery');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
$notifier = new VNotify;
|
||||
|
||||
$cfg = $class_database->getConfigurations('activity_logging,password_recovery_captcha,username_recovery_captcha,recovery_link_lifetime,allow_username_recovery,allow_password_recovery,backend_username_recovery,backend_password_recovery,backend_username_recovery_captcha,backend_password_recovery_captcha,backend_username,backend_email,noreply_email,recaptcha_site_key,recaptcha_secret_key');
|
||||
$section_check = ($class_smarty->backendSectionCheck() == 1) ? 'backend' : 'frontend';
|
||||
$logged_in = $section_check == 'frontend' ? VLogin::isLoggedIn('fe') : ($section_check == 'backend' ? VLogin::isLoggedIn('be') : null);
|
||||
|
||||
$rec_username = $_POST["rec_username"] != '' ? $class_filter->clr_str($_POST["rec_username"]) : null;
|
||||
$rec_email = $_POST["rec_email"] != '' ? $class_filter->clr_str($_POST["rec_email"]) : null;
|
||||
$left_captcha = (($cfg["password_recovery_captcha"] == 1 or $cfg["backend_password_recovery_captcha"] == 1) and $_POST["g-recaptcha-response"] != '') ? $class_filter->clr_str($_POST["g-recaptcha-response"]) : null;
|
||||
$right_captcha = (($cfg["username_recovery_captcha"] == 1 or $cfg["backend_username_recovery_captcha"] == 1) and $_POST["g-recaptcha-response"] != '') ? $class_filter->clr_str($_POST["g-recaptcha-response"]) : null;
|
||||
|
||||
$pass_rec_cond = ($section_check == 'backend') ? $cfg["backend_password_recovery"] : $cfg["allow_password_recovery"];
|
||||
$pass_rec_captcha = ($section_check == 'backend') ? $cfg["backend_password_recovery_captcha"] : $cfg["password_recovery_captcha"];
|
||||
$user_rec_cond = ($section_check == 'backend') ? $cfg["backend_username_recovery"] : $cfg["allow_username_recovery"];
|
||||
$user_rec_captcha = ($section_check == 'backend') ? $cfg["backend_username_recovery_captcha"] : $cfg["username_recovery_captcha"];
|
||||
|
||||
$siteKey = $cfg['recaptcha_site_key'];
|
||||
$secret = $cfg['recaptcha_secret_key'];
|
||||
|
||||
switch (intval($_GET["t"])) {
|
||||
case '':break;
|
||||
case '0':break;
|
||||
case '1':
|
||||
if ($pass_rec_cond == 1) {
|
||||
switch ($pass_rec_captcha) {
|
||||
case '0':$password_recovery = (!VUserinfo::existingUsername($rec_username, $section_check)) ? $notifier->showNotice('error', $language["notif.error.invalid.request"]) : (VUserinfo::existingUsername($rec_username, $section_check)) ? VNotify::queInit('password_recovery', array(VUserinfo::getUserEmail(VUserinfo::getUserID($rec_username))), $section_check) . VNotify::showNotice('confirmation', $language["notif.success.request"], 'x_err') : null;
|
||||
break;
|
||||
case '1':
|
||||
if (!VUserinfo::existingUsername($rec_username, $section_check) or $left_captcha == '') {
|
||||
$notifier->showNotice('error', $language["notif.error.invalid.request"]);
|
||||
} elseif (VUserinfo::existingUsername($rec_username, $section_check)) {
|
||||
$recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\CurlPost());
|
||||
$resp = $recaptcha->verify($left_captcha, $_SERVER[REM_ADDR]);
|
||||
|
||||
if ($resp->isSuccess()) {
|
||||
VNotify::queInit('password_recovery', array(VUserinfo::getUserEmail(VUserinfo::getUserID($rec_username))), $section_check) . VNotify::showNotice('confirmation', $language["notif.success.request"], 'x_err');
|
||||
} else {
|
||||
foreach ($resp->getErrorCodes() as $code) {
|
||||
$notifier->showNotice('error', $code);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
$log = ($cfg["activity_logging"] == 1 and $action = new VActivity($user_id, 0)) ? $action->addTo('log_urecovery') : null;
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
if ($user_rec_cond == 1) {
|
||||
switch ($user_rec_captcha) {
|
||||
case '0':$username_recovery = (!VUserinfo::existingEmail($rec_email, $section_check)) ? $notifier->showNotice('error', $language["notif.error.invalid.request"], 'r_err') : (VUserinfo::existingEmail($rec_email, $section_check)) ? VNotify::queInit('username_recovery', array($rec_email), $section_check) . VNotify::showNotice('confirmation', $language["notif.success.request"], 'r_err') : null;
|
||||
break;
|
||||
case '1':
|
||||
if (!VUserinfo::existingEmail($rec_email, $section_check) or $right_captcha == '') {
|
||||
$notifier->showNotice('error', $language["notif.error.invalid.request"], 'r_err');
|
||||
} elseif (VUserinfo::existingEmail($rec_email, $section_check)) {
|
||||
$recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\CurlPost());
|
||||
$resp = $recaptcha->verify($right_captcha, $_SERVER[REM_ADDR]);
|
||||
|
||||
if ($resp->isSuccess()) {
|
||||
VNotify::queInit('username_recovery', array($rec_email), $section_check) . VNotify::showNotice('confirmation', $language["notif.success.request"], 'r_err');
|
||||
} else {
|
||||
foreach ($resp->getErrorCodes() as $code) {
|
||||
$notifier->showNotice('error', $code);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
$log = ($cfg["activity_logging"] == 1 and $action = new VActivity($user_id, 0)) ? $action->addTo('log_precovery') : null;
|
||||
}
|
||||
break;
|
||||
default:break;
|
||||
}
|
||||
if (intval($_POST["reset_password"] == 1) or ($_GET["s"] != '' and $_GET["id"] != '')) {
|
||||
$error_message = ($_GET["s"] != '' and $_GET["id"] != '') ? VRecovery::validCheck($section_check) : null;
|
||||
$error_message = (intval($_POST["reset_password"] == 1) and $error_message == '') ? VRecovery::processForm($section_check) : $error_message;
|
||||
$notice_message = ((intval($_POST["reset_password"] == 1)) and ($error_message == '' and VRecovery::doPasswordReset($section_check))) ? $language["recovery.forgot.password.confirm"] : null;
|
||||
}
|
||||
|
||||
$u = VUserinfo::getUserInfo(VRecovery::getRecoveryID($_GET["s"]));
|
||||
$recovery_username = ($_GET["s"] != '' and $_GET["id"] != '' and $section_check == 'frontend') ? $smarty->assign('fe_recovery_username', $u["uname"]) : ($_GET["s"] != '' and $_GET["id"] != '' and $section_check == 'backend') ? $smarty->assign('recovery_username', $cfg["backend_username"]) : null;
|
||||
$page_display = ($_GET["t"] == '') ? $class_smarty->displayPage($section_check, ($section_check == 'frontend' ? 'tpl_recovery' : 'backend_tpl_recovery'), $error_message, $notice_message) : null;
|
||||
8
f_modules/m_frontend/m_auth/renew.php
Normal file
8
f_modules/m_frontend/m_auth/renew.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
define("_ISVALID", true);
|
||||
include_once "f_core/config.core.php";
|
||||
// Password renewal - basic implementation
|
||||
echo "<h1>Password Renewal</h1>";
|
||||
echo "<p>Password renewal functionality coming soon...</p>";
|
||||
echo "<a href=\"/signin\">← Back to Sign In</a>";
|
||||
?>
|
||||
85
f_modules/m_frontend/m_auth/signin.php
Normal file
85
f_modules/m_frontend/m_auth/signin.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once 'f_core/f_classes/class_recaptcha/autoload.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
|
||||
$error_message = isset($_SESSION["USER_ERROR"]) ? $_SESSION["USER_ERROR"] : null;
|
||||
$notice_message = null;
|
||||
$cfg = $class_database->getConfigurations('login_remember,paid_memberships,frontend_signin_section,frontend_signin_count,fb_app_id,fb_app_secret,fb_auth,gp_app_id,gp_app_secret,gp_auth,recaptcha_site_key,recaptcha_secret_key,signin_captcha,list_reserved_users');
|
||||
$_SESSION["renew_id"] = $cfg["paid_memberships"] == 1 ? '' : null;
|
||||
|
||||
if ($cfg['gp_auth'] == 1 and $cfg["frontend_signin_section"] == 1) {
|
||||
//google authentication
|
||||
include_once 'f_core/f_classes/class_google/Google_Client.php';
|
||||
include_once 'f_core/f_classes/class_google/contrib/Google_Oauth2Service.php';
|
||||
|
||||
$clientId = $cfg['gp_app_id'];
|
||||
$clientSecret = $cfg['gp_app_secret'];
|
||||
$redirectUrl = $cfg['main_url'] . '/f_modules/m_frontend/m_auth/gp_callback_login.php';
|
||||
$homeUrl = $cfg['main_url'];
|
||||
|
||||
$gClient = new Google_Client();
|
||||
$gClient->setAccessType('online');
|
||||
$gClient->setApprovalPrompt('auto');
|
||||
$gClient->setClientId($clientId);
|
||||
$gClient->setClientSecret($clientSecret);
|
||||
$gClient->setRedirectUri($redirectUrl);
|
||||
|
||||
$google_oauthV2 = new Google_Oauth2Service($gClient);
|
||||
|
||||
$authUrl = $gClient->createAuthUrl();
|
||||
|
||||
$smarty->assign('gp_loginUrl', htmlspecialchars($authUrl));
|
||||
}
|
||||
|
||||
if ($cfg['fb_auth'] == 1 and $cfg["frontend_signin_section"] == 1) {
|
||||
//facebook authentication
|
||||
include_once 'f_core/f_classes/class_facebook/Facebook/autoload.php';
|
||||
|
||||
$fb = new Facebook\Facebook([
|
||||
'app_id' => $cfg['fb_app_id'],
|
||||
'app_secret' => $cfg['fb_app_secret'],
|
||||
'default_graph_version' => 'v2.7',
|
||||
'default_access_token' => '1061711193887319|fc3a99ba0d42b98b51ac3fa124268422',
|
||||
]);
|
||||
|
||||
$fb_helper = $fb->getRedirectLoginHelper();
|
||||
$fb_permissions = ['email']; // Optional permissions
|
||||
$fb_loginUrl = $fb_helper->getLoginUrl($cfg['main_url'] . '/f_modules/m_frontend/m_auth/fb_callback_login.php', $fb_permissions);
|
||||
|
||||
$smarty->assign('fb_loginUrl', htmlspecialchars($fb_loginUrl));
|
||||
}
|
||||
|
||||
if (intval($_POST["frontend_global_submit"] == 1) and $cfg["frontend_signin_section"] == 1) {
|
||||
//regular login
|
||||
$remember = ($error_message == '' and intval($_POST["signin_remember"]) == 1) ? 1 : null;
|
||||
$error_message = !VLogin::loginAttempt('frontend', $class_filter->clr_str($_POST["frontend_signin_username"]), $_POST["frontend_signin_password"], $remember) ? $language["frontend.signin.error.auth"] : null;
|
||||
}
|
||||
|
||||
$remember = ($cfg["login_remember"] == 1 and $cfg["frontend_signin_section"] == 1) ? VLoginRemember::checkLogin('frontend') : null;
|
||||
$logged_in = VLogin::isLoggedIn();
|
||||
|
||||
$class_smarty->displayPage('frontend', 'tpl_signin', $error_message, $notice_message);
|
||||
|
||||
$_SESSION["USER_ERROR"] = null;
|
||||
25
f_modules/m_frontend/m_auth/signout.php
Normal file
25
f_modules/m_frontend/m_auth/signout.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$signout = VLogin::logoutAttempt('frontend');
|
||||
73
f_modules/m_frontend/m_auth/signup.php
Normal file
73
f_modules/m_frontend/m_auth/signup.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once 'f_core/f_classes/class_recaptcha/autoload.php';
|
||||
include_once 'f_core/f_classes/class_facebook/Facebook/autoload.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('backend', 'language.members.entries');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signup');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.welcome');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
|
||||
$cfg = $class_database->getConfigurations('global_signup,signup_ip_access,list_ip_signup,disabled_signup_message,reserved_usernames,frontend_signin_section,frontend_signin_count,signup_min_age,signup_max_age,signup_min_password,signup_max_password,signup_min_username,signup_max_username,paid_memberships,numeric_delimiter,paypal_email,paypal_test,paypal_test_email,signup_username_availability,signup_password_meter,signup_captcha,signup_domain_restriction,list_email_domains,list_reserved_users,signup_terms,username_format,username_format_dott,username_format_dash,username_format_underscore,discount_codes,account_email_verification,account_approval,notify_welcome,paypal_payments,approve_friends,backend_notification_payment,backend_notification_signup,backend_email,backend_username,recaptcha_site_key,recaptcha_secret_key,fb_app_id,fb_app_secret,fb_auth,gp_app_id,gp_app_secret,gp_auth');
|
||||
$pack_class = ($cfg["paid_memberships"] == 1 and $cfg["global_signup"] == 1 and $error_message == '') ? include_once 'f_core/f_classes/class.payment.php' : null;
|
||||
$logged_in = (($_GET["do"] == 'continue' or $_GET["do"] == 'process') and intval($_SESSION["USER_ID"]) > 0 and intval($_SESSION["renew_id"]) == intval($_SESSION["USER_ID"])) ? null : VLogin::isLoggedIn();
|
||||
$error_message = ($cfg["signup_ip_access"] == 1 and !VIPaccess::checkIPlist($cfg["list_ip_signup"])) ? $smarty->assign('do_disable', 'yes') : null;
|
||||
$memberships = ($cfg["paid_memberships"] == 1 and $cfg["global_signup"] == 1 and $error_message == '') ? VPayment::getPackTypes() : null;
|
||||
//form sessions will be reset, unless we're dealing with the second form
|
||||
$formSessionReset = (intval($_POST["signup_submit_right"] != 1)) ? VSignup::formSessionReset() : null;
|
||||
|
||||
if (intval($_POST["frontend_global_submit"] == 1) and $cfg["global_signup"] == 1) {
|
||||
//left form has been submitted
|
||||
$form_fields = VArraySection::getArray('signup');
|
||||
$allowedFields = $form_fields[1];
|
||||
$requiredFields = $allowedFields;
|
||||
|
||||
$error_message = VSignup::processForm($allowedFields, $requiredFields);
|
||||
$notice_message = ($error_message == '' and VSignup::formSessionInit()) ? $language["notif.notice.signup.step1"] : null;
|
||||
$notice_message = ($error_message == '' and VSignup::processAccount()) ? $language["notif.notice.signup.success"] . $cfg["website_shortname"] . ($cfg["account_approval"] == 1 ? '. ' . $language["notif.notice.signup.approve"] : null) : null;
|
||||
$user_info = ($cfg["paid_memberships"] == 1 and $notice_message != '') ? VPayment::getUserPack() : VUserinfo::getUserEmail();
|
||||
}
|
||||
if (($cfg['fb_auth'] == 1 or $cfg['gp_auth'] == 1) and $_POST and isset($_GET["do"]) and $_GET["do"] == 'auth_register') {
|
||||
// $auth = VSignup::auth_register_ajax();
|
||||
}
|
||||
|
||||
//free account update
|
||||
$notice_message = ($cfg["global_signup"] == 1 and $cfg["paid_memberships"] == 1 and $_GET["p"] != '' and $_GET["u"] != '' and $_POST["signup_finalize"] == 1) ? VPayment::updateFreeEntry() : $notice_message;
|
||||
//payment processing
|
||||
$payment_prepare = ($cfg["global_signup"] == 1 and $cfg["paid_memberships"] == 1 and $_GET["p"] != '' and $_GET["u"] != '') ? VPayment::preparePayment() : null;
|
||||
$payment_confirm = ($cfg["global_signup"] == 1 and $cfg["paid_memberships"] == 1 and $_GET["do"] == 'continue') ? VPayment::continuePayment() : null;
|
||||
$payment_process = ($cfg["global_signup"] == 1 and $cfg["paid_memberships"] == 1 and ($_GET["do"] == 'process' or $_GET["do"] == 'ipn' or $_GET["do"] == 'success' or $_GET["do"] == 'cancel')) ? VPayment::doPayment($_GET["do"]) : null;
|
||||
//username avail.
|
||||
$username_check = (($cfg["signup_username_availability"] == 1 and $cfg["global_signup"] == 1) and ($_GET["do"] == 'ucheck' and strlen($_POST["frontend_signin_username"]) > 0)) ? VUserinfo::usernameAvailability($_POST["frontend_signin_username"]) : null;
|
||||
//fb register
|
||||
$fb_register = ($cfg['fb_auth'] == 1 and !isset($_GET["do"])) ? $smarty->assign('fb_register', VSignup::fb_register_button()) : null;
|
||||
//gp register
|
||||
$gp_register = ($cfg['gp_auth'] == 1 and !isset($_GET["do"])) ? $smarty->assign('gp_register', VSignup::gp_register_button()) : null;
|
||||
//page
|
||||
$display_page = !isset($_GET["do"]) ? $class_smarty->displayPage('frontend', 'tpl_signup', $error_message, $notice_message) : null;
|
||||
35
f_modules/m_frontend/m_auth/verify.php
Normal file
35
f_modules/m_frontend/m_auth/verify.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.signin');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.notifications');
|
||||
|
||||
$error_message = null;
|
||||
$notice_message = null;
|
||||
$cfg = $class_database->getConfigurations('account_email_verification');
|
||||
|
||||
if ($error_message == '' and isset($_GET["sid"]) and isset($_GET["uid"])) {
|
||||
$error_message = VRecovery::validCheck('frontend', 'verification');
|
||||
$notice_message = ($error_message == '' and VSignup::verifyAccount()) ? $language["notif.success.verified"] : null;
|
||||
} elseif ($cfg["account_email_verification"] == 0) {
|
||||
$error_message = 'tpl_error_max';
|
||||
}
|
||||
|
||||
$class_smarty->displayPage('frontend', 'tpl_verify', $error_message, $notice_message);
|
||||
31
f_modules/m_frontend/m_cron/admin_dashboard.php
Normal file
31
f_modules/m_frontend/m_cron/admin_dashboard.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
if (empty($_SERVER["argv"][1])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$dash = new VbeDashboard();
|
||||
|
||||
$dash->updateDashboardStats();
|
||||
27
f_modules/m_frontend/m_cron/admin_notifications.php
Normal file
27
f_modules/m_frontend/m_cron/admin_notifications.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$dash = new VbeDashboard();
|
||||
|
||||
$dash->updateNotifications();
|
||||
27
f_modules/m_frontend/m_cron/cache_clear.php
Normal file
27
f_modules/m_frontend/m_cron/cache_clear.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once 'f_core/f_classes/class.conversion.php';
|
||||
|
||||
$conv = new VQue();
|
||||
$conv->cache_clear();
|
||||
14
f_modules/m_frontend/m_cron/chat-server/cfg.php
Normal file
14
f_modules/m_frontend/m_cron/chat-server/cfg.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
/* database */
|
||||
$dbhost = getenv('DB_HOST') ?: 'db';
|
||||
$dbname = getenv('DB_NAME') ?: 'easystream';
|
||||
$dbuser = getenv('DB_USER') ?: 'easystream';
|
||||
$dbpass = getenv('DB_PASS') ?: 'easystream';
|
||||
/* main url */
|
||||
$base = getenv('CRON_BASE_URL') ?: 'http://localhost:8080';
|
||||
/* cron salt key */
|
||||
$ssk = getenv('CRON_SSK') ?: 'CHANGE_ME_IN_BACKEND';
|
||||
139
f_modules/m_frontend/m_cron/chat-server/chat.php
Normal file
139
f_modules/m_frontend/m_cron/chat-server/chat.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'filter.php';
|
||||
|
||||
$class_filter = new VFilter;
|
||||
|
||||
$host = array('127.0.0.1');
|
||||
|
||||
$_POST = $HTTP_RAW_POST_DATA = file_get_contents('php://input');
|
||||
//$_POST = $HTTP_RAW_POST_DATA;
|
||||
$post = json_decode($_POST);
|
||||
|
||||
if ($_POST and in_array($_SERVER["REMOTE_ADDR"], $host)) {
|
||||
require 'cfg.php';
|
||||
|
||||
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
|
||||
|
||||
if (!$conn) {
|
||||
die('Could not connect: ' . mysqli_error());
|
||||
}
|
||||
echo "Connected successfully\n";
|
||||
|
||||
$salt = $cfg["live_chat_salt"];
|
||||
$p_chatid = $class_filter->clr_str($post->a);
|
||||
$p_fkey = $class_filter->clr_str($post->b);
|
||||
$p_nick = $class_filter->clr_str($post->c);
|
||||
$p_dnick = $class_filter->clr_str($post->cd);
|
||||
$p_ip = $class_filter->clr_str($post->d);
|
||||
$p_chid = $class_filter->clr_str($post->e);
|
||||
$p_uid = $class_filter->clr_str($post->f);
|
||||
$p_cua = $class_filter->clr_str($post->g);
|
||||
$p_own = $class_filter->clr_str($post->h);
|
||||
$p_ukey = $class_filter->clr_str($post->i);
|
||||
$p_badge = $class_filter->clr_str($post->j);
|
||||
$p_live = (int) $post->k;
|
||||
$p_first = (int) $post->first;
|
||||
$p_inc = (int) $post->inc;
|
||||
$cip = $p_ip;
|
||||
$p_fp = $p_cua;
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livechat` WHERE `channel_id`='%s' AND `chat_id`='%s' AND `stream_id`='%s' LIMIT 1;", $p_chid, $p_chatid, $p_fkey);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
$v = $r->fetch_assoc();
|
||||
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livechat` (`first`, `chat_id`, `channel_id`, `channel_owner`, `usr_id`, `usr_key`, `stream_id`, `chat_user`, `chat_display`, `is_live`, `chat_ip`, `chat_fp`, `chat_time`, `badge`, `logged_in`, `usr_profileinc`) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');",
|
||||
$p_first, $p_chatid, $p_chid, $p_own, $p_uid, $p_ukey, $p_fkey, $p_nick, $p_dnick, $p_live, $p_ip, $p_fp, date("Y-m-d H:i:s"), $p_badge, (substr($p_nick, 0, 5) === "Guest" ? 0 : 1), $p_inc);
|
||||
$r = mysqli_query($conn, $q);
|
||||
} else {
|
||||
$q = sprintf("UPDATE `db_livechat` SET `is_live`='%s', `chat_display`='%s', `usr_profileinc`='%s', `first`='%s', `chat_ip`='%s', `chat_fp`='%s' WHERE `db_id`='%s' LIMIT 1;", $p_live, $p_dnick, $p_inc, $p_first, $p_ip, $p_fp, $v["db_id"]);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livemods` WHERE `channel_id`='%s' LIMIT 1;", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livemods` (`channel_id`, `mod_list`) VALUES ('%s', '[]');", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livevips` WHERE `channel_id`='%s' LIMIT 1;", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livevips` (`channel_id`, `vip_list`) VALUES ('%s', '[]');", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livebans` WHERE `channel_id`='%s' LIMIT 1;", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livebans` (`channel_id`, `ban_list`) VALUES ('%s', '[]');", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livefollows` WHERE `channel_id`='%s' LIMIT 1;", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livefollows` (`channel_id`, `follow_list`) VALUES ('%s', '[]');", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livesubs` WHERE `channel_id`='%s' LIMIT 1;", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livesubs` (`channel_id`, `sub_list`) VALUES ('%s', '[]');", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_livesettings` WHERE `channel_id`='%s' LIMIT 1;", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livesettings` (`channel_id`) VALUES ('%s');", $p_chid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `db_id` FROM `db_liveignore` WHERE `usr_id`='%s' LIMIT 1;", $p_uid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_liveignore` (`usr_id`, `ignore_list`) VALUES ('%s', '[]');", $p_uid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
$q = sprintf("SELECT `color_class`, `color_code`, `timestamps`, `modicons` FROM `db_livecolors` WHERE `usr_id`='%s' LIMIT 1", $p_uid);
|
||||
$r = mysqli_query($conn, $q);
|
||||
$rn = $r->num_rows;
|
||||
if ($rn == 0) {
|
||||
$q = sprintf("INSERT INTO `db_livecolors` (`usr_id`, `color_class`, `modicons`, `timestamps`) VALUES ('%s', '%s', '0', '0');", $p_uid, 'c' . rand(1, 15));
|
||||
$r = mysqli_query($conn, $q);
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
}
|
||||
44
f_modules/m_frontend/m_cron/chat-server/clean_chat.php
Normal file
44
f_modules/m_frontend/m_cron/chat-server/clean_chat.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
|
||||
|
||||
if (!$conn) {
|
||||
die('Could not connect: ' . mysqli_error());
|
||||
}
|
||||
echo "Connected successfully\n";
|
||||
|
||||
$sql = sprintf("DELETE FROM `db_livenotifications` WHERE `displayed`='1';");
|
||||
|
||||
if (mysqli_query($conn, $sql)) {
|
||||
echo "db_livenotifications updated successfully\n";
|
||||
} else {
|
||||
echo "Error updating table db_livenotifications: " . mysqli_error($conn) . "\n";
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM `db_livechat` WHERE `chat_user` LIKE 'Guest%';";
|
||||
|
||||
if (mysqli_query($conn, $sql)) {
|
||||
echo "db_livechat records updated successfully\n";
|
||||
} else {
|
||||
echo "Error updating db_livechat: " . mysqli_error($conn) . "\n";
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
341
f_modules/m_frontend/m_cron/chat-server/filter.php
Normal file
341
f_modules/m_frontend/m_cron/chat-server/filter.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
/**
|
||||
* Sanitize HTML body content
|
||||
* Remove dangerous tags and attributes that can lead to security issues like
|
||||
* XSS or HTTP response splitting
|
||||
*/
|
||||
class VFilter
|
||||
{
|
||||
// Private fields
|
||||
public $_encoding;
|
||||
public $_allowedTags;
|
||||
public $_allowJavascriptEvents;
|
||||
public $_allowJavascriptInUrls;
|
||||
public $_allowObjects;
|
||||
public $_allowScript;
|
||||
public $_allowStyle;
|
||||
public $_additionalTags;
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function HTML_Sanitizer()
|
||||
{
|
||||
$this->resetAll();
|
||||
}
|
||||
/**
|
||||
* (re)set all options to default value
|
||||
*/
|
||||
public function resetAll()
|
||||
{
|
||||
$this->_encoding = 'UTF-8';
|
||||
$this->_allowDOMEvents = false;
|
||||
$this->_allowJavascriptInUrls = false;
|
||||
$this->_allowStyle = false;
|
||||
$this->_allowScript = false;
|
||||
$this->_allowObjects = false;
|
||||
$this->_allowStyle = false;
|
||||
|
||||
$this->_allowedTags = '<a><br><b><h1><h2><h3><h4><h5><h6>'
|
||||
. '<img><li><ol><p><strong><table><tr><td><th><u><ul><thead>'
|
||||
. '<tbody><tfoot><em><dd><dt><dl><span><div><del><add><i><hr>'
|
||||
. '<pre><br><blockquote><address><code><caption><abbr><acronym>'
|
||||
. '<cite><dfn><q><ins><sup><sub><kbd><samp><var><tt><small><big>'
|
||||
;
|
||||
$this->_additionalTags = '';
|
||||
}
|
||||
/**
|
||||
* Add additional tags to allowed tags
|
||||
* @param string
|
||||
* @access public
|
||||
*/
|
||||
public function addAdditionalTags($tags)
|
||||
{$this->_additionalTags .= $tags;}
|
||||
/**
|
||||
* Allow object, embed, applet and param tags in html
|
||||
* @access public
|
||||
*/
|
||||
public function allowObjects()
|
||||
{$this->_allowObjects = true;}
|
||||
/**
|
||||
* Allow DOM event on DOM elements
|
||||
* @access public
|
||||
*/
|
||||
public function allowDOMEvents()
|
||||
{$this->_allowDOMEvents = true;}
|
||||
/**
|
||||
* Allow script tags
|
||||
* @access public
|
||||
*/
|
||||
public function allowScript()
|
||||
{$this->_allowScript = true;}
|
||||
/**
|
||||
* Allow the use of javascript: in urls
|
||||
* @access public
|
||||
*/
|
||||
public function allowJavascriptInUrls()
|
||||
{$this->_allowJavascriptInUrls = true;}
|
||||
/**
|
||||
* Allow style tags and attributes
|
||||
* @access public
|
||||
*/
|
||||
public function allowStyle()
|
||||
{$this->_allowStyle = true;}
|
||||
/**
|
||||
* Helper to allow all javascript related tags and attributes
|
||||
* @access public
|
||||
*/
|
||||
public function allowAllJavascript()
|
||||
{
|
||||
$this->allowDOMEvents();
|
||||
$this->allowScript();
|
||||
$this->allowJavascriptInUrls();
|
||||
}
|
||||
/**
|
||||
* Allow all tags and attributes
|
||||
* @access public
|
||||
*/
|
||||
public function allowAll()
|
||||
{
|
||||
$this->allowAllJavascript();
|
||||
$this->allowObjects();
|
||||
$this->allowStyle();
|
||||
}
|
||||
/**
|
||||
* Filter URLs to avoid HTTP response splitting attacks
|
||||
* @access public
|
||||
* @param string url
|
||||
* @return string filtered url
|
||||
*/
|
||||
public function filterHTTPResponseSplitting($url)
|
||||
{
|
||||
$dangerousCharactersPattern = '~(\r\n|\r|\n|%0a|%0d|%0D|%0A)~';
|
||||
return preg_replace($dangerousCharactersPattern, '', $url);
|
||||
}
|
||||
/**
|
||||
* Remove potential javascript in urls
|
||||
* @access public
|
||||
* @param string url
|
||||
* @return string filtered url
|
||||
*/
|
||||
public function removeJavascriptURL($str)
|
||||
{
|
||||
$HTML_Sanitizer_stripJavascriptURL = 'javascript:[^"]+';
|
||||
|
||||
$str = preg_replace("/$HTML_Sanitizer_stripJavascriptURL/i"
|
||||
, ''
|
||||
, $str);
|
||||
|
||||
return $str;
|
||||
}
|
||||
/**
|
||||
* Remove potential flaws in urls
|
||||
* @access private
|
||||
* @param string url
|
||||
* @return string filtered url
|
||||
*/
|
||||
public function sanitizeURL($url)
|
||||
{
|
||||
if (!$this->_allowJavascriptInUrls) {$url = $this->removeJavascriptURL($url);}
|
||||
$url = $this->filterHTTPResponseSplitting($url);
|
||||
|
||||
return $url;
|
||||
}
|
||||
/**
|
||||
* Callback for PCRE
|
||||
* @access private
|
||||
* @param matches array
|
||||
* @return string
|
||||
* @see sanitizeURL
|
||||
*/
|
||||
public function _sanitizeURLCallback($matches)
|
||||
{return 'href="' . $this->sanitizeURL($matches[1]) . '"';}
|
||||
/**
|
||||
* Remove potential flaws in href attributes
|
||||
* @access private
|
||||
* @param string html tag
|
||||
* @return string filtered html tag
|
||||
*/
|
||||
public function sanitizeHref($str)
|
||||
{
|
||||
$HTML_Sanitizer_URL = 'href="([^"]+)"';
|
||||
|
||||
return preg_replace_callback("/$HTML_Sanitizer_URL/i"
|
||||
, array(&$this, '_sanitizeURLCallback')
|
||||
, $str);
|
||||
}
|
||||
/**
|
||||
* Callback for PCRE
|
||||
* @access private
|
||||
* @param matches array
|
||||
* @return string
|
||||
* @see sanitizeURL
|
||||
*/
|
||||
public function _sanitizeSrcCallback($matches)
|
||||
{return 'src="' . $this->sanitizeURL($matches[1]) . '"';}
|
||||
/**
|
||||
* Remove potential flaws in href attributes
|
||||
* @access private
|
||||
* @param string html tag
|
||||
* @return string filtered html tag
|
||||
*/
|
||||
public function sanitizeSrc($str)
|
||||
{
|
||||
$HTML_Sanitizer_URL = 'src="([^"]+)"';
|
||||
|
||||
return preg_replace_callback("/$HTML_Sanitizer_URL/i"
|
||||
, array(&$this, '_sanitizeSrcCallback')
|
||||
, $str);
|
||||
}
|
||||
/**
|
||||
* Remove dangerous attributes from html tags
|
||||
* @access private
|
||||
* @param string html tag
|
||||
* @return string filtered html tag
|
||||
*/
|
||||
public function removeEvilAttributes($str)
|
||||
{
|
||||
if (!$this->_allowDOMEvents) {
|
||||
$str = preg_replace_callback('/<(.*?)>/i'
|
||||
, array(&$this, '_removeDOMEventsCallback')
|
||||
, $str);
|
||||
}
|
||||
if (!$this->_allowStyle) {
|
||||
$str = preg_replace_callback('/<(.*?)>/i'
|
||||
, array(&$this, '_removeStyleCallback')
|
||||
, $str);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
/**
|
||||
* Remove DOM events attributes from html tags
|
||||
* @access private
|
||||
* @param string html tag
|
||||
* @return string filtered html tag
|
||||
*/
|
||||
public function removeDOMEvents($str)
|
||||
{
|
||||
$str = preg_replace('/\s*=\s*/', '=', $str);
|
||||
|
||||
$HTML_Sanitizer_stripAttrib = '(onclick|ondblclick|onmousedown|'
|
||||
. 'onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|'
|
||||
. 'onkeyup|onfocus|onblur|onabort|onerror|onload)'
|
||||
;
|
||||
|
||||
$str = stripslashes(preg_replace("/$HTML_Sanitizer_stripAttrib/i"
|
||||
, 'forbidden'
|
||||
, $str));
|
||||
|
||||
return $str;
|
||||
}
|
||||
/**
|
||||
* Callback for PCRE
|
||||
* @access private
|
||||
* @param matches array
|
||||
* @return string
|
||||
* @see removeDOMEvents
|
||||
*/
|
||||
public function _removeDOMEventsCallback($matches)
|
||||
{return '<' . $this->removeDOMEvents($matches[1]) . '>';}
|
||||
/**
|
||||
* Remove style attributes from html tags
|
||||
* @access private
|
||||
* @param string html tag
|
||||
* @return string filtered html tag
|
||||
*/
|
||||
public function removeStyle($str)
|
||||
{
|
||||
$str = preg_replace('/\s*=\s*/', '=', $str);
|
||||
|
||||
$HTML_Sanitizer_stripAttrib = '(style)'
|
||||
;
|
||||
|
||||
$str = stripslashes(preg_replace("/$HTML_Sanitizer_stripAttrib/i"
|
||||
, 'forbidden'
|
||||
, $str));
|
||||
|
||||
return $str;
|
||||
}
|
||||
/**
|
||||
* Callback for PCRE
|
||||
* @access private
|
||||
* @param matches array
|
||||
* @return string
|
||||
* @see removeStyle
|
||||
*/
|
||||
public function _removeStyleCallback($matches)
|
||||
{return '<' . $this->removeStyle($matches[1]) . '>';}
|
||||
/**
|
||||
* Remove dangerous HTML tags
|
||||
* @access private
|
||||
* @param string html code
|
||||
* @return string filtered url
|
||||
*/
|
||||
public function removeEvilTags($str)
|
||||
{
|
||||
$allowedTags = $this->_allowedTags;
|
||||
|
||||
if ($this->_allowScript) {$allowedTags .= '<script>';}
|
||||
if ($this->_allowStyle) {$allowedTags .= '<style>';}
|
||||
if ($this->_allowObjects) {$allowedTags .= '<object><embed><applet><param>';}
|
||||
|
||||
$allowedTags .= $this->_additionalTags;
|
||||
$str = strip_tags($str, $allowedTags);
|
||||
|
||||
return $str;
|
||||
}
|
||||
public function removeSQLTags($str)
|
||||
{
|
||||
$str = str_ireplace(array('CONCAT', 'ELT(', 'INFORMATION_SCHEMA'), array('', '', ''), $str);
|
||||
|
||||
return $str;
|
||||
}
|
||||
/**
|
||||
* Sanitize HTML
|
||||
* remove dangerous tags and attributes
|
||||
* clean urls
|
||||
* @access public
|
||||
* @param string html code
|
||||
* @return string sanitized html code
|
||||
*/
|
||||
public function sanitize($html)
|
||||
{
|
||||
$html = $this->removeEvilTags($html);
|
||||
$html = $this->removeEvilAttributes($html);
|
||||
$html = $this->sanitizeHref($html);
|
||||
$html = $this->sanitizeSrc($html);
|
||||
$html = $this->removeSQLTags($html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function clr_str($str)
|
||||
{
|
||||
static $san = null;
|
||||
if (empty($san)) {$san = new VFilter;}
|
||||
|
||||
return htmlspecialchars($san->sanitize($str), ENT_QUOTES, $this->_encoding);
|
||||
}
|
||||
}
|
||||
|
||||
function html_sanitize($str)
|
||||
{
|
||||
static $san = null;
|
||||
if (empty($san)) {$san = new VFilter;}
|
||||
return $san->sanitize($str);
|
||||
}
|
||||
0
f_modules/m_frontend/m_cron/chat-server/index.html
Normal file
0
f_modules/m_frontend/m_cron/chat-server/index.html
Normal file
0
f_modules/m_frontend/m_cron/chat-server/index.php
Normal file
0
f_modules/m_frontend/m_cron/chat-server/index.php
Normal file
99
f_modules/m_frontend/m_cron/chat-server/notify.php
Normal file
99
f_modules/m_frontend/m_cron/chat-server/notify.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'filter.php';
|
||||
|
||||
$class_filter = new VFilter;
|
||||
|
||||
//include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
|
||||
$host = array('127.0.0.1');
|
||||
|
||||
$_POST = $HTTP_RAW_POST_DATA = file_get_contents('php://input');
|
||||
|
||||
$post = json_decode($_POST);
|
||||
|
||||
if ($_POST and in_array($_SERVER["REMOTE_ADDR"], $host)) {
|
||||
require 'cfg.php';
|
||||
|
||||
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
|
||||
|
||||
if (!$conn) {
|
||||
die('Could not connect: ' . mysqli_error($conn));
|
||||
}
|
||||
echo "Connected successfully\n";
|
||||
|
||||
$cid = $class_filter->clr_str($post->a);
|
||||
$sid = $class_filter->clr_str($post->e);
|
||||
$type = is_array($post->b) ? $post->b : $class_filter->clr_str($post->b);
|
||||
$user = $class_filter->clr_str($post->c);
|
||||
$user2 = $class_filter->clr_str($post->d);
|
||||
$pk = $class_filter->clr_str($post->g);
|
||||
|
||||
switch ($type) {
|
||||
case "follow":
|
||||
case "unfollow":
|
||||
$text = $type == 'follow' ? $user . ' is now following' : $user . ' has unfollowed';
|
||||
$sql = sprintf("SELECT `db_id`, `chat_user`, `channel_id`, `channel_owner`, `usr_id`, `logged_in` FROM `db_livechat` WHERE `stream_id`='%s' AND `chat_id`='%s' ORDER BY `db_id` DESC LIMIT 1;", $sid, $cid);
|
||||
$r = mysqli_query($conn, $sql);
|
||||
$rn = $r->num_rows;
|
||||
$rv = $r->fetch_assoc();
|
||||
|
||||
if ($rn > 0) {
|
||||
$ch_id = $rv["channel_id"];
|
||||
$sql = sprintf("SELECT `db_id` FROM `db_livenotifications` WHERE `type`='follow' AND `channel_id`='%s' AND `text` LIKE '%s' LIMIT 1;", $ch_id, $user . '%');
|
||||
$rr = mysqli_query($conn, $sql);
|
||||
|
||||
if ($rr->num_rows == 0 and $type == 'follow') {
|
||||
$q = sprintf("INSERT INTO `db_livenotifications` (`type`, `channel_id`, `text`, `displayed`) VALUES ('%s', '%s', '%s', '0');", $type, $ch_id, $text);
|
||||
mysqli_query($conn, $q);
|
||||
}
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (is_array($type) and ($type[0] == 'subscribe' or $type[0] == 'unsubscribe')) {
|
||||
$text = $type[0] == 'subscribe' ? $user2 . ' has subscribed with a ' . $pk . ' subscription' : $user2 . ' has unsubscribed';
|
||||
$sql = sprintf("SELECT `db_id`, `chat_user`, `channel_id`, `channel_owner`, `usr_id`, `logged_in` FROM `db_livechat` WHERE `stream_id`='%s' AND `chat_id`='%s' ORDER BY `db_id` DESC LIMIT 1;", $sid, $cid);
|
||||
$r = mysqli_query($conn, $sql);
|
||||
$rn = $r->num_rows;
|
||||
$rv = $r->fetch_assoc();
|
||||
|
||||
if ($rn > 0) {
|
||||
$ch_id = $rv["channel_id"];
|
||||
|
||||
if ($type[0] == 'subscribe') {
|
||||
$q = sprintf("INSERT INTO `db_livenotifications` (`type`, `channel_id`, `text`, `displayed`) VALUES ('%s', '%s', '%s', '0');", $type[0], $ch_id, $text);
|
||||
mysqli_query($conn, $q);
|
||||
}
|
||||
}
|
||||
}
|
||||
mysqli_close($conn);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ($conn) {
|
||||
mysqli_close($conn);
|
||||
}
|
||||
102
f_modules/m_frontend/m_cron/chat-server/sync_subs.php
Normal file
102
f_modules/m_frontend/m_cron/chat-server/sync_subs.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$url = $base . '/syncsubs?s=';
|
||||
$date = date("Y-m-d");
|
||||
$tk = md5($date . $ssk);
|
||||
$url .= $tk;
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
$list = json_decode($data);
|
||||
|
||||
/* follows */
|
||||
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
|
||||
if (!$conn) {
|
||||
die('Could not connect: ' . mysqli_error());
|
||||
}
|
||||
|
||||
echo "Connected successfully\n";
|
||||
|
||||
if (is_object($list->followers)) {
|
||||
foreach ($list->followers as $usr_id => $users) {
|
||||
if ($usr_id > 0) {
|
||||
$sql = sprintf("SELECT `db_id` FROM `db_livefollows` WHERE `channel_id`='%s' LIMIT 1;", $usr_id);
|
||||
$r = mysqli_query($conn, $sql);
|
||||
$n = $r->num_rows;
|
||||
$v = $r->fetch_assoc();
|
||||
|
||||
if ($n > 0) {
|
||||
$sql = sprintf("UPDATE `db_livefollows` SET `follow_list`='%s' WHERE `db_id`='%s' LIMIT 1;", json_encode($users), $v["db_id"]);
|
||||
} else {
|
||||
$sql = sprintf("INSERT INTO `db_livefollows` (`channel_id`, `follow_list`) VALUES ('%s', '%s');", $usr_id, json_encode($users));
|
||||
}
|
||||
|
||||
if (mysqli_query($conn, $sql)) {
|
||||
echo "db_livefollows records updated successfully for channel_id $usr_id\n";
|
||||
} else {
|
||||
echo "Error updating record: " . mysqli_error($conn) . "\n";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
|
||||
/* subs */
|
||||
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
|
||||
if (!$conn) {
|
||||
die('Could not connect: ' . mysqli_error());
|
||||
}
|
||||
|
||||
echo "Connected successfully\n";
|
||||
|
||||
if (is_object($list->subscribers)) {
|
||||
foreach ($list->subscribers as $usr_id => $users) {
|
||||
if ($usr_id > 0) {
|
||||
$sql = sprintf("SELECT `db_id` FROM `db_livesubs` WHERE `channel_id`='%s' LIMIT 1;", $usr_id);
|
||||
$r = mysqli_query($conn, $sql);
|
||||
$n = $r->num_rows;
|
||||
$v = $r->fetch_assoc();
|
||||
|
||||
if ($n > 0) {
|
||||
$sql = sprintf("UPDATE `db_livesubs` SET `sub_list`='%s' WHERE `db_id`='%s' LIMIT 1;", json_encode($users), $v["db_id"]);
|
||||
} else {
|
||||
$sql = sprintf("INSERT INTO `db_livesubs` (`channel_id`, `sub_list`) VALUES ('%s', '%s');", $usr_id, json_encode($users));
|
||||
}
|
||||
|
||||
if (mysqli_query($conn, $sql)) {
|
||||
echo "db_livesubs records updated successfully for channel_id $usr_id\n";
|
||||
} else {
|
||||
echo "Error updating record: " . mysqli_error($conn) . "\n";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
32
f_modules/m_frontend/m_cron/clean_chat.php
Normal file
32
f_modules/m_frontend/m_cron/clean_chat.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$sql = "DELETE FROM `db_livechat` WHERE `chat_user` LIKE 'Guest%';";
|
||||
$rs = $db->execute($sql);
|
||||
|
||||
if ($db->Affected_Rows() > 0) {
|
||||
echo "db_livechat records updated successfully\n";
|
||||
} else {
|
||||
echo "db_livechat not updated\n";
|
||||
}
|
||||
32
f_modules/m_frontend/m_cron/clean_temps.php
Normal file
32
f_modules/m_frontend/m_cron/clean_temps.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$sql = sprintf("DELETE FROM `db_livetemps` WHERE DATE(`date`) < DATE(NOW() - INTERVAL 2 DAY);");
|
||||
$rs = $db->execute($sql);
|
||||
|
||||
if ($db->Affected_Rows() > 0) {
|
||||
echo "db_livetemps records updated successfully\n";
|
||||
} else {
|
||||
echo "db_livetemps not updated\n";
|
||||
}
|
||||
62
f_modules/m_frontend/m_cron/convert_queue.php
Normal file
62
f_modules/m_frontend/m_cron/convert_queue.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once 'f_core/f_classes/class.conversion.php';
|
||||
|
||||
$cfg[] = $class_database->getConfigurations('conversion_video_que,conversion_short_que,conversion_image_que,conversion_audio_que,conversion_document_que');
|
||||
$conv = new VQue();
|
||||
|
||||
if ($cfg["video_module"] == 1 and $cfg["conversion_video_que"] == 1 and $conv->load("video")) {
|
||||
if ($conv->check()) {
|
||||
$conv->startConversion();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["short_module"] == 1 and $cfg["conversion_short_que"] == 1 and $conv->load("short")) {
|
||||
if ($conv->check()) {
|
||||
$conv->startConversion();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["image_module"] == 1 and $cfg["conversion_image_que"] == 1 and $conv->load("image")) {
|
||||
if ($conv->check()) {
|
||||
$conv->startConversion();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["audio_module"] == 1 and $cfg["conversion_audio_que"] == 1 and $conv->load("audio")) {
|
||||
if ($conv->check()) {
|
||||
$conv->startConversion();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["document_module"] == 1 and $cfg["conversion_document_que"] == 1 and $conv->load("doc")) {
|
||||
if ($conv->check()) {
|
||||
$conv->startConversion();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
45
f_modules/m_frontend/m_cron/disk_usage.php
Normal file
45
f_modules/m_frontend/m_cron/disk_usage.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
|
||||
set_time_limit(0);
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$cfg[] = $class_database->getConfigurations('backend_email');
|
||||
|
||||
$bytes = disk_free_space("/");
|
||||
$si_prefix = array('B', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB');
|
||||
$base = 1024;
|
||||
$class = min((int) log($bytes, $base), count($si_prefix) - 1);
|
||||
|
||||
$data = sprintf('%1.2f', $bytes / pow($base, $class)) . ' ' . $si_prefix[$class];
|
||||
$free = sprintf('%1.2f', $bytes / pow($base, $class));
|
||||
|
||||
if ($si_prefix[$class] == 'GB') {
|
||||
if ($free < 5) {
|
||||
VNotify::queInit('disk_usage', array($cfg['backend_email']), $data);
|
||||
}
|
||||
}
|
||||
if ($si_prefix[$class] == 'MB') {
|
||||
if ($free < 900) {
|
||||
VNotify::queInit('disk_usage', array($cfg['backend_email']), $data);
|
||||
}
|
||||
}
|
||||
25
f_modules/m_frontend/m_cron/expired_subs.php
Normal file
25
f_modules/m_frontend/m_cron/expired_subs.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$act = VSubscriber::check_expired_subs();
|
||||
30
f_modules/m_frontend/m_cron/mailer.php
Normal file
30
f_modules/m_frontend/m_cron/mailer.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
|
||||
set_time_limit(0);
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$mail_type = $class_filter->clr_str($_SERVER["argv"][1]);
|
||||
$mail_key = $class_filter->clr_str($_SERVER["argv"][2]);
|
||||
|
||||
$do_notify = VNotify::Mailer($mail_type, $mail_key);
|
||||
66
f_modules/m_frontend/m_cron/notify_subscribers.php
Normal file
66
f_modules/m_frontend/m_cron/notify_subscribers.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
|
||||
set_time_limit(0);
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.global');
|
||||
include_once $class_language->setLanguageFile('frontend', 'language.email.notif');
|
||||
|
||||
$period = $class_filter->clr_str($_SERVER["argv"][1]);
|
||||
$db_period = $period == 'daily' ? 1 : ($period == 'weekly' ? 2 : 0);
|
||||
|
||||
if ($db_period > 0) {
|
||||
$sql = sprintf("SELECT `usr_id`, `usr_user`, `usr_email` FROM `db_accountuser` WHERE `usr_weekupdates`='%s' AND `usr_status`='1';", $db_period);
|
||||
$res = $db->execute($sql);
|
||||
|
||||
if ($res->fields["usr_id"]) {
|
||||
$_i = array();
|
||||
|
||||
while (!$res->EOF) {
|
||||
$n = 0;
|
||||
$sql = sprintf("SELECT `usr_id` FROM `db_subscribers` WHERE `sub_id`='%s';", $res->fields["usr_id"]);
|
||||
$r = $db->execute($sql);
|
||||
|
||||
if ($r->fields["usr_id"]) {
|
||||
while (!$r->EOF) {
|
||||
$_user1 = $res->fields["usr_id"];
|
||||
$_user2 = $r->fields["usr_id"];
|
||||
|
||||
$_i[$_user1][$n] = $_user2;
|
||||
|
||||
$r->MoveNext();
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
|
||||
$res->MoveNext();
|
||||
}
|
||||
|
||||
if (count($_i) > 0) {
|
||||
foreach ($_i as $u => $v) {
|
||||
$_mail = VUserinfo::getUserEmail($u);
|
||||
|
||||
VNotify::queInit('email_digest', array($_mail), array(VUserinfo::getUserName($u), $v, $db_period));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
503
f_modules/m_frontend/m_cron/sitemap.php
Normal file
503
f_modules/m_frontend/m_cron/sitemap.php
Normal file
@@ -0,0 +1,503 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
|
||||
set_time_limit(0);
|
||||
//ignore_user_abort(1);
|
||||
set_include_path($main_dir);
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
if (empty($_SERVER["argv"][1])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$sitemap_basename = 'basename';
|
||||
|
||||
$limit = null;
|
||||
$vidid = null;
|
||||
$p = null;
|
||||
$mp4 = null;
|
||||
$type = $class_filter->clr_str($_SERVER["argv"][1]);
|
||||
|
||||
if ($type != 'video' and $type != 'short' and $type != 'image') {
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['limit'])) {
|
||||
$limit = preg_replace("/[^0-9,]/", "", $_GET['limit']);
|
||||
if ($limit) {
|
||||
$limit = "LIMIT $limit";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['vid'])) {
|
||||
$vidid = preg_replace("/[^0-9]/", "", $_GET['vid']);
|
||||
if ($vidid) {
|
||||
$vidid = "AND vid_id > $vidid";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$skey = "last_" . $type . "_sitemap";
|
||||
$cfg[] = $class_database->getConfigurations($skey . ',stream_method,stream_server,stream_lighttpd_secure');
|
||||
$lvid = $cfg[$skey];
|
||||
//$lvid = 0;//reset, start from id0
|
||||
$vidid = "AND A.`db_id` > $lvid";
|
||||
|
||||
$limit = 'LIMIT 1000';
|
||||
|
||||
$sql = sprintf("SELECT
|
||||
A.`db_id`, A.`file_key`, A.`upload_date`, A.`file_duration`, A.`file_views`, A.`upload_server`, A.`thumb_server`, A.`embed_src`, A.`embed_key`, A.`file_hd`, A.`thumb_cache`,
|
||||
A.`file_title`, A.`file_description`, A.`file_tags`,
|
||||
D.`usr_key`
|
||||
FROM `db_%sfiles` A, `db_accountuser` D WHERE
|
||||
A.`usr_id`=D.`usr_id` AND
|
||||
A.`active`='1' AND A.`deleted`='0' AND A.`approved`='1'
|
||||
%s ORDER BY A.`db_id` ASC %s;", $type, $vidid, $limit);
|
||||
|
||||
$res = $db->execute($sql);
|
||||
|
||||
//exit;
|
||||
|
||||
$SitemapNode = new VSitemap($cfg['sitemap_dir'] . '/sm_' . $type, $sitemap_basename, $type);
|
||||
|
||||
if ($res->fields["file_key"]) {
|
||||
while (!$res->EOF) {
|
||||
$db_id = $res->fields["db_id"];
|
||||
$vid_id = $res->fields["file_key"];
|
||||
$usr_id = $res->fields["usr_key"];
|
||||
$tmb_srv = $res->fields["thumb_server"];
|
||||
$vid_srv = $res->fields["upload_server"];
|
||||
$db_title = $res->fields["file_title"];
|
||||
$_fsrc = $res->fields["embed_src"];
|
||||
$_furl = $res->fields["embed_key"];
|
||||
$hd = $res->fields["file_hd"];
|
||||
$thumb_cache = $res->fields["thumb_cache"];
|
||||
$thumb_cache = $thumb_cache > 1 ? $thumb_cache : null;
|
||||
|
||||
if ($hd == 0) {
|
||||
$hd = '';
|
||||
}
|
||||
|
||||
$title = html_convert_entities(htmlspecialchars($db_title));
|
||||
$description = html_convert_entities(htmlspecialchars($res->fields["file_description"]));
|
||||
$screenshot = VGenerate::thumbSigned($type, $vid_id, array($usr_id, $thumb_cache), (3600 * 24 * 7), 0);
|
||||
$duration = $res->fields["file_duration"];
|
||||
$view_count = $res->fields["file_views"];
|
||||
$tags = explode(" ", $res->fields["file_tags"]);
|
||||
$date = date(DATE_ATOM, strtotime($res->fields["upload_date"]));
|
||||
|
||||
$url = VGenerate::fileURL($type, $vid_id, 'upload');
|
||||
$link = $cfg["main_url"] . '/' . VGenerate::fileHref($type[0], $vid_id, $db_title);
|
||||
|
||||
$_src = VPlayers::fileSources($type, $usr_id, $vid_id);
|
||||
|
||||
$sql = sprintf("SELECT
|
||||
A.`server_type`, A.`lighttpd_url`, A.`lighttpd_secdownload`, A.`lighttpd_prefix`, A.`lighttpd_key`, A.`cf_enabled`, A.`cf_dist_type`,
|
||||
A.`cf_signed_url`, A.`cf_signed_expire`, A.`cf_key_pair`, A.`cf_key_file`, A.`s3_bucketname`, A.`s3_accesskey`, A.`s3_secretkey`,
|
||||
B.`upload_server`
|
||||
FROM
|
||||
`db_servers` A, `db_%sfiles` B
|
||||
WHERE
|
||||
B.`file_key`='%s' AND
|
||||
B.`upload_server` > '0' AND
|
||||
A.`server_id`=B.`upload_server` LIMIT 1;", $type, $vid_id);
|
||||
$srv = $db->execute($sql);
|
||||
|
||||
$cf_signed_url = $srv->fields["cf_signed_url"];
|
||||
$cf_signed_expire = 3600 * 24 * 2; //$srv->fields["cf_signed_expire"];
|
||||
$cf_key_pair = $srv->fields["cf_key_pair"];
|
||||
$cf_key_file = $srv->fields["cf_key_file"];
|
||||
$aws_access_key_id = $srv->fields["s3_accesskey"];
|
||||
$aws_secret_key = $srv->fields["s3_secretkey"];
|
||||
$aws_bucket = $srv->fields["s3_bucketname"];
|
||||
|
||||
if (count($_src) > 0) {
|
||||
$_file = array();
|
||||
foreach ($_src as $k => $v) {
|
||||
if (($srv->fields["lighttpd_url"] != '' and $srv->fields["lighttpd_secdownload"] == 1) or ($cfg["stream_method"] == 2 and $cfg["stream_server"] == 'lighttpd' and $cfg["stream_lighttpd_secure"] == 1)) {
|
||||
$l0 = explode("/", $v[0]);
|
||||
$loc0 = $cfg["media_files_dir"] . '/' . $l0[6] . '/' . $l0[7] . '/' . $l0[8];
|
||||
$l1 = explode("/", $v[1]);
|
||||
$loc1 = $cfg["media_files_dir"] . '/' . $l1[6] . '/' . $l1[7] . '/' . $l1[8];
|
||||
$l2 = explode("/", $v[2]);
|
||||
$loc2 = $cfg["media_files_dir"] . '/' . $l2[6] . '/' . $l2[7] . '/' . $l2[8];
|
||||
} elseif ($cfg["stream_method"] == 2 and $cfg["stream_server"] == 'lighttpd' and $cfg["stream_lighttpd_secure"] == 0) {
|
||||
$loc0 = str_replace($cfg["stream_lighttpd_url"], $cfg["media_files_dir"], $v[0]);
|
||||
$loc1 = str_replace($cfg["stream_lighttpd_url"], $cfg["media_files_dir"], $v[1]);
|
||||
$loc2 = str_replace($cfg["stream_lighttpd_url"], $cfg["media_files_dir"], $v[2]);
|
||||
} else {
|
||||
$loc0 = str_replace($url, $cfg["media_files_dir"], $v[0]);
|
||||
$loc1 = str_replace($url, $cfg["media_files_dir"], $v[1]);
|
||||
$loc2 = str_replace($url, $cfg["media_files_dir"], $v[2]);
|
||||
|
||||
if ($srv->fields["server_type"] == 's3' and $srv->fields["cf_enabled"] == 1 and $cf_signed_url == 1) {
|
||||
if ($srv->fields["cf_dist_type"] == 'r') {
|
||||
$v[0] = strstr($v[0], $videos->fields["usr_key"]);
|
||||
$v[1] = strstr($v[1], $videos->fields["usr_key"]);
|
||||
$v[2] = strstr($v[2], $videos->fields["usr_key"]);
|
||||
|
||||
$v[0] = VbeServers::getS3SignedURL($aws_access_key_id, $aws_secret_key, $v[0], $aws_bucket, $cf_signed_expire);
|
||||
$v[1] = VbeServers::getS3SignedURL($aws_access_key_id, $aws_secret_key, $v[1], $aws_bucket, $cf_signed_expire);
|
||||
$v[2] = VbeServers::getS3SignedURL($aws_access_key_id, $aws_secret_key, $v[2], $aws_bucket, $cf_signed_expire);
|
||||
} else {
|
||||
$v[0] = VbeServers::getSignedURL($v[0], $cf_signed_expire, $cf_key_pair, $cf_key_file, 0, 0);
|
||||
$v[1] = VbeServers::getSignedURL($v[1], $cf_signed_expire, $cf_key_pair, $cf_key_file, 0, 0);
|
||||
$v[2] = VbeServers::getSignedURL($v[2], $cf_signed_expire, $cf_key_pair, $cf_key_file, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hd == '') {
|
||||
if (file_exists($loc0)) {
|
||||
$_file[] = $v[0];
|
||||
} else {
|
||||
if (file_exists($loc1)) {
|
||||
$_file[] = $v[1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (file_exists($loc0)) {
|
||||
$_file[] = $v[0];
|
||||
} else {
|
||||
if (file_exists($loc1)) {
|
||||
$_file[] = $v[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($_fsrc != 'local') {
|
||||
$info = array();
|
||||
$_p = VPlayers::playerInit('view');
|
||||
$width = $_p[1];
|
||||
$height = $_p[2];
|
||||
$info["key"] = $vid_id;
|
||||
|
||||
switch ($_fsrc) {
|
||||
case "youtube":$_url = 'https://www.youtube.com/embed/' . $_furl;
|
||||
break;
|
||||
case "vimeo":$_url = 'https://player.vimeo.com/video/' . $_furl;
|
||||
break;
|
||||
case "dailymotion":$_url = 'https://www.dailymotion.com/embed/video/' . $_furl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$config = array(
|
||||
'type' => 'url',
|
||||
'params' => array(
|
||||
'loc' => $link,
|
||||
'video' => array(
|
||||
'title' => $title,
|
||||
'thumbnail_loc' => $screenshot,
|
||||
'description' => str_replace(array("\r", "\n"), array("", ""), $description),
|
||||
($_fsrc != 'local' ? 'player_loc' : 'content_loc') => ($_fsrc != 'local' ? $_url : ($hd == '' ? $_file[0] : ($_file[2] != '' ? $_file[2] : $_file[1]))),
|
||||
'publication_date' => $date,
|
||||
'duration' => $duration,
|
||||
'view_count' => $view_count,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($tags as $i => $tag) {
|
||||
$final_tag = trim(html_convert_entities(htmlspecialchars($tag)));
|
||||
|
||||
$config['params']['video']['tag' . $i] = str_replace(array("\r", "\n"), array("", ""), $final_tag);
|
||||
}
|
||||
|
||||
$SitemapNode->add_node($config);
|
||||
|
||||
$db->execute(sprintf("UPDATE `db_settings` SET `cfg_data`='%s' WHERE `cfg_name`='%s' LIMIT 1;", $db_id, $skey));
|
||||
|
||||
$res->MoveNext();
|
||||
} //endwhile
|
||||
} //endif
|
||||
|
||||
function html_convert_entities($string)
|
||||
{
|
||||
return preg_replace_callback('/&([a-zA-Z][a-zA-Z0-9]+);/',
|
||||
'convert_entity', $string);
|
||||
}
|
||||
|
||||
/* Swap HTML named entity with its numeric equivalent. If the entity
|
||||
* isn't in the lookup table, this function returns a blank, which
|
||||
* destroys the character in the output - this is probably the
|
||||
* desired behaviour when producing XML. */
|
||||
function convert_entity($matches)
|
||||
{
|
||||
static $table = array('quot' => '"',
|
||||
'amp' => '&',
|
||||
'lt' => '<',
|
||||
'gt' => '>',
|
||||
'OElig' => 'Œ',
|
||||
'oelig' => 'œ',
|
||||
'Scaron' => 'Š',
|
||||
'scaron' => 'š',
|
||||
'Yuml' => 'Ÿ',
|
||||
'circ' => 'ˆ',
|
||||
'tilde' => '˜',
|
||||
'ensp' => ' ',
|
||||
'emsp' => ' ',
|
||||
'thinsp' => ' ',
|
||||
'zwnj' => '‌',
|
||||
'zwj' => '‍',
|
||||
'lrm' => '‎',
|
||||
'rlm' => '‏',
|
||||
'ndash' => '–',
|
||||
'mdash' => '—',
|
||||
'lsquo' => '‘',
|
||||
'rsquo' => '’',
|
||||
'sbquo' => '‚',
|
||||
'ldquo' => '“',
|
||||
'rdquo' => '”',
|
||||
'bdquo' => '„',
|
||||
'dagger' => '†',
|
||||
'Dagger' => '‡',
|
||||
'permil' => '‰',
|
||||
'lsaquo' => '‹',
|
||||
'rsaquo' => '›',
|
||||
'euro' => '€',
|
||||
'fnof' => 'ƒ',
|
||||
'Alpha' => 'Α',
|
||||
'Beta' => 'Β',
|
||||
'Gamma' => 'Γ',
|
||||
'Delta' => 'Δ',
|
||||
'Epsilon' => 'Ε',
|
||||
'Zeta' => 'Ζ',
|
||||
'Eta' => 'Η',
|
||||
'Theta' => 'Θ',
|
||||
'Iota' => 'Ι',
|
||||
'Kappa' => 'Κ',
|
||||
'Lambda' => 'Λ',
|
||||
'Mu' => 'Μ',
|
||||
'Nu' => 'Ν',
|
||||
'Xi' => 'Ξ',
|
||||
'Omicron' => 'Ο',
|
||||
'Pi' => 'Π',
|
||||
'Rho' => 'Ρ',
|
||||
'Sigma' => 'Σ',
|
||||
'Tau' => 'Τ',
|
||||
'Upsilon' => 'Υ',
|
||||
'Phi' => 'Φ',
|
||||
'Chi' => 'Χ',
|
||||
'Psi' => 'Ψ',
|
||||
'Omega' => 'Ω',
|
||||
'alpha' => 'α',
|
||||
'beta' => 'β',
|
||||
'gamma' => 'γ',
|
||||
'delta' => 'δ',
|
||||
'epsilon' => 'ε',
|
||||
'zeta' => 'ζ',
|
||||
'eta' => 'η',
|
||||
'theta' => 'θ',
|
||||
'iota' => 'ι',
|
||||
'kappa' => 'κ',
|
||||
'lambda' => 'λ',
|
||||
'mu' => 'μ',
|
||||
'nu' => 'ν',
|
||||
'xi' => 'ξ',
|
||||
'omicron' => 'ο',
|
||||
'pi' => 'π',
|
||||
'rho' => 'ρ',
|
||||
'sigmaf' => 'ς',
|
||||
'sigma' => 'σ',
|
||||
'tau' => 'τ',
|
||||
'upsilon' => 'υ',
|
||||
'phi' => 'φ',
|
||||
'chi' => 'χ',
|
||||
'psi' => 'ψ',
|
||||
'omega' => 'ω',
|
||||
'thetasym' => 'ϑ',
|
||||
'upsih' => 'ϒ',
|
||||
'piv' => 'ϖ',
|
||||
'bull' => '•',
|
||||
'hellip' => '…',
|
||||
'prime' => '′',
|
||||
'Prime' => '″',
|
||||
'oline' => '‾',
|
||||
'frasl' => '⁄',
|
||||
'weierp' => '℘',
|
||||
'image' => 'ℑ',
|
||||
'real' => 'ℜ',
|
||||
'trade' => '™',
|
||||
'alefsym' => 'ℵ',
|
||||
'larr' => '←',
|
||||
'uarr' => '↑',
|
||||
'rarr' => '→',
|
||||
'darr' => '↓',
|
||||
'harr' => '↔',
|
||||
'crarr' => '↵',
|
||||
'lArr' => '⇐',
|
||||
'uArr' => '⇑',
|
||||
'rArr' => '⇒',
|
||||
'dArr' => '⇓',
|
||||
'hArr' => '⇔',
|
||||
'forall' => '∀',
|
||||
'part' => '∂',
|
||||
'exist' => '∃',
|
||||
'empty' => '∅',
|
||||
'nabla' => '∇',
|
||||
'isin' => '∈',
|
||||
'notin' => '∉',
|
||||
'ni' => '∋',
|
||||
'prod' => '∏',
|
||||
'sum' => '∑',
|
||||
'minus' => '−',
|
||||
'lowast' => '∗',
|
||||
'radic' => '√',
|
||||
'prop' => '∝',
|
||||
'infin' => '∞',
|
||||
'ang' => '∠',
|
||||
'and' => '∧',
|
||||
'or' => '∨',
|
||||
'cap' => '∩',
|
||||
'cup' => '∪',
|
||||
'int' => '∫',
|
||||
'there4' => '∴',
|
||||
'sim' => '∼',
|
||||
'cong' => '≅',
|
||||
'asymp' => '≈',
|
||||
'ne' => '≠',
|
||||
'equiv' => '≡',
|
||||
'le' => '≤',
|
||||
'ge' => '≥',
|
||||
'sub' => '⊂',
|
||||
'sup' => '⊃',
|
||||
'nsub' => '⊄',
|
||||
'sube' => '⊆',
|
||||
'supe' => '⊇',
|
||||
'oplus' => '⊕',
|
||||
'otimes' => '⊗',
|
||||
'perp' => '⊥',
|
||||
'sdot' => '⋅',
|
||||
'lceil' => '⌈',
|
||||
'rceil' => '⌉',
|
||||
'lfloor' => '⌊',
|
||||
'rfloor' => '⌋',
|
||||
'lang' => '〈',
|
||||
'rang' => '〉',
|
||||
'loz' => '◊',
|
||||
'spades' => '♠',
|
||||
'clubs' => '♣',
|
||||
'hearts' => '♥',
|
||||
'diams' => '♦',
|
||||
'nbsp' => ' ',
|
||||
'iexcl' => '¡',
|
||||
'cent' => '¢',
|
||||
'pound' => '£',
|
||||
'curren' => '¤',
|
||||
'yen' => '¥',
|
||||
'brvbar' => '¦',
|
||||
'sect' => '§',
|
||||
'uml' => '¨',
|
||||
'copy' => '©',
|
||||
'ordf' => 'ª',
|
||||
'laquo' => '«',
|
||||
'not' => '¬',
|
||||
'shy' => '­',
|
||||
'reg' => '®',
|
||||
'macr' => '¯',
|
||||
'deg' => '°',
|
||||
'plusmn' => '±',
|
||||
'sup2' => '²',
|
||||
'sup3' => '³',
|
||||
'acute' => '´',
|
||||
'micro' => 'µ',
|
||||
'para' => '¶',
|
||||
'middot' => '·',
|
||||
'cedil' => '¸',
|
||||
'sup1' => '¹',
|
||||
'ordm' => 'º',
|
||||
'raquo' => '»',
|
||||
'frac14' => '¼',
|
||||
'frac12' => '½',
|
||||
'frac34' => '¾',
|
||||
'iquest' => '¿',
|
||||
'Agrave' => 'À',
|
||||
'Aacute' => 'Á',
|
||||
'Acirc' => 'Â',
|
||||
'Atilde' => 'Ã',
|
||||
'Auml' => 'Ä',
|
||||
'Aring' => 'Å',
|
||||
'AElig' => 'Æ',
|
||||
'Ccedil' => 'Ç',
|
||||
'Egrave' => 'È',
|
||||
'Eacute' => 'É',
|
||||
'Ecirc' => 'Ê',
|
||||
'Euml' => 'Ë',
|
||||
'Igrave' => 'Ì',
|
||||
'Iacute' => 'Í',
|
||||
'Icirc' => 'Î',
|
||||
'Iuml' => 'Ï',
|
||||
'ETH' => 'Ð',
|
||||
'Ntilde' => 'Ñ',
|
||||
'Ograve' => 'Ò',
|
||||
'Oacute' => 'Ó',
|
||||
'Ocirc' => 'Ô',
|
||||
'Otilde' => 'Õ',
|
||||
'Ouml' => 'Ö',
|
||||
'times' => '×',
|
||||
'Oslash' => 'Ø',
|
||||
'Ugrave' => 'Ù',
|
||||
'Uacute' => 'Ú',
|
||||
'Ucirc' => 'Û',
|
||||
'Uuml' => 'Ü',
|
||||
'Yacute' => 'Ý',
|
||||
'THORN' => 'Þ',
|
||||
'szlig' => 'ß',
|
||||
'agrave' => 'à',
|
||||
'aacute' => 'á',
|
||||
'acirc' => 'â',
|
||||
'atilde' => 'ã',
|
||||
'auml' => 'ä',
|
||||
'aring' => 'å',
|
||||
'aelig' => 'æ',
|
||||
'ccedil' => 'ç',
|
||||
'egrave' => 'è',
|
||||
'eacute' => 'é',
|
||||
'ecirc' => 'ê',
|
||||
'euml' => 'ë',
|
||||
'igrave' => 'ì',
|
||||
'iacute' => 'í',
|
||||
'icirc' => 'î',
|
||||
'iuml' => 'ï',
|
||||
'eth' => 'ð',
|
||||
'ntilde' => 'ñ',
|
||||
'ograve' => 'ò',
|
||||
'oacute' => 'ó',
|
||||
'ocirc' => 'ô',
|
||||
'otilde' => 'õ',
|
||||
'ouml' => 'ö',
|
||||
'divide' => '÷',
|
||||
'oslash' => 'ø',
|
||||
'ugrave' => 'ù',
|
||||
'uacute' => 'ú',
|
||||
'ucirc' => 'û',
|
||||
'uuml' => 'ü',
|
||||
'yacute' => 'ý',
|
||||
'thorn' => 'þ',
|
||||
'yuml' => 'ÿ',
|
||||
|
||||
);
|
||||
// Entity not found? Destroy it.
|
||||
return isset($table[$matches[1]]) ? $table[$matches[1]] : '';
|
||||
}
|
||||
54
f_modules/m_frontend/m_cron/transfer_queue.php
Normal file
54
f_modules/m_frontend/m_cron/transfer_queue.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
include_once 'f_core/f_classes/class.be.servers.php';
|
||||
|
||||
$queue = new fileQueue();
|
||||
|
||||
if ($cfg["video_module"] == 1 and $queue->load("video")) {
|
||||
if ($queue->check()) {
|
||||
$queue->startTransfer();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["image_module"] == 1 and $queue->load("image")) {
|
||||
if ($queue->check()) {
|
||||
$queue->startTransfer();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["audio_module"] == 1 and $queue->load("audio")) {
|
||||
if ($queue->check()) {
|
||||
$queue->startTransfer();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($cfg["document_module"] == 1 and $queue->load("doc")) {
|
||||
if ($queue->check()) {
|
||||
$queue->startTransfer();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
25
f_modules/m_frontend/m_cron/update_followers_subs.php
Normal file
25
f_modules/m_frontend/m_cron/update_followers_subs.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$act = VSubscriber::update_followers_subs();
|
||||
11
f_modules/m_frontend/m_cron/vod-server/cfg.php
Normal file
11
f_modules/m_frontend/m_cron/vod-server/cfg.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
/* path to recordings */
|
||||
$path = getenv('VOD_REC_PATH') ?: '/mnt/rec';
|
||||
/* main url */
|
||||
$base = getenv('CRON_BASE_URL') ?: 'http://localhost:8080';
|
||||
/* cron salt key */
|
||||
$ssk = getenv('CRON_SSK') ?: 'CHANGE_ME_IN_BACKEND';
|
||||
64
f_modules/m_frontend/m_cron/vod-server/clear_vods.php
Normal file
64
f_modules/m_frontend/m_cron/vod-server/clear_vods.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
set_time_limit(0);
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$log = $path . '/.clear_vods.log';
|
||||
error_log(sprintf("[%s] clear_vods cron task...\n\n", date("Y-m-d H:i:s")), 3, $log);
|
||||
|
||||
$cmd = 'ls ' . $path . '/*out.mp4';
|
||||
|
||||
exec($cmd, $out);
|
||||
|
||||
if ($out[0]) {
|
||||
foreach ($out as $file) {
|
||||
if (file_exists($file)) {
|
||||
$base = str_replace(array($path . '/', '.mp4'), array('', ''), $file);
|
||||
$sql = sprintf("SELECT `db_id` FROM `db_livefiles` WHERE `file_name`='%s' LIMIT 1;", $base);
|
||||
$rs = $db->execute($sql);
|
||||
|
||||
if (!$rs->fields["db_id"]) {
|
||||
$a = explode("-", $file);
|
||||
$l = str_replace('out.mp4', 'p.mp4', $a[1]);
|
||||
$preview = $a[0] . $l;
|
||||
|
||||
//echo sprintf("can delete %s\n", $file);
|
||||
//echo sprintf("can delete %s\n", $preview);
|
||||
|
||||
if (unlink($file)) {
|
||||
error_log(sprintf("[%s] unlinked %s\n", date("Y-m-d H:i:s"), $file), 3, $log);
|
||||
} else {
|
||||
error_log(sprintf("[%s] failed unlink %s\n", date("Y-m-d H:i:s"), $file), 3, $log);
|
||||
}
|
||||
|
||||
if (file_exists($preview)) {
|
||||
if (unlink($preview)) {
|
||||
error_log(sprintf("[%s] unlinked %s\n", date("Y-m-d H:i:s"), $preview), 3, $log);
|
||||
} else {
|
||||
error_log(sprintf("[%s] failed unlink %s\n", date("Y-m-d H:i:s"), $preview), 3, $log);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
f_modules/m_frontend/m_cron/vod-server/index.html
Normal file
0
f_modules/m_frontend/m_cron/vod-server/index.html
Normal file
0
f_modules/m_frontend/m_cron/vod-server/index.php
Normal file
0
f_modules/m_frontend/m_cron/vod-server/index.php
Normal file
64
f_modules/m_frontend/m_cron/vod-server/recording_fix.php
Normal file
64
f_modules/m_frontend/m_cron/vod-server/recording_fix.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
set_time_limit(0);
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$main_dir = realpath(dirname(__FILE__) . '/../../../../');
|
||||
set_include_path($main_dir);
|
||||
|
||||
include_once 'f_core/config.core.php';
|
||||
|
||||
$commands = array();
|
||||
$found = 0;
|
||||
exec("ps ax", $commands);
|
||||
if (count($commands) > 0) {
|
||||
foreach ($commands as $command) {
|
||||
if (strpos($command, 'ffmpeg') === false) {
|
||||
} else {
|
||||
$found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
exit;
|
||||
}
|
||||
$ffmpeg = '/usr/bin/ffmpeg';
|
||||
$log = $path . '/.recording_fix.log';
|
||||
$cmd = 'ls ' . $path . '/*out.mp4';
|
||||
|
||||
error_log(sprintf("[%s] recording_fix cron task...\n\n", date("Y-m-d H:i:s")), 3, $log);
|
||||
|
||||
exec($cmd, $out);
|
||||
|
||||
if ($out[0]) {
|
||||
foreach ($out as $file) {
|
||||
if (!is_file($file) or (is_file($file) and filesize($file) == 0)) {
|
||||
$flv = str_replace(".mp4", ".flv", $file);
|
||||
|
||||
if (file_exists($flv) and filesize($flv) > 0) {
|
||||
$cmd_ffmpeg = sprintf("%s -y -i %s -codec copy -movflags +faststart %s", $ffmpeg, $flv, $file);
|
||||
error_log(sprintf("[%s] %s\n", date("Y-m-d H:i:s"), $cmd_ffmpeg), 3, $log);
|
||||
|
||||
exec($cmd_ffmpeg . ' 2>&1', $out_ffmpeg);
|
||||
$result = implode("\n", $out_ffmpeg);
|
||||
|
||||
VFileinfo::write($log, $result . "\n\n\n", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$cmd = 'ls ' . $path . '/*out.mp4';
|
||||
|
||||
exec($cmd, $out);
|
||||
|
||||
if ($out[0]) {
|
||||
foreach ($out as $file) {
|
||||
if (file_exists($file)) {
|
||||
$a = explode("-", $file);
|
||||
$l = str_replace('out.mp4', 'p.mp4', $a[1]);
|
||||
$preview = $a[0] . $l;
|
||||
|
||||
if (!file_exists($preview)) {
|
||||
$cmd = 'ffmpeg -y -t 30 -i ' . $file . ' -codec copy -movflags +faststart ' . $preview;
|
||||
|
||||
exec(escapeshellcmd($cmd) . ' >/dev/null &');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
f_modules/m_frontend/m_cron/vod-server/sync_df.php
Normal file
37
f_modules/m_frontend/m_cron/vod-server/sync_df.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
define('_SERVER_SLUG', 'vods1-local');
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$url = $base . '/syncdf?s=';
|
||||
|
||||
$df = disk_free_space("/");
|
||||
$free = $df;
|
||||
|
||||
$date = date("Y-m-d");
|
||||
$tk = md5($date . $ssk);
|
||||
$url = sprintf("%s%s&a=%s&_=%s", $url, $tk, _SERVER_SLUG, $free);
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
73
f_modules/m_frontend/m_cron/vod-server/sync_vods.php
Normal file
73
f_modules/m_frontend/m_cron/vod-server/sync_vods.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
ini_set("error_reporting", E_ALL & ~E_STRICT & ~E_NOTICE & ~E_DEPRECATED);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
require 'cfg.php';
|
||||
|
||||
$url = $base . '/syncvods?s=';
|
||||
|
||||
$date = date("Y-m-d");
|
||||
$tk = md5($date . $ssk);
|
||||
$url .= $tk;
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
$list = json_decode($data);
|
||||
|
||||
$log = $path . '/.sync_vods.log';
|
||||
error_log(sprintf("[%s] sync_vods cron task...\n\n", date("Y-m-d H:i:s")), 3, $log);
|
||||
|
||||
if ($list[0]) {
|
||||
foreach ($list as $filename) {
|
||||
$vod = $path . '/' . $filename . '.mp4';
|
||||
if (file_exists($vod)) {
|
||||
if (unlink($vod)) {
|
||||
error_log(sprintf("[%s] unlinked %s\n", date("Y-m-d H:i:s"), $vod), 3, $log);
|
||||
} else {
|
||||
error_log(sprintf("[%s] failed unlink %s\n", date("Y-m-d H:i:s"), $vod), 3, $log);
|
||||
}
|
||||
}
|
||||
|
||||
$flv = $path . '/' . $filename . '.flv';
|
||||
if (file_exists($flv)) {
|
||||
if (unlink($flv)) {
|
||||
error_log(sprintf("[%s] unlinked %s\n", date("Y-m-d H:i:s"), $flv), 3, $log);
|
||||
} else {
|
||||
error_log(sprintf("[%s] failed unlink %s\n", date("Y-m-d H:i:s"), $flv), 3, $log);
|
||||
}
|
||||
}
|
||||
|
||||
$ff = explode("-", $filename);
|
||||
$_ff = str_replace('out', 'p', $ff[1]);
|
||||
|
||||
$pv = $path . '/' . $ff[0] . $_ff . '.mp4';
|
||||
if (file_exists($pv)) {
|
||||
if (unlink($pv)) {
|
||||
error_log(sprintf("[%s] unlinked %s\n", date("Y-m-d H:i:s"), $pv), 3, $log);
|
||||
} else {
|
||||
error_log(sprintf("[%s] failed unlink %s\n", date("Y-m-d H:i:s"), $pv), 3, $log);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
83
f_modules/m_frontend/m_donations/README.md
Normal file
83
f_modules/m_frontend/m_donations/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# EasyStream Donations Module
|
||||
|
||||
A comprehensive donation system for EasyStream with Square integration, analytics, goals, and notifications.
|
||||
|
||||
## Structure
|
||||
```
|
||||
m_donations/
|
||||
├── src/ # Source code
|
||||
│ ├── Core/ # Core functionality
|
||||
│ ├── Handlers/ # Business logic
|
||||
│ ├── Models/ # Data models
|
||||
│ └── Utils/ # Utilities
|
||||
├── config/ # Configuration
|
||||
├── sql/ # Database schemas
|
||||
├── public/ # Public files
|
||||
├── views/ # Templates
|
||||
└── assets/ # Static assets
|
||||
├── css/ # Styles
|
||||
├── js/ # Scripts
|
||||
└── img/ # Images
|
||||
```
|
||||
|
||||
## Features
|
||||
- Square payment integration
|
||||
- Donation goals and milestones
|
||||
- Real-time analytics
|
||||
- Notification system
|
||||
- RESTful API
|
||||
- Rate limiting
|
||||
- Security features
|
||||
|
||||
## Installation
|
||||
1. Copy module files:
|
||||
```bash
|
||||
cp -r m_donations/ /path/to/easystream/f_modules/m_frontend/
|
||||
```
|
||||
|
||||
2. Import database:
|
||||
```bash
|
||||
mysql -u your_username -p your_database < sql/install.sql
|
||||
```
|
||||
|
||||
3. Configure:
|
||||
- Copy `config/config.example.php` to `config/config.php`
|
||||
- Update Square credentials
|
||||
- Set webhook URL: `https://your-domain.com/f_modules/m_frontend/m_donations/public/webhook.php`
|
||||
|
||||
## Database Tables
|
||||
- `donations` - Donation records
|
||||
- `donation_goals` - Streamer goals
|
||||
- `donation_milestones` - Goal milestones
|
||||
- `donation_analytics` - Analytics data
|
||||
- `donation_notifications` - System notifications
|
||||
- `api_keys` - API authentication
|
||||
- `api_rate_limits` - Rate limiting
|
||||
|
||||
## Security
|
||||
- API key authentication
|
||||
- Rate limiting
|
||||
- Input validation
|
||||
- XSS protection
|
||||
- CSRF protection
|
||||
- SQL injection prevention
|
||||
|
||||
## Development
|
||||
1. Add database tables in `sql/install.sql`
|
||||
2. Update `config/config.php`
|
||||
3. Create model in `src/Models/`
|
||||
4. Create handler in `src/Handlers/`
|
||||
5. Add API endpoints in `api/index.php`
|
||||
6. Create views in `views/`
|
||||
7. Add assets in `assets/`
|
||||
|
||||
## Testing
|
||||
- Unit tests: `phpunit tests/`
|
||||
- API tests: `phpunit tests/api/`
|
||||
- Integration tests: `phpunit tests/integration/`
|
||||
|
||||
## Support
|
||||
Contact: Sami Ahmed
|
||||
|
||||
## License
|
||||
EasyStream License Agreement
|
||||
300
f_modules/m_frontend/m_donations/README_RAINFOREST.md
Normal file
300
f_modules/m_frontend/m_donations/README_RAINFOREST.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# 🌧️ Rainforest Pay Integration for EasyStream
|
||||
|
||||
## 📋 **Complete Integration Package**
|
||||
|
||||
This is a comprehensive Rainforest Pay integration for EasyStream that provides:
|
||||
|
||||
- ✅ **Full Payment Processing** - Donations, tips, and creator monetization
|
||||
- ✅ **Multiple Payment Methods** - Cards, bank transfers, mobile money, wallets
|
||||
- ✅ **Secure Webhooks** - Real-time payment notifications
|
||||
- ✅ **Payout System** - Automated creator payments
|
||||
- ✅ **Admin Dashboard** - Transaction monitoring and management
|
||||
- ✅ **User-Friendly Interface** - Modern donation forms and flows
|
||||
|
||||
## 🚀 **Quick Setup Guide**
|
||||
|
||||
### 1. **Database Setup**
|
||||
```sql
|
||||
-- Run the installation SQL
|
||||
mysql -u your_user -p your_database < install_rainforest.sql
|
||||
```
|
||||
|
||||
### 2. **Configuration**
|
||||
Edit `config.rainforest.php` with your Rainforest Pay credentials:
|
||||
|
||||
```php
|
||||
'api_key' => 'your_rainforest_api_key',
|
||||
'secret_key' => 'your_rainforest_secret_key',
|
||||
'merchant_id' => 'your_merchant_id',
|
||||
'environment' => 'sandbox', // or 'production'
|
||||
```
|
||||
|
||||
### 3. **Environment Variables** (Recommended)
|
||||
```bash
|
||||
# Add to your .env file
|
||||
RAINFOREST_API_KEY=your_api_key_here
|
||||
RAINFOREST_SECRET_KEY=your_secret_key_here
|
||||
RAINFOREST_MERCHANT_ID=your_merchant_id_here
|
||||
RAINFOREST_ENVIRONMENT=sandbox
|
||||
RAINFOREST_WEBHOOK_URL=https://yourdomain.com/donations/webhook
|
||||
RAINFOREST_WEBHOOK_SECRET=your_webhook_secret_here
|
||||
```
|
||||
|
||||
### 4. **Webhook Setup**
|
||||
Configure your Rainforest Pay webhook URL to:
|
||||
```
|
||||
https://yourdomain.com/f_modules/m_frontend/m_donations/rainforest_webhook.php
|
||||
```
|
||||
|
||||
## 📁 **File Structure**
|
||||
|
||||
```
|
||||
f_modules/m_frontend/m_donations/
|
||||
├── config.rainforest.php # Configuration settings
|
||||
├── rainforest_pay.php # Main payment handler class
|
||||
├── rainforest_donation_form.php # User donation interface
|
||||
├── rainforest_webhook.php # Webhook processor
|
||||
├── install_rainforest.sql # Database setup
|
||||
└── README_RAINFOREST.md # This documentation
|
||||
```
|
||||
|
||||
## 🎯 **Integration Points**
|
||||
|
||||
### **Donation Form URL**
|
||||
```
|
||||
/f_modules/m_frontend/m_donations/rainforest_donation_form.php?streamer=USER_ID
|
||||
```
|
||||
|
||||
### **API Endpoints**
|
||||
```php
|
||||
// Create donation
|
||||
POST /f_modules/m_frontend/m_donations/rainforest_pay.php?action=create_donation
|
||||
|
||||
// Process webhook
|
||||
POST /f_modules/m_frontend/m_donations/rainforest_webhook.php
|
||||
|
||||
// Request payout
|
||||
POST /f_modules/m_frontend/m_donations/rainforest_pay.php?action=request_payout
|
||||
|
||||
// Get payment methods
|
||||
GET /f_modules/m_frontend/m_donations/rainforest_pay.php?action=get_payment_methods
|
||||
```
|
||||
|
||||
## 💳 **Supported Payment Methods**
|
||||
|
||||
- **💳 Credit/Debit Cards** - Visa, Mastercard, American Express
|
||||
- **🏦 Bank Transfers** - Direct bank account transfers
|
||||
- **📱 Mobile Money** - MTN, Airtel, Vodafone, and other providers
|
||||
- **👛 Digital Wallets** - PayPal, Apple Pay, Google Pay
|
||||
|
||||
## 🔧 **Usage Examples**
|
||||
|
||||
### **Basic Donation Processing**
|
||||
```php
|
||||
$rainforest = new RainforestPayHandler($class_database);
|
||||
|
||||
$result = $rainforest->createDonation(
|
||||
$streamer_id = 123,
|
||||
$amount = 25.00,
|
||||
$donor_name = 'John Doe',
|
||||
$message = 'Great content!',
|
||||
$payment_method = 'card'
|
||||
);
|
||||
|
||||
if ($result['success']) {
|
||||
// Redirect to payment URL
|
||||
header('Location: ' . $result['payment_url']);
|
||||
} else {
|
||||
// Handle error
|
||||
echo $result['message'];
|
||||
}
|
||||
```
|
||||
|
||||
### **Payout Request**
|
||||
```php
|
||||
$result = $rainforest->requestPayout($streamer_id = 123);
|
||||
|
||||
if ($result['success']) {
|
||||
echo "Payout of $" . $result['amount'] . " requested successfully";
|
||||
} else {
|
||||
echo "Payout failed: " . $result['message'];
|
||||
}
|
||||
```
|
||||
|
||||
### **Get Donation History**
|
||||
```php
|
||||
$donations = $rainforest->getDonationHistory($streamer_id = 123, $limit = 20);
|
||||
|
||||
foreach ($donations as $donation) {
|
||||
echo "Donation: $" . $donation['amount'] . " from " . $donation['donor_name'];
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 **Frontend Integration**
|
||||
|
||||
### **Add Donation Button to Channel Pages**
|
||||
```html
|
||||
<a href="/f_modules/m_frontend/m_donations/rainforest_donation_form.php?streamer=<?php echo $streamer_id; ?>"
|
||||
class="donate-btn">
|
||||
💖 Donate
|
||||
</a>
|
||||
```
|
||||
|
||||
### **Add to Video Player**
|
||||
```html
|
||||
<button onclick="openDonationForm(<?php echo $streamer_id; ?>)" class="player-donate-btn">
|
||||
💰 Support Creator
|
||||
</button>
|
||||
```
|
||||
|
||||
### **JavaScript Integration**
|
||||
```javascript
|
||||
function openDonationForm(streamerId) {
|
||||
const url = `/f_modules/m_frontend/m_donations/rainforest_donation_form.php?streamer=${streamerId}`;
|
||||
window.open(url, 'donation', 'width=600,height=800,scrollbars=yes');
|
||||
}
|
||||
```
|
||||
|
||||
## 🔒 **Security Features**
|
||||
|
||||
- ✅ **Webhook Signature Verification** - Ensures authentic notifications
|
||||
- ✅ **Data Encryption** - Sensitive data is encrypted
|
||||
- ✅ **Rate Limiting** - Prevents abuse and spam
|
||||
- ✅ **Fraud Detection** - Built-in fraud prevention
|
||||
- ✅ **Audit Logging** - Complete transaction audit trail
|
||||
|
||||
## 📊 **Admin Features**
|
||||
|
||||
### **Transaction Monitoring**
|
||||
```php
|
||||
// Get platform earnings
|
||||
$sql = "SELECT SUM(platform_fee) as total_fees FROM donations WHERE status = 'completed'";
|
||||
|
||||
// Get top earners
|
||||
$sql = "SELECT streamer_id, SUM(streamer_amount) as earnings
|
||||
FROM donations
|
||||
WHERE status = 'completed'
|
||||
GROUP BY streamer_id
|
||||
ORDER BY earnings DESC
|
||||
LIMIT 10";
|
||||
```
|
||||
|
||||
### **Payout Management**
|
||||
```php
|
||||
// Get pending payouts
|
||||
$sql = "SELECT * FROM payouts WHERE status = 'pending' ORDER BY created_at ASC";
|
||||
|
||||
// Process automatic payouts
|
||||
$rainforest->processAutomaticPayouts();
|
||||
```
|
||||
|
||||
## 🎯 **Customization Options**
|
||||
|
||||
### **Fee Configuration**
|
||||
```php
|
||||
// In config.rainforest.php
|
||||
'platform_fee_percentage' => 2.5, // 2.5% platform fee
|
||||
'platform_fee_fixed' => 0.30, // $0.30 fixed fee
|
||||
'payout_fee_percentage' => 1.0, // 1% payout fee
|
||||
'payout_fee_fixed' => 0.25, // $0.25 payout fee
|
||||
```
|
||||
|
||||
### **Donation Limits**
|
||||
```php
|
||||
'min_donation' => 1.00, // Minimum $1
|
||||
'max_donation' => 10000.00, // Maximum $10,000
|
||||
'min_payout' => 10.00, // Minimum payout $10
|
||||
```
|
||||
|
||||
### **Payment Methods**
|
||||
```php
|
||||
'payment_methods' => [
|
||||
'card' => true, // Enable/disable cards
|
||||
'bank_transfer' => true, // Enable/disable bank transfers
|
||||
'mobile_money' => true, // Enable/disable mobile money
|
||||
'wallet' => false // Enable/disable wallets
|
||||
]
|
||||
```
|
||||
|
||||
## 🚨 **Error Handling**
|
||||
|
||||
The integration includes comprehensive error handling:
|
||||
|
||||
- **Invalid amounts** - Validates min/max donation limits
|
||||
- **Payment failures** - Graceful handling of failed payments
|
||||
- **Network issues** - Retry logic for API calls
|
||||
- **Webhook validation** - Signature verification for security
|
||||
- **Database errors** - Transaction rollback on failures
|
||||
|
||||
## 📈 **Analytics & Reporting**
|
||||
|
||||
### **Built-in Views**
|
||||
- `donation_summary` - Overall donation statistics per streamer
|
||||
- `monthly_donation_stats` - Monthly earnings breakdown
|
||||
|
||||
### **Custom Reports**
|
||||
```sql
|
||||
-- Top donors
|
||||
SELECT donor_name, SUM(amount) as total_donated
|
||||
FROM donations
|
||||
WHERE status = 'completed'
|
||||
GROUP BY donor_name
|
||||
ORDER BY total_donated DESC;
|
||||
|
||||
-- Daily earnings
|
||||
SELECT DATE(completed_at) as date, SUM(streamer_amount) as earnings
|
||||
FROM donations
|
||||
WHERE status = 'completed'
|
||||
GROUP BY DATE(completed_at)
|
||||
ORDER BY date DESC;
|
||||
```
|
||||
|
||||
## 🔧 **Troubleshooting**
|
||||
|
||||
### **Common Issues**
|
||||
|
||||
1. **Webhook not receiving notifications**
|
||||
- Check webhook URL configuration
|
||||
- Verify SSL certificate
|
||||
- Check firewall settings
|
||||
|
||||
2. **Payment failures**
|
||||
- Verify API credentials
|
||||
- Check environment setting (sandbox/production)
|
||||
- Review error logs
|
||||
|
||||
3. **Database connection issues**
|
||||
- Verify database credentials
|
||||
- Check table permissions
|
||||
- Run installation SQL
|
||||
|
||||
### **Debug Mode**
|
||||
Enable debug logging in `config.rainforest.php`:
|
||||
```php
|
||||
'debug' => true,
|
||||
'log_level' => 'debug'
|
||||
```
|
||||
|
||||
## 📞 **Support**
|
||||
|
||||
For Rainforest Pay specific issues:
|
||||
- 📧 **Email**: support@rainforestpay.com
|
||||
- 📚 **Documentation**: https://docs.rainforestpay.com
|
||||
- 🔧 **API Reference**: https://api.rainforestpay.com/docs
|
||||
|
||||
For EasyStream integration issues:
|
||||
- Check the error logs in `logs/rainforest_pay.log`
|
||||
- Review webhook logs in `logs/rainforest_webhooks.log`
|
||||
- Verify database table structure matches installation SQL
|
||||
|
||||
## 🎉 **Ready to Go!**
|
||||
|
||||
Your Rainforest Pay integration is now complete and ready for production use. The system provides:
|
||||
|
||||
- **Seamless donation processing** for creators
|
||||
- **Multiple payment options** for donors
|
||||
- **Automated payout system** for creators
|
||||
- **Complete admin oversight** for platform owners
|
||||
- **Secure, scalable architecture** for growth
|
||||
|
||||
**Happy monetizing!** 💰🚀
|
||||
292
f_modules/m_frontend/m_donations/api/README.md
Normal file
292
f_modules/m_frontend/m_donations/api/README.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# EasyStream Donations API Documentation
|
||||
|
||||
This API allows external applications to interact with the EasyStream donation system. All requests require authentication using an API key.
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests must include an API key in the Authorization header:
|
||||
|
||||
```
|
||||
Authorization: your-api-key-here
|
||||
```
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://your-domain.com/f_modules/m_frontend/m_donations/api
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Analytics
|
||||
|
||||
#### Get Analytics Data
|
||||
```http
|
||||
GET /analytics
|
||||
```
|
||||
|
||||
Query Parameters:
|
||||
- `start_date` (optional): Start date in YYYY-MM-DD format (default: 30 days ago)
|
||||
- `end_date` (optional): End date in YYYY-MM-DD format (default: today)
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"date": "2024-03-20",
|
||||
"total_donations": 5,
|
||||
"total_amount": 150.00,
|
||||
"average_donation": 30.00,
|
||||
"unique_donors": 3
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Get Analytics Summary
|
||||
```http
|
||||
GET /analytics/summary
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"total_donations": 150,
|
||||
"total_amount": 5000.00,
|
||||
"average_donation": 33.33,
|
||||
"unique_donors": 45,
|
||||
"largest_donation": 200.00,
|
||||
"smallest_donation": 5.00
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Top Donors
|
||||
```http
|
||||
GET /analytics/top-donors
|
||||
```
|
||||
|
||||
Query Parameters:
|
||||
- `limit` (optional): Number of top donors to return (default: 10)
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"username": "donor1",
|
||||
"display_name": "John Doe",
|
||||
"donation_count": 15,
|
||||
"total_amount": 500.00
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Goals
|
||||
|
||||
#### Get All Goals
|
||||
```http
|
||||
GET /goals
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"goal_id": 1,
|
||||
"title": "New Equipment",
|
||||
"description": "Help me upgrade my streaming setup",
|
||||
"target_amount": 1000.00,
|
||||
"current_amount": 500.00,
|
||||
"start_date": "2024-03-01T00:00:00Z",
|
||||
"end_date": "2024-04-01T00:00:00Z",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Create New Goal
|
||||
```http
|
||||
POST /goals
|
||||
```
|
||||
|
||||
Request Body:
|
||||
```json
|
||||
{
|
||||
"title": "New Equipment",
|
||||
"description": "Help me upgrade my streaming setup",
|
||||
"target_amount": 1000.00,
|
||||
"end_date": "2024-04-01"
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"goal_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Active Goals
|
||||
```http
|
||||
GET /goals/active
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"goal_id": 1,
|
||||
"title": "New Equipment",
|
||||
"description": "Help me upgrade my streaming setup",
|
||||
"target_amount": 1000.00,
|
||||
"current_amount": 500.00,
|
||||
"start_date": "2024-03-01T00:00:00Z",
|
||||
"end_date": "2024-04-01T00:00:00Z",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Add Milestone to Goal
|
||||
```http
|
||||
POST /goals/milestones
|
||||
```
|
||||
|
||||
Request Body:
|
||||
```json
|
||||
{
|
||||
"goal_id": 1,
|
||||
"title": "Halfway There",
|
||||
"description": "Reach 50% of the goal",
|
||||
"target_amount": 500.00,
|
||||
"reward_description": "Special shoutout on stream"
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"milestone_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Notifications
|
||||
|
||||
#### Get All Notifications
|
||||
```http
|
||||
GET /notifications
|
||||
```
|
||||
|
||||
Query Parameters:
|
||||
- `limit` (optional): Number of notifications to return (default: 20)
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"notification_id": 1,
|
||||
"type": "donation",
|
||||
"title": "New Donation",
|
||||
"message": "Received $50.00 from John Doe",
|
||||
"is_read": false,
|
||||
"created_at": "2024-03-20T15:30:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Get Unread Notifications
|
||||
```http
|
||||
GET /notifications/unread
|
||||
```
|
||||
|
||||
Query Parameters:
|
||||
- `limit` (optional): Number of notifications to return (default: 10)
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"notification_id": 1,
|
||||
"type": "donation",
|
||||
"title": "New Donation",
|
||||
"message": "Received $50.00 from John Doe",
|
||||
"is_read": false,
|
||||
"created_at": "2024-03-20T15:30:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Mark Notifications as Read
|
||||
```http
|
||||
POST /notifications/unread
|
||||
```
|
||||
|
||||
Request Body:
|
||||
```json
|
||||
{
|
||||
"notification_ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints may return the following error responses:
|
||||
|
||||
### 400 Bad Request
|
||||
```json
|
||||
{
|
||||
"error": "Missing required fields"
|
||||
}
|
||||
```
|
||||
|
||||
### 401 Unauthorized
|
||||
```json
|
||||
{
|
||||
"error": "API key is required"
|
||||
}
|
||||
```
|
||||
or
|
||||
```json
|
||||
{
|
||||
"error": "Invalid API key"
|
||||
}
|
||||
```
|
||||
|
||||
### 404 Not Found
|
||||
```json
|
||||
{
|
||||
"error": "Endpoint not found"
|
||||
}
|
||||
```
|
||||
|
||||
### 405 Method Not Allowed
|
||||
```json
|
||||
{
|
||||
"error": "Method not allowed"
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
The API is rate limited to 100 requests per minute per API key. When the rate limit is exceeded, the API will return a 429 Too Many Requests response:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Rate limit exceeded"
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Always include error handling in your API calls
|
||||
2. Cache responses when appropriate to reduce API calls
|
||||
3. Use appropriate HTTP methods (GET for retrieving data, POST for creating)
|
||||
4. Include proper error messages in your application when API calls fail
|
||||
5. Keep your API key secure and never expose it in client-side code
|
||||
194
f_modules/m_frontend/m_donations/api/index.php
Normal file
194
f_modules/m_frontend/m_donations/api/index.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
use Donations\AnalyticsHandler;
|
||||
use Donations\GoalHandler;
|
||||
use Donations\NotificationHandler;
|
||||
|
||||
// Set headers
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
// Handle preflight requests
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get request method and path
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$path = str_replace('/f_modules/m_frontend/m_donations/api', '', $path);
|
||||
$path = trim($path, '/');
|
||||
|
||||
// Get request body
|
||||
$body = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate API key
|
||||
$headers = getallheaders();
|
||||
$api_key = $headers['Authorization'] ?? null;
|
||||
|
||||
if (!$api_key) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'API key is required']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Validate API key against database
|
||||
$sql = "SELECT user_id FROM api_keys WHERE api_key = ? AND is_active = 1";
|
||||
$user = db()->getRow($sql, [$api_key]);
|
||||
|
||||
if (!$user) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Invalid API key']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$streamer_id = $user['user_id'];
|
||||
|
||||
// Route requests
|
||||
try {
|
||||
switch ($path) {
|
||||
case 'analytics':
|
||||
$handler = new AnalyticsHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$start_date = $_GET['start_date'] ?? date('Y-m-d', strtotime('-30 days'));
|
||||
$end_date = $_GET['end_date'] ?? date('Y-m-d');
|
||||
echo json_encode($handler->getAnalytics($streamer_id, $start_date, $end_date));
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'analytics/summary':
|
||||
$handler = new AnalyticsHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
echo json_encode($handler->getSummary($streamer_id));
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'analytics/top-donors':
|
||||
$handler = new AnalyticsHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$limit = $_GET['limit'] ?? 10;
|
||||
echo json_encode($handler->getTopDonors($streamer_id, $limit));
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'goals':
|
||||
$handler = new GoalHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
echo json_encode($handler->getStreamerGoals($streamer_id));
|
||||
break;
|
||||
case 'POST':
|
||||
if (!isset($body['title']) || !isset($body['target_amount'])) {
|
||||
throw new Exception('Missing required fields');
|
||||
}
|
||||
$goal_id = $handler->createGoal(
|
||||
$streamer_id,
|
||||
$body['title'],
|
||||
$body['description'] ?? '',
|
||||
$body['target_amount'],
|
||||
$body['end_date'] ?? null
|
||||
);
|
||||
echo json_encode(['success' => true, 'goal_id' => $goal_id]);
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'goals/active':
|
||||
$handler = new GoalHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
echo json_encode($handler->getActiveGoals($streamer_id));
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'goals/milestones':
|
||||
$handler = new GoalHandler();
|
||||
switch ($method) {
|
||||
case 'POST':
|
||||
if (!isset($body['goal_id']) || !isset($body['title']) || !isset($body['target_amount'])) {
|
||||
throw new Exception('Missing required fields');
|
||||
}
|
||||
$milestone_id = $handler->addMilestone(
|
||||
$body['goal_id'],
|
||||
$body['title'],
|
||||
$body['description'] ?? '',
|
||||
$body['target_amount'],
|
||||
$body['reward_description'] ?? ''
|
||||
);
|
||||
echo json_encode(['success' => true, 'milestone_id' => $milestone_id]);
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'notifications':
|
||||
$handler = new NotificationHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$limit = $_GET['limit'] ?? 20;
|
||||
echo json_encode($handler->getAllNotifications($streamer_id, $limit));
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'notifications/unread':
|
||||
$handler = new NotificationHandler();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$limit = $_GET['limit'] ?? 10;
|
||||
echo json_encode($handler->getUnreadNotifications($streamer_id, $limit));
|
||||
break;
|
||||
case 'POST':
|
||||
if (!isset($body['notification_ids'])) {
|
||||
throw new Exception('Missing notification IDs');
|
||||
}
|
||||
$success = $handler->markAsRead($body['notification_ids']);
|
||||
echo json_encode(['success' => $success]);
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Endpoint not found']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
56
f_modules/m_frontend/m_donations/api/overlay_status.php
Normal file
56
f_modules/m_frontend/m_donations/api/overlay_status.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$usr_key = $class_filter->clr_str($_GET['u']);
|
||||
if ($usr_key == '') { echo json_encode(['recent'=>[], 'goal'=>['title'=>'','raised'=>0,'target'=>0]]); exit; }
|
||||
|
||||
// map usr_key -> usr_id
|
||||
$usr_id = (int) $class_database->singleFieldValue('db_accountuser','usr_id','usr_key',$usr_key);
|
||||
|
||||
$recent = [];
|
||||
$goal = ['title'=>'','raised'=>0,'target'=>0];
|
||||
|
||||
try {
|
||||
// Donations may be stored by the donations module; try common table names
|
||||
// recent donations
|
||||
$q = $db->execute(sprintf("SELECT `donor_name`,`amount`,`message`,`created_at` FROM `donations` WHERE `channel_usr_id`='%s' ORDER BY `created_at` DESC LIMIT 5;", $usr_id));
|
||||
while(!$q->EOF){
|
||||
$recent[] = [
|
||||
'name' => $q->fields['donor_name'],
|
||||
'amount' => (float) $q->fields['amount'],
|
||||
'message' => $q->fields['message'],
|
||||
'time' => $q->fields['created_at'],
|
||||
];
|
||||
$q->MoveNext();
|
||||
}
|
||||
} catch (Exception $e) { /* table missing -> ignore */ }
|
||||
|
||||
try {
|
||||
$g = $db->execute(sprintf("SELECT `title`,`target_amount`,`current_amount` FROM `donation_goals` WHERE `channel_usr_id`='%s' AND `active`='1' ORDER BY `id` DESC LIMIT 1;", $usr_id));
|
||||
if ($g->fields['title'] != ''){
|
||||
$goal['title'] = $g->fields['title'];
|
||||
$goal['target'] = (float) $g->fields['target_amount'];
|
||||
$goal['raised'] = (float) $g->fields['current_amount'];
|
||||
}
|
||||
} catch (Exception $e) { /* ignore */ }
|
||||
|
||||
echo json_encode(['recent'=>$recent, 'goal'=>$goal]);
|
||||
|
||||
74
f_modules/m_frontend/m_donations/config.rainforest.php
Normal file
74
f_modules/m_frontend/m_donations/config.rainforest.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Rainforest Pay Configuration for EasyStream
|
||||
| Integration with Rainforest Pay payment gateway
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
return [
|
||||
'rainforest' => [
|
||||
// API Configuration
|
||||
'api_key' => getenv('RAINFOREST_API_KEY') ?: 'your_api_key_here',
|
||||
'secret_key' => getenv('RAINFOREST_SECRET_KEY') ?: 'your_secret_key_here',
|
||||
'merchant_id' => getenv('RAINFOREST_MERCHANT_ID') ?: 'your_merchant_id_here',
|
||||
|
||||
// Environment Settings
|
||||
'environment' => getenv('RAINFOREST_ENVIRONMENT') ?: 'sandbox', // 'sandbox' or 'production'
|
||||
'api_base_url' => [
|
||||
'sandbox' => 'https://api-sandbox.rainforestpay.com/v1',
|
||||
'production' => 'https://api.rainforestpay.com/v1'
|
||||
],
|
||||
|
||||
// Currency and Limits
|
||||
'currency' => 'USD',
|
||||
'min_donation' => 1.00,
|
||||
'max_donation' => 10000.00,
|
||||
|
||||
// Supported Payment Methods
|
||||
'payment_methods' => [
|
||||
'card' => true, // Credit/Debit Cards
|
||||
'bank_transfer' => true, // Bank Transfers
|
||||
'mobile_money' => true, // Mobile Money (MTN, Airtel, etc.)
|
||||
'crypto' => false, // Cryptocurrency (if supported)
|
||||
'wallet' => true // Digital Wallets
|
||||
],
|
||||
|
||||
// Webhook Configuration
|
||||
'webhook_url' => getenv('RAINFOREST_WEBHOOK_URL') ?: 'https://yourdomain.com/donations/webhook',
|
||||
'webhook_secret' => getenv('RAINFOREST_WEBHOOK_SECRET') ?: 'your_webhook_secret_here',
|
||||
|
||||
// Transaction Settings
|
||||
'auto_capture' => true,
|
||||
'timeout' => 300, // 5 minutes
|
||||
|
||||
// Fee Configuration
|
||||
'platform_fee_percentage' => 2.5, // Platform fee percentage
|
||||
'platform_fee_fixed' => 0.30, // Fixed platform fee
|
||||
|
||||
// Payout Settings
|
||||
'min_payout' => 10.00,
|
||||
'payout_schedule' => 'weekly', // 'daily', 'weekly', 'monthly'
|
||||
'payout_fee_percentage' => 1.0,
|
||||
'payout_fee_fixed' => 0.25
|
||||
],
|
||||
|
||||
// Streamer Configuration
|
||||
'streamer' => [
|
||||
'min_balance' => 10.00,
|
||||
'payout_fee' => 2.5, // Percentage
|
||||
'payout_fee_fixed' => 0.50,
|
||||
'auto_payout' => false,
|
||||
'payout_threshold' => 100.00
|
||||
],
|
||||
|
||||
// Security Settings
|
||||
'security' => [
|
||||
'encrypt_data' => true,
|
||||
'log_transactions' => true,
|
||||
'fraud_detection' => true,
|
||||
'rate_limiting' => [
|
||||
'max_attempts' => 5,
|
||||
'time_window' => 3600 // 1 hour
|
||||
]
|
||||
]
|
||||
];
|
||||
?>
|
||||
20
f_modules/m_frontend/m_donations/config.square.php
Normal file
20
f_modules/m_frontend/m_donations/config.square.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
// Square API Configuration
|
||||
return [
|
||||
'square' => [
|
||||
'application_id' => 'YOUR_SQUARE_APP_ID',
|
||||
'access_token' => 'YOUR_SQUARE_ACCESS_TOKEN',
|
||||
'location_id' => 'YOUR_SQUARE_LOCATION_ID',
|
||||
'environment' => 'sandbox', // Change to 'production' when going live
|
||||
'currency' => 'USD',
|
||||
'min_donation' => 1.00,
|
||||
'max_donation' => 1000.00,
|
||||
'default_amounts' => [5, 10, 25, 50, 100],
|
||||
'webhook_secret' => 'YOUR_WEBHOOK_SECRET'
|
||||
],
|
||||
'streamer' => [
|
||||
'min_balance' => 10.00, // Minimum balance before payout
|
||||
'payout_fee' => 2.9, // Square payout fee percentage
|
||||
'payout_fee_fixed' => 0.30 // Fixed fee per payout
|
||||
]
|
||||
];
|
||||
109
f_modules/m_frontend/m_donations/config/config.php
Normal file
109
f_modules/m_frontend/m_donations/config/config.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
// Module paths
|
||||
define('DONATIONS_PATH', __DIR__ . '/..');
|
||||
define('DONATIONS_SRC', DONATIONS_PATH . '/src');
|
||||
define('DONATIONS_PUBLIC', DONATIONS_PATH . '/public');
|
||||
define('DONATIONS_VIEWS', DONATIONS_PATH . '/views');
|
||||
define('DONATIONS_ASSETS', DONATIONS_PATH . '/assets');
|
||||
|
||||
// Autoloader
|
||||
spl_autoload_register(function ($class) {
|
||||
$file = DONATIONS_SRC . '/' . str_replace('\\', '/', $class) . '.php';
|
||||
if (file_exists($file)) require_once $file;
|
||||
});
|
||||
|
||||
// Helper functions
|
||||
function redirect($url) {
|
||||
header("Location: $url");
|
||||
exit;
|
||||
}
|
||||
|
||||
function json_response($data, $status = 200) {
|
||||
http_response_code($status);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
function view($template, $data = []) {
|
||||
extract($data);
|
||||
require DONATIONS_VIEWS . "/$template.php";
|
||||
}
|
||||
|
||||
function handle_error($message, $code = 500) {
|
||||
if (request_wants_json()) {
|
||||
json_response(['error' => $message], $code);
|
||||
} else {
|
||||
http_response_code($code);
|
||||
view('error', ['message' => $message]);
|
||||
}
|
||||
}
|
||||
|
||||
function request_wants_json() {
|
||||
return isset($_SERVER['HTTP_ACCEPT']) &&
|
||||
strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false;
|
||||
}
|
||||
|
||||
function csrf_token() {
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
|
||||
function verify_csrf_token($token) {
|
||||
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
|
||||
function db() {
|
||||
global $class_database;
|
||||
return $class_database;
|
||||
}
|
||||
|
||||
// Module configuration
|
||||
return [
|
||||
'square' => [
|
||||
'application_id' => 'YOUR_SQUARE_APP_ID',
|
||||
'access_token' => 'YOUR_SQUARE_ACCESS_TOKEN',
|
||||
'location_id' => 'YOUR_SQUARE_LOCATION_ID',
|
||||
'environment' => 'sandbox',
|
||||
'currency' => 'USD',
|
||||
'min_donation' => 1.00,
|
||||
'max_donation' => 1000.00,
|
||||
'default_amounts' => [5, 10, 25, 50, 100],
|
||||
'webhook_secret' => 'YOUR_WEBHOOK_SECRET'
|
||||
],
|
||||
'payout' => [
|
||||
'min_balance' => 50.00,
|
||||
'payout_fee' => 0.05,
|
||||
'payout_fee_fixed' => 1.00
|
||||
],
|
||||
'api' => [
|
||||
'rate_limit' => 100,
|
||||
'rate_window' => 60,
|
||||
'allowed_ips' => []
|
||||
],
|
||||
'tables' => [
|
||||
'donations' => 'donations',
|
||||
'donation_goals' => 'donation_goals',
|
||||
'donation_milestones' => 'donation_milestones',
|
||||
'donation_analytics' => 'donation_analytics',
|
||||
'donation_notifications' => 'donation_notifications',
|
||||
'api_keys' => 'api_keys',
|
||||
'api_rate_limits' => 'api_rate_limits'
|
||||
],
|
||||
'notifications' => [
|
||||
'retention_days' => 30,
|
||||
'batch_size' => 100
|
||||
],
|
||||
'analytics' => [
|
||||
'default_period' => 30,
|
||||
'cache_duration' => 3600,
|
||||
'min_data_points' => 10
|
||||
],
|
||||
'security' => [
|
||||
'allowed_origins' => [],
|
||||
'max_request_size' => '10M',
|
||||
'session_timeout' => 3600
|
||||
]
|
||||
];
|
||||
19
f_modules/m_frontend/m_donations/config/square.php
Normal file
19
f_modules/m_frontend/m_donations/config/square.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
return [
|
||||
'square' => [
|
||||
'application_id' => 'YOUR_SQUARE_APP_ID',
|
||||
'access_token' => 'YOUR_SQUARE_ACCESS_TOKEN',
|
||||
'location_id' => 'YOUR_SQUARE_LOCATION_ID',
|
||||
'environment' => 'sandbox', // Change to 'production' when going live
|
||||
'currency' => 'USD',
|
||||
'min_donation' => 1.00,
|
||||
'max_donation' => 1000.00,
|
||||
'default_amounts' => [5, 10, 25, 50, 100],
|
||||
'webhook_secret' => 'YOUR_WEBHOOK_SECRET'
|
||||
],
|
||||
'streamer' => [
|
||||
'min_balance' => 10.00, // Minimum balance before payout
|
||||
'payout_fee' => 2.9, // Square payout fee percentage
|
||||
'payout_fee_fixed' => 0.30 // Fixed fee per payout
|
||||
]
|
||||
];
|
||||
172
f_modules/m_frontend/m_donations/donate.php
Normal file
172
f_modules/m_frontend/m_donations/donate.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
// Load Square configuration
|
||||
$square_config = require_once __DIR__ . '/config.square.php';
|
||||
|
||||
// Include Square SDK
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Square\SquareClient;
|
||||
use Square\Environment;
|
||||
use Square\Models\CreatePaymentRequest;
|
||||
use Square\Models\Money;
|
||||
|
||||
class DonationHandler {
|
||||
private $square_client;
|
||||
private $config;
|
||||
private $class_database;
|
||||
|
||||
public function __construct($class_database) {
|
||||
$this->class_database = $class_database;
|
||||
$this->config = require __DIR__ . '/config.square.php';
|
||||
|
||||
// Initialize Square client
|
||||
$this->square_client = new SquareClient([
|
||||
'accessToken' => $this->config['square']['access_token'],
|
||||
'environment' => $this->config['square']['environment'] === 'production' ? Environment::PRODUCTION : Environment::SANDBOX
|
||||
]);
|
||||
}
|
||||
|
||||
public function createDonation($streamer_id, $amount, $donor_name = '', $message = '') {
|
||||
try {
|
||||
// Validate amount
|
||||
if ($amount < $this->config['square']['min_donation'] || $amount > $this->config['square']['max_donation']) {
|
||||
return ['success' => false, 'message' => 'Invalid donation amount'];
|
||||
}
|
||||
|
||||
// Create payment request
|
||||
$money = new Money();
|
||||
$money->setAmount($amount * 100); // Convert to cents
|
||||
$money->setCurrency($this->config['square']['currency']);
|
||||
|
||||
$payment_request = new CreatePaymentRequest();
|
||||
$payment_request->setSourceId('EXTERNAL');
|
||||
$payment_request->setAmountMoney($money);
|
||||
$payment_request->setLocationId($this->config['square']['location_id']);
|
||||
|
||||
// Add metadata
|
||||
$payment_request->setMetadata([
|
||||
'streamer_id' => $streamer_id,
|
||||
'donor_name' => $donor_name,
|
||||
'message' => $message
|
||||
]);
|
||||
|
||||
// Create payment
|
||||
$payment = $this->square_client->getPaymentsApi()->createPayment($payment_request);
|
||||
|
||||
if ($payment->isSuccess()) {
|
||||
// Record donation in database
|
||||
$this->recordDonation($streamer_id, $amount, $donor_name, $message, $payment->getResult()->getPayment()->getId());
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'payment_id' => $payment->getResult()->getPayment()->getId(),
|
||||
'message' => 'Donation processed successfully'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Failed to process donation: ' . $payment->getErrors()[0]->getDetail()
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Error processing donation: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function recordDonation($streamer_id, $amount, $donor_name, $message, $payment_id) {
|
||||
$sql = "INSERT INTO donations (
|
||||
streamer_id, amount, donor_name, message, payment_id, status, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, 'completed', NOW())";
|
||||
|
||||
$params = [$streamer_id, $amount, $donor_name, $message, $payment_id];
|
||||
$this->class_database->executeQuery($sql, $params);
|
||||
|
||||
// Update streamer's balance
|
||||
$sql = "UPDATE users SET donation_balance = donation_balance + ? WHERE user_id = ?";
|
||||
$this->class_database->executeQuery($sql, [$amount, $streamer_id]);
|
||||
}
|
||||
|
||||
public function getStreamerBalance($streamer_id) {
|
||||
$sql = "SELECT donation_balance FROM users WHERE user_id = ?";
|
||||
$result = $this->class_database->getRow($sql, [$streamer_id]);
|
||||
return $result['donation_balance'] ?? 0;
|
||||
}
|
||||
|
||||
public function requestPayout($streamer_id) {
|
||||
try {
|
||||
$balance = $this->getStreamerBalance($streamer_id);
|
||||
|
||||
if ($balance < $this->config['streamer']['min_balance']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Insufficient balance for payout'
|
||||
];
|
||||
}
|
||||
|
||||
// Calculate fees
|
||||
$fee_amount = ($balance * ($this->config['streamer']['payout_fee'] / 100)) + $this->config['streamer']['payout_fee_fixed'];
|
||||
$payout_amount = $balance - $fee_amount;
|
||||
|
||||
// Create payout request
|
||||
$payout = $this->square_client->getPayoutsApi()->createPayout([
|
||||
'amount_money' => [
|
||||
'amount' => $payout_amount * 100, // Convert to cents
|
||||
'currency' => $this->config['square']['currency']
|
||||
],
|
||||
'location_id' => $this->config['square']['location_id']
|
||||
]);
|
||||
|
||||
if ($payout->isSuccess()) {
|
||||
// Record payout in database
|
||||
$this->recordPayout($streamer_id, $payout_amount, $fee_amount, $payout->getResult()->getPayout()->getId());
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Payout processed successfully',
|
||||
'amount' => $payout_amount
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Failed to process payout: ' . $payout->getErrors()[0]->getDetail()
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Error processing payout: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function recordPayout($streamer_id, $amount, $fee, $payout_id) {
|
||||
// Record payout
|
||||
$sql = "INSERT INTO payouts (
|
||||
streamer_id, amount, fee, payout_id, status, created_at
|
||||
) VALUES (?, ?, ?, ?, 'completed', NOW())";
|
||||
|
||||
$params = [$streamer_id, $amount, $fee, $payout_id];
|
||||
$this->class_database->executeQuery($sql, $params);
|
||||
|
||||
// Reset streamer's balance
|
||||
$sql = "UPDATE users SET donation_balance = 0 WHERE user_id = ?";
|
||||
$this->class_database->executeQuery($sql, [$streamer_id]);
|
||||
}
|
||||
|
||||
public function getDonationHistory($streamer_id, $limit = 10) {
|
||||
$sql = "SELECT d.*, u.username as streamer_name
|
||||
FROM donations d
|
||||
JOIN users u ON d.streamer_id = u.user_id
|
||||
WHERE d.streamer_id = ?
|
||||
ORDER BY d.created_at DESC
|
||||
LIMIT ?";
|
||||
|
||||
return $this->class_database->getRows($sql, [$streamer_id, $limit]);
|
||||
}
|
||||
}
|
||||
150
f_modules/m_frontend/m_donations/donation_form.php
Normal file
150
f_modules/m_frontend/m_donations/donation_form.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
// Load Square configuration
|
||||
$square_config = require_once __DIR__ . '/config.square.php';
|
||||
|
||||
// Get streamer information
|
||||
$streamer_id = $_GET['streamer_id'] ?? 0;
|
||||
$sql = "SELECT username, display_name FROM users WHERE user_id = ?";
|
||||
$streamer = $class_database->getRow($sql, [$streamer_id]);
|
||||
|
||||
if (!$streamer) {
|
||||
die('Invalid streamer');
|
||||
}
|
||||
|
||||
// Initialize donation handler
|
||||
require_once __DIR__ . '/donate.php';
|
||||
$donation_handler = new DonationHandler($class_database);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Donate to <?php echo htmlspecialchars($streamer['display_name'] ?? $streamer['username']); ?></title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<style>
|
||||
.donation-amount-btn {
|
||||
margin: 5px;
|
||||
min-width: 80px;
|
||||
}
|
||||
.custom-amount-input {
|
||||
max-width: 150px;
|
||||
}
|
||||
.donation-form {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container py-5">
|
||||
<div class="donation-form">
|
||||
<h2 class="text-center mb-4">Donate to <?php echo htmlspecialchars($streamer['display_name'] ?? $streamer['username']); ?></h2>
|
||||
|
||||
<form id="donationForm" class="needs-validation" novalidate>
|
||||
<input type="hidden" name="streamer_id" value="<?php echo $streamer_id; ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="donorName" class="form-label">Your Name (Optional)</label>
|
||||
<input type="text" class="form-control" id="donorName" name="donor_name">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Donation Amount</label>
|
||||
<div class="d-flex flex-wrap justify-content-center mb-3">
|
||||
<?php foreach ($square_config['square']['default_amounts'] as $amount): ?>
|
||||
<button type="button" class="btn btn-outline-primary donation-amount-btn"
|
||||
data-amount="<?php echo $amount; ?>">
|
||||
$<?php echo number_format($amount, 2); ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" class="form-control custom-amount-input" id="customAmount"
|
||||
name="amount" min="<?php echo $square_config['square']['min_donation']; ?>"
|
||||
max="<?php echo $square_config['square']['max_donation']; ?>"
|
||||
step="0.01" required>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
Minimum: $<?php echo number_format($square_config['square']['min_donation'], 2); ?><br>
|
||||
Maximum: $<?php echo number_format($square_config['square']['max_donation'], 2); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">Message (Optional)</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary">Donate Now</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.getElementById('donationForm');
|
||||
const customAmount = document.getElementById('customAmount');
|
||||
const amountButtons = document.querySelectorAll('.donation-amount-btn');
|
||||
|
||||
// Handle preset amount buttons
|
||||
amountButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const amount = this.dataset.amount;
|
||||
customAmount.value = amount;
|
||||
customAmount.focus();
|
||||
});
|
||||
});
|
||||
|
||||
// Form submission
|
||||
form.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!form.checkValidity()) {
|
||||
e.stopPropagation();
|
||||
form.classList.add('was-validated');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
const data = {
|
||||
streamer_id: formData.get('streamer_id'),
|
||||
amount: parseFloat(formData.get('amount')),
|
||||
donor_name: formData.get('donor_name'),
|
||||
message: formData.get('message')
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('process_donation.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Redirect to Square payment page
|
||||
window.location.href = result.payment_url;
|
||||
} else {
|
||||
alert(result.message || 'An error occurred. Please try again.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('An error occurred. Please try again.');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
49
f_modules/m_frontend/m_donations/install.sql
Normal file
49
f_modules/m_frontend/m_donations/install.sql
Normal file
@@ -0,0 +1,49 @@
|
||||
-- Add donation_balance column to users table if it doesn't exist
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS donation_balance DECIMAL(10,2) DEFAULT 0.00;
|
||||
|
||||
-- Create donations table
|
||||
CREATE TABLE IF NOT EXISTS donations (
|
||||
donation_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
streamer_id INT NOT NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
donor_name VARCHAR(255),
|
||||
message TEXT,
|
||||
payment_id VARCHAR(255) NOT NULL,
|
||||
status ENUM('pending', 'completed', 'failed') NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_payment_id (payment_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Create payouts table
|
||||
CREATE TABLE IF NOT EXISTS payouts (
|
||||
payout_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
streamer_id INT NOT NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
fee DECIMAL(10,2) NOT NULL,
|
||||
payout_id VARCHAR(255) NOT NULL,
|
||||
status ENUM('pending', 'processing', 'completed', 'failed') NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_payout_id (payout_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Create webhook_logs table
|
||||
CREATE TABLE IF NOT EXISTS webhook_logs (
|
||||
log_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
event_type VARCHAR(50) NOT NULL,
|
||||
resource_id VARCHAR(255) NOT NULL,
|
||||
streamer_id INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_donations_streamer_id ON donations(streamer_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_donations_status ON donations(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_payouts_streamer_id ON payouts(streamer_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_payouts_status ON payouts(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhook_logs_event_type ON webhook_logs(event_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhook_logs_resource_id ON webhook_logs(resource_id);
|
||||
210
f_modules/m_frontend/m_donations/install_rainforest.sql
Normal file
210
f_modules/m_frontend/m_donations/install_rainforest.sql
Normal file
@@ -0,0 +1,210 @@
|
||||
-- Rainforest Pay Integration Database Tables for EasyStream
|
||||
-- Run this SQL to create the necessary tables for Rainforest Pay integration
|
||||
|
||||
-- Donations table
|
||||
CREATE TABLE IF NOT EXISTS `donations` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`streamer_id` int(11) NOT NULL,
|
||||
`donor_id` int(11) DEFAULT NULL,
|
||||
`amount` decimal(10,2) NOT NULL,
|
||||
`platform_fee` decimal(10,2) NOT NULL DEFAULT 0.00,
|
||||
`streamer_amount` decimal(10,2) NOT NULL,
|
||||
`donor_name` varchar(255) DEFAULT NULL,
|
||||
`message` text DEFAULT NULL,
|
||||
`payment_method` varchar(50) NOT NULL DEFAULT 'card',
|
||||
`rainforest_payment_id` varchar(255) NOT NULL,
|
||||
`status` enum('pending','completed','failed','cancelled','refunded') NOT NULL DEFAULT 'pending',
|
||||
`rainforest_data` json DEFAULT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`completed_at` timestamp NULL DEFAULT NULL,
|
||||
`failed_at` timestamp NULL DEFAULT NULL,
|
||||
`failure_reason` text DEFAULT NULL,
|
||||
`payout_id` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_streamer_id` (`streamer_id`),
|
||||
KEY `idx_donor_id` (`donor_id`),
|
||||
KEY `idx_rainforest_payment_id` (`rainforest_payment_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_payout_id` (`payout_id`),
|
||||
FOREIGN KEY (`streamer_id`) REFERENCES `db_accountuser` (`usr_id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`donor_id`) REFERENCES `db_accountuser` (`usr_id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Payouts table
|
||||
CREATE TABLE IF NOT EXISTS `payouts` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`streamer_id` int(11) NOT NULL,
|
||||
`amount` decimal(10,2) NOT NULL,
|
||||
`fee` decimal(10,2) NOT NULL DEFAULT 0.00,
|
||||
`rainforest_payout_id` varchar(255) NOT NULL,
|
||||
`status` enum('pending','completed','failed','cancelled') NOT NULL DEFAULT 'pending',
|
||||
`rainforest_data` json DEFAULT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`completed_at` timestamp NULL DEFAULT NULL,
|
||||
`failed_at` timestamp NULL DEFAULT NULL,
|
||||
`failure_reason` text DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_streamer_id` (`streamer_id`),
|
||||
KEY `idx_rainforest_payout_id` (`rainforest_payout_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
FOREIGN KEY (`streamer_id`) REFERENCES `db_accountuser` (`usr_id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Streamer payout settings table
|
||||
CREATE TABLE IF NOT EXISTS `streamer_payout_settings` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`streamer_id` int(11) NOT NULL,
|
||||
`payout_method` enum('bank_transfer','mobile_money','wallet','crypto') NOT NULL,
|
||||
`payout_details` json NOT NULL,
|
||||
`is_verified` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`auto_payout` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`payout_threshold` decimal(10,2) NOT NULL DEFAULT 10.00,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_streamer_id` (`streamer_id`),
|
||||
FOREIGN KEY (`streamer_id`) REFERENCES `db_accountuser` (`usr_id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Donation goals table (optional feature)
|
||||
CREATE TABLE IF NOT EXISTS `donation_goals` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`streamer_id` int(11) NOT NULL,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`target_amount` decimal(10,2) NOT NULL,
|
||||
`current_amount` decimal(10,2) NOT NULL DEFAULT 0.00,
|
||||
`is_active` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`completed_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_streamer_id` (`streamer_id`),
|
||||
KEY `idx_is_active` (`is_active`),
|
||||
FOREIGN KEY (`streamer_id`) REFERENCES `db_accountuser` (`usr_id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Transaction logs table for audit trail
|
||||
CREATE TABLE IF NOT EXISTS `rainforest_transaction_logs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`transaction_type` enum('donation','payout','webhook','refund') NOT NULL,
|
||||
`transaction_id` varchar(255) NOT NULL,
|
||||
`streamer_id` int(11) DEFAULT NULL,
|
||||
`amount` decimal(10,2) DEFAULT NULL,
|
||||
`status` varchar(50) NOT NULL,
|
||||
`request_data` json DEFAULT NULL,
|
||||
`response_data` json DEFAULT NULL,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`user_agent` text DEFAULT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_transaction_type` (`transaction_type`),
|
||||
KEY `idx_transaction_id` (`transaction_id`),
|
||||
KEY `idx_streamer_id` (`streamer_id`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
FOREIGN KEY (`streamer_id`) REFERENCES `db_accountuser` (`usr_id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Add donation balance column to existing user table
|
||||
ALTER TABLE `db_accountuser`
|
||||
ADD COLUMN `donation_balance` decimal(10,2) NOT NULL DEFAULT 0.00 AFTER `usr_affiliate`,
|
||||
ADD COLUMN `total_donations_received` decimal(10,2) NOT NULL DEFAULT 0.00 AFTER `donation_balance`,
|
||||
ADD COLUMN `total_donations_count` int(11) NOT NULL DEFAULT 0 AFTER `total_donations_received`,
|
||||
ADD COLUMN `rainforest_customer_id` varchar(255) DEFAULT NULL AFTER `total_donations_count`,
|
||||
ADD INDEX `idx_donation_balance` (`donation_balance`),
|
||||
ADD INDEX `idx_rainforest_customer_id` (`rainforest_customer_id`);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX `idx_donations_streamer_status` ON `donations` (`streamer_id`, `status`);
|
||||
CREATE INDEX `idx_donations_completed_at` ON `donations` (`completed_at`);
|
||||
CREATE INDEX `idx_payouts_streamer_status` ON `payouts` (`streamer_id`, `status`);
|
||||
|
||||
-- Insert default donation goals (optional)
|
||||
INSERT INTO `donation_goals` (`streamer_id`, `title`, `description`, `target_amount`)
|
||||
SELECT `usr_id`, 'Support My Content', 'Help me create better content for you!', 100.00
|
||||
FROM `db_accountuser`
|
||||
WHERE `usr_id` IN (1, 2, 3) -- Replace with actual streamer IDs
|
||||
ON DUPLICATE KEY UPDATE `id` = `id`;
|
||||
|
||||
-- Create views for easy reporting
|
||||
CREATE OR REPLACE VIEW `donation_summary` AS
|
||||
SELECT
|
||||
d.streamer_id,
|
||||
u.usr_user as streamer_username,
|
||||
u.usr_dname as streamer_display_name,
|
||||
COUNT(d.id) as total_donations,
|
||||
SUM(d.amount) as total_amount,
|
||||
SUM(d.streamer_amount) as total_earned,
|
||||
SUM(d.platform_fee) as total_fees,
|
||||
AVG(d.amount) as average_donation,
|
||||
MAX(d.amount) as largest_donation,
|
||||
MIN(d.created_at) as first_donation,
|
||||
MAX(d.completed_at) as latest_donation
|
||||
FROM `donations` d
|
||||
JOIN `db_accountuser` u ON d.streamer_id = u.usr_id
|
||||
WHERE d.status = 'completed'
|
||||
GROUP BY d.streamer_id;
|
||||
|
||||
CREATE OR REPLACE VIEW `monthly_donation_stats` AS
|
||||
SELECT
|
||||
d.streamer_id,
|
||||
u.usr_user as streamer_username,
|
||||
YEAR(d.completed_at) as year,
|
||||
MONTH(d.completed_at) as month,
|
||||
COUNT(d.id) as donations_count,
|
||||
SUM(d.amount) as total_amount,
|
||||
SUM(d.streamer_amount) as streamer_earnings,
|
||||
SUM(d.platform_fee) as platform_fees
|
||||
FROM `donations` d
|
||||
JOIN `db_accountuser` u ON d.streamer_id = u.usr_id
|
||||
WHERE d.status = 'completed'
|
||||
GROUP BY d.streamer_id, YEAR(d.completed_at), MONTH(d.completed_at)
|
||||
ORDER BY year DESC, month DESC;
|
||||
|
||||
-- Insert sample configuration data
|
||||
INSERT INTO `db_settings` (`setting_key`, `setting_value`, `setting_description`) VALUES
|
||||
('rainforest_pay_enabled', '1', 'Enable Rainforest Pay integration'),
|
||||
('rainforest_pay_environment', 'sandbox', 'Rainforest Pay environment (sandbox/production)'),
|
||||
('rainforest_pay_min_donation', '1.00', 'Minimum donation amount'),
|
||||
('rainforest_pay_max_donation', '10000.00', 'Maximum donation amount'),
|
||||
('rainforest_pay_platform_fee', '2.5', 'Platform fee percentage'),
|
||||
('rainforest_pay_platform_fee_fixed', '0.30', 'Fixed platform fee amount')
|
||||
ON DUPLICATE KEY UPDATE `setting_value` = VALUES(`setting_value`);
|
||||
|
||||
-- Create triggers for automatic balance updates
|
||||
DELIMITER $$
|
||||
|
||||
CREATE TRIGGER `update_streamer_balance_after_donation`
|
||||
AFTER UPDATE ON `donations`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.status = 'completed' AND OLD.status != 'completed' THEN
|
||||
UPDATE `db_accountuser`
|
||||
SET
|
||||
`donation_balance` = `donation_balance` + NEW.streamer_amount,
|
||||
`total_donations_received` = `total_donations_received` + NEW.amount,
|
||||
`total_donations_count` = `total_donations_count` + 1
|
||||
WHERE `usr_id` = NEW.streamer_id;
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
CREATE TRIGGER `update_streamer_balance_after_payout`
|
||||
AFTER UPDATE ON `payouts`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.status = 'completed' AND OLD.status != 'completed' THEN
|
||||
UPDATE `db_accountuser`
|
||||
SET `donation_balance` = `donation_balance` - (NEW.amount + NEW.fee)
|
||||
WHERE `usr_id` = NEW.streamer_id;
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- Grant necessary permissions (adjust as needed)
|
||||
-- GRANT SELECT, INSERT, UPDATE ON `donations` TO 'easystream_user'@'localhost';
|
||||
-- GRANT SELECT, INSERT, UPDATE ON `payouts` TO 'easystream_user'@'localhost';
|
||||
-- GRANT SELECT, INSERT, UPDATE ON `streamer_payout_settings` TO 'easystream_user'@'localhost';
|
||||
|
||||
COMMIT;
|
||||
157
f_modules/m_frontend/m_donations/install_token_system.sql
Normal file
157
f_modules/m_frontend/m_donations/install_token_system.sql
Normal file
@@ -0,0 +1,157 @@
|
||||
-- Token System Database Tables
|
||||
-- Complete monetization system with Rainforest Pay integration
|
||||
|
||||
-- Token purchases table
|
||||
CREATE TABLE IF NOT EXISTS token_purchases (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
usd_amount DECIMAL(10,2) NOT NULL,
|
||||
token_amount INT NOT NULL,
|
||||
exchange_rate DECIMAL(10,4) NOT NULL,
|
||||
payment_id VARCHAR(255) NULL,
|
||||
status ENUM('pending', 'completed', 'failed', 'cancelled') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_payment_id (payment_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_created_at (created_at)
|
||||
);
|
||||
|
||||
-- Token redemptions table
|
||||
CREATE TABLE IF NOT EXISTS token_redemptions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
token_amount INT NOT NULL,
|
||||
gross_usd DECIMAL(10,2) NOT NULL,
|
||||
platform_fee DECIMAL(10,2) NOT NULL,
|
||||
net_usd DECIMAL(10,2) NOT NULL,
|
||||
redemption_method ENUM('bank_transfer', 'mobile_money', 'paypal') NOT NULL,
|
||||
payout_id INT NULL,
|
||||
status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP NULL,
|
||||
failed_at TIMESTAMP NULL,
|
||||
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_payout_id (payout_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_created_at (created_at)
|
||||
);
|
||||
|
||||
-- Token payouts table (Rainforest Pay integration)
|
||||
CREATE TABLE IF NOT EXISTS token_payouts (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
redemption_id INT NOT NULL,
|
||||
user_id INT NOT NULL,
|
||||
usd_amount DECIMAL(10,2) NOT NULL,
|
||||
rainforest_payout_id VARCHAR(255) NULL,
|
||||
status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending',
|
||||
failure_reason TEXT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP NULL,
|
||||
failed_at TIMESTAMP NULL,
|
||||
|
||||
INDEX idx_redemption_id (redemption_id),
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_rainforest_payout_id (rainforest_payout_id),
|
||||
INDEX idx_status (status),
|
||||
|
||||
FOREIGN KEY (redemption_id) REFERENCES token_redemptions(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- User payout settings table
|
||||
CREATE TABLE IF NOT EXISTS user_payout_settings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL UNIQUE,
|
||||
payout_method ENUM('bank_transfer', 'mobile_money', 'paypal') NOT NULL,
|
||||
payout_details JSON NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_payout_method (payout_method)
|
||||
);
|
||||
|
||||
-- Token packages configuration (stored in db_settings)
|
||||
INSERT INTO db_settings (setting_key, setting_value, setting_description) VALUES
|
||||
('token_package_small', '{"tokens": 100, "bonus": 10, "usd": 10, "popular": false}', 'Small token package'),
|
||||
('token_package_medium', '{"tokens": 500, "bonus": 75, "usd": 50, "popular": true}', 'Medium token package (most popular)'),
|
||||
('token_package_large', '{"tokens": 1000, "bonus": 200, "usd": 100, "popular": false}', 'Large token package'),
|
||||
('token_package_mega', '{"tokens": 2500, "bonus": 750, "usd": 250, "popular": false}', 'Mega token package')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
setting_value = VALUES(setting_value),
|
||||
setting_description = VALUES(setting_description);
|
||||
|
||||
-- Update token settings with monetization features
|
||||
UPDATE db_settings SET setting_value = JSON_SET(
|
||||
COALESCE(setting_value, '{}'),
|
||||
'$.min_purchase', 5,
|
||||
'$.max_purchase', 1000,
|
||||
'$.min_redemption', 100,
|
||||
'$.platform_fee_rate', 0.05,
|
||||
'$.exchange_rate', 0.01
|
||||
) WHERE setting_key = 'token_settings';
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_token_transactions_user_type ON token_transactions(user_id, transaction_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_token_transactions_created ON token_transactions(created_at);
|
||||
|
||||
-- Add donation balance column to users table if not exists
|
||||
ALTER TABLE db_accountuser
|
||||
ADD COLUMN IF NOT EXISTS donation_balance DECIMAL(10,2) DEFAULT 0.00,
|
||||
ADD COLUMN IF NOT EXISTS token_balance INT DEFAULT 0;
|
||||
|
||||
-- Create view for user token balances
|
||||
CREATE OR REPLACE VIEW user_token_balances AS
|
||||
SELECT
|
||||
u.usr_id as user_id,
|
||||
u.usr_user as username,
|
||||
COALESCE(SUM(CASE WHEN tt.transaction_type IN ('purchase', 'gift_received', 'refund') THEN tt.amount ELSE 0 END), 0) -
|
||||
COALESCE(SUM(CASE WHEN tt.transaction_type IN ('gift_sent', 'redemption', 'spend') THEN tt.amount ELSE 0 END), 0) as token_balance,
|
||||
COUNT(CASE WHEN tt.transaction_type = 'purchase' THEN 1 END) as total_purchases,
|
||||
COALESCE(SUM(CASE WHEN tt.transaction_type = 'purchase' THEN tt.amount END), 0) as total_purchased_tokens,
|
||||
COUNT(CASE WHEN tt.transaction_type = 'redemption' THEN 1 END) as total_redemptions,
|
||||
COALESCE(SUM(CASE WHEN tt.transaction_type = 'redemption' THEN tt.amount END), 0) as total_redeemed_tokens
|
||||
FROM db_accountuser u
|
||||
LEFT JOIN token_transactions tt ON u.usr_id = tt.user_id
|
||||
GROUP BY u.usr_id, u.usr_user;
|
||||
|
||||
-- Create view for token purchase analytics
|
||||
CREATE OR REPLACE VIEW token_purchase_analytics AS
|
||||
SELECT
|
||||
DATE(tp.created_at) as purchase_date,
|
||||
COUNT(*) as total_purchases,
|
||||
SUM(tp.usd_amount) as total_usd,
|
||||
SUM(tp.token_amount) as total_tokens,
|
||||
AVG(tp.usd_amount) as avg_purchase_amount,
|
||||
COUNT(DISTINCT tp.user_id) as unique_buyers
|
||||
FROM token_purchases tp
|
||||
WHERE tp.status = 'completed'
|
||||
GROUP BY DATE(tp.created_at)
|
||||
ORDER BY purchase_date DESC;
|
||||
|
||||
-- Create view for token redemption analytics
|
||||
CREATE OR REPLACE VIEW token_redemption_analytics AS
|
||||
SELECT
|
||||
DATE(tr.created_at) as redemption_date,
|
||||
COUNT(*) as total_redemptions,
|
||||
SUM(tr.token_amount) as total_tokens_redeemed,
|
||||
SUM(tr.gross_usd) as total_gross_usd,
|
||||
SUM(tr.platform_fee) as total_platform_fees,
|
||||
SUM(tr.net_usd) as total_net_usd,
|
||||
AVG(tr.token_amount) as avg_redemption_amount,
|
||||
COUNT(DISTINCT tr.user_id) as unique_redeemers
|
||||
FROM token_redemptions tr
|
||||
WHERE tr.status = 'completed'
|
||||
GROUP BY DATE(tr.created_at)
|
||||
ORDER BY redemption_date DESC;
|
||||
|
||||
-- Sample data for testing (remove in production)
|
||||
-- INSERT INTO token_purchases (user_id, usd_amount, token_amount, exchange_rate, status) VALUES
|
||||
-- (1, 10.00, 110, 0.01, 'completed'),
|
||||
-- (2, 50.00, 575, 0.01, 'completed'),
|
||||
-- (3, 100.00, 1200, 0.01, 'pending');
|
||||
|
||||
COMMIT;
|
||||
111
f_modules/m_frontend/m_donations/process_donation.php
Normal file
111
f_modules/m_frontend/m_donations/process_donation.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
// Load Square configuration
|
||||
$square_config = require_once __DIR__ . '/config.square.php';
|
||||
|
||||
// Include Square SDK
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Square\SquareClient;
|
||||
use Square\Environment;
|
||||
use Square\Models\CreatePaymentRequest;
|
||||
use Square\Models\Money;
|
||||
|
||||
// Set JSON response header
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Get POST data
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$data) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Invalid request data'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['streamer_id']) || !isset($data['amount'])) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Missing required fields'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate amount
|
||||
if ($data['amount'] < $square_config['square']['min_donation'] ||
|
||||
$data['amount'] > $square_config['square']['max_donation']) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Invalid donation amount'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// Initialize Square client
|
||||
$square_client = new SquareClient([
|
||||
'accessToken' => $square_config['square']['access_token'],
|
||||
'environment' => $square_config['square']['environment'] === 'production' ? Environment::PRODUCTION : Environment::SANDBOX
|
||||
]);
|
||||
|
||||
// Create payment request
|
||||
$money = new Money();
|
||||
$money->setAmount($data['amount'] * 100); // Convert to cents
|
||||
$money->setCurrency($square_config['square']['currency']);
|
||||
|
||||
$payment_request = new CreatePaymentRequest();
|
||||
$payment_request->setSourceId('EXTERNAL');
|
||||
$payment_request->setAmountMoney($money);
|
||||
$payment_request->setLocationId($square_config['square']['location_id']);
|
||||
|
||||
// Add metadata
|
||||
$payment_request->setMetadata([
|
||||
'streamer_id' => $data['streamer_id'],
|
||||
'donor_name' => $data['donor_name'] ?? '',
|
||||
'message' => $data['message'] ?? ''
|
||||
]);
|
||||
|
||||
// Create payment
|
||||
$payment = $square_client->getPaymentsApi()->createPayment($payment_request);
|
||||
|
||||
if ($payment->isSuccess()) {
|
||||
// Record donation in database
|
||||
$sql = "INSERT INTO donations (
|
||||
streamer_id, amount, donor_name, message, payment_id, status, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, 'pending', NOW())";
|
||||
|
||||
$params = [
|
||||
$data['streamer_id'],
|
||||
$data['amount'],
|
||||
$data['donor_name'] ?? '',
|
||||
$data['message'] ?? '',
|
||||
$payment->getResult()->getPayment()->getId()
|
||||
];
|
||||
|
||||
$class_database->executeQuery($sql, $params);
|
||||
|
||||
// Get payment URL
|
||||
$payment_url = $payment->getResult()->getPayment()->getPaymentUrl();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'payment_url' => $payment_url,
|
||||
'message' => 'Payment created successfully'
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Failed to create payment: ' . $payment->getErrors()[0]->getDetail()
|
||||
]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Error processing donation: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
24
f_modules/m_frontend/m_donations/public/donate.php
Normal file
24
f_modules/m_frontend/m_donations/public/donate.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
use Donations\DonationHandler;
|
||||
|
||||
// Get streamer information
|
||||
$streamer_id = $_GET['streamer_id'] ?? 0;
|
||||
$sql = "SELECT username, display_name FROM users WHERE user_id = ?";
|
||||
$streamer = db()->getRow($sql, [$streamer_id]);
|
||||
|
||||
if (!$streamer) {
|
||||
handle_error('Invalid streamer', 404);
|
||||
}
|
||||
|
||||
// Initialize donation handler
|
||||
$donation_handler = new DonationHandler();
|
||||
|
||||
// Load view
|
||||
view('donation_form', [
|
||||
'streamer' => $streamer,
|
||||
'config' => $square_config
|
||||
]);
|
||||
69
f_modules/m_frontend/m_donations/public/overlay.php
Normal file
69
f_modules/m_frontend/m_donations/public/overlay.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
$usr_key = $class_filter->clr_str($_GET['u']);
|
||||
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
?><!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Donations Overlay</title>
|
||||
<style>
|
||||
html,body{margin:0;height:100%;background:transparent;overflow:hidden}
|
||||
.ov-wrap{position:relative;width:100%;height:100%;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#fff}
|
||||
.event{position:absolute;left:16px;bottom:16px;background:rgba(0,0,0,.55);padding:12px 14px;border-radius:8px;backdrop-filter:saturate(150%) blur(6px);box-shadow:0 6px 18px rgba(0,0,0,.3)}
|
||||
.event .name{font-weight:600}
|
||||
.event .amount{color:#6cf;margin-left:8px}
|
||||
.goal{position:absolute;right:16px;top:16px;background:rgba(0,0,0,.55);padding:10px 12px;border-radius:8px}
|
||||
.bar{width:220px;height:8px;background:rgba(255,255,255,.2);border-radius:6px;margin-top:8px;overflow:hidden}
|
||||
.fill{height:100%;background:#6cf;width:0%}
|
||||
</style>
|
||||
<script>const uKey = <?php echo json_encode($usr_key); ?>;</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ov-wrap">
|
||||
<div class="goal">
|
||||
<div id="goal-title">Goal</div>
|
||||
<div class="bar"><div id="goal-fill" class="fill"></div></div>
|
||||
</div>
|
||||
<div id="event" class="event" style="display:none"></div>
|
||||
</div>
|
||||
<script>
|
||||
async function tick(){
|
||||
try{
|
||||
const res = await fetch('../api/overlay_status.php?u='+encodeURIComponent(uKey), {cache:'no-store'});
|
||||
const j = await res.json();
|
||||
const ev = document.getElementById('event');
|
||||
if (j && j.recent && j.recent[0]){
|
||||
const r = j.recent[0];
|
||||
ev.innerHTML = `<span class="name">${r.name || 'Someone'}</span> <span class="amount">$${Number(r.amount||0).toFixed(2)}</span><div>${r.message?r.message:''}</div>`;
|
||||
ev.style.display='block';
|
||||
}
|
||||
const pct = Math.max(0, Math.min(100, j.goal && j.goal.target>0 ? (j.goal.raised/j.goal.target*100) : 0));
|
||||
document.getElementById('goal-fill').style.width = pct+'%';
|
||||
document.getElementById('goal-title').textContent = j.goal && j.goal.title ? j.goal.title : 'Goal';
|
||||
}catch(e){/* noop */}
|
||||
}
|
||||
tick(); setInterval(tick, 5000);
|
||||
</script>
|
||||
</body>
|
||||
/html>
|
||||
|
||||
35
f_modules/m_frontend/m_donations/public/process_donation.php
Normal file
35
f_modules/m_frontend/m_donations/public/process_donation.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
use Donations\DonationHandler;
|
||||
|
||||
// Get POST data
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$data) {
|
||||
json_response([
|
||||
'success' => false,
|
||||
'message' => 'Invalid request data'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['streamer_id']) || !isset($data['amount'])) {
|
||||
json_response([
|
||||
'success' => false,
|
||||
'message' => 'Missing required fields'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Process donation
|
||||
$handler = new DonationHandler();
|
||||
$result = $handler->createDonation(
|
||||
$data['streamer_id'],
|
||||
$data['amount'],
|
||||
$data['donor_name'] ?? '',
|
||||
$data['message'] ?? ''
|
||||
);
|
||||
|
||||
json_response($result);
|
||||
50
f_modules/m_frontend/m_donations/public/process_payment.php
Normal file
50
f_modules/m_frontend/m_donations/public/process_payment.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
use Donations\DonationHandler;
|
||||
|
||||
// Get JSON input
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$input) {
|
||||
handle_error('Invalid request data', 400);
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
$required_fields = ['nonce', 'amount', 'streamer_id'];
|
||||
foreach ($required_fields as $field) {
|
||||
if (!isset($input[$field]) || empty($input[$field])) {
|
||||
handle_error("Missing required field: {$field}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize donation handler
|
||||
$donation_handler = new DonationHandler();
|
||||
|
||||
try {
|
||||
// Process the donation
|
||||
$result = $donation_handler->createDonation(
|
||||
$input['streamer_id'],
|
||||
$input['amount'],
|
||||
$input['nonce'],
|
||||
$input['message'] ?? null
|
||||
);
|
||||
|
||||
// Return success response
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Donation processed successfully',
|
||||
'donation_id' => $result['donation_id']
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Log error
|
||||
error_log("Donation processing error: " . $e->getMessage());
|
||||
|
||||
// Return error response
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
9
f_modules/m_frontend/m_donations/public/webhook.php
Normal file
9
f_modules/m_frontend/m_donations/public/webhook.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
use Donations\WebhookHandler;
|
||||
|
||||
$handler = new WebhookHandler();
|
||||
$handler->handle();
|
||||
722
f_modules/m_frontend/m_donations/rainforest_donation_form.php
Normal file
722
f_modules/m_frontend/m_donations/rainforest_donation_form.php
Normal file
@@ -0,0 +1,722 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Rainforest Pay Donation Form for EasyStream
|
||||
| User-friendly donation interface with Rainforest Pay integration
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
// Load Rainforest Pay handler
|
||||
require_once __DIR__ . '/rainforest_pay.php';
|
||||
|
||||
$error_message = '';
|
||||
$success_message = '';
|
||||
$streamer_id = isset($_GET['streamer']) ? intval($_GET['streamer']) : 0;
|
||||
|
||||
if (!$streamer_id) {
|
||||
$error_message = 'Invalid streamer ID';
|
||||
}
|
||||
|
||||
// Get streamer information
|
||||
$streamer = null;
|
||||
if ($streamer_id) {
|
||||
$sql = "SELECT usr_id, usr_user, usr_dname, ch_title, usr_photo
|
||||
FROM db_accountuser WHERE usr_id = ? AND active = 1";
|
||||
$result = $class_database->execute($sql, [$streamer_id]);
|
||||
$streamer = $result->fields ?? null;
|
||||
}
|
||||
|
||||
if (!$streamer) {
|
||||
$error_message = 'Streamer not found';
|
||||
}
|
||||
|
||||
// Initialize Rainforest Pay handler
|
||||
$rainforest = new RainforestPayHandler($class_database);
|
||||
$payment_methods = $rainforest->getPaymentMethods();
|
||||
|
||||
// Handle form submission
|
||||
if ($_POST && !$error_message) {
|
||||
$amount = floatval($_POST['amount'] ?? 0);
|
||||
$donor_name = $class_filter->clr_str($_POST['donor_name'] ?? '');
|
||||
$message = $class_filter->clr_str($_POST['message'] ?? '');
|
||||
$payment_method = $class_filter->clr_str($_POST['payment_method'] ?? 'card');
|
||||
|
||||
if ($amount <= 0) {
|
||||
$error_message = 'Please enter a valid donation amount';
|
||||
} else {
|
||||
$result = $rainforest->createDonation($streamer_id, $amount, $donor_name, $message, $payment_method);
|
||||
|
||||
if ($result['success']) {
|
||||
// Redirect to payment URL if provided
|
||||
if (isset($result['payment_url'])) {
|
||||
header('Location: ' . $result['payment_url']);
|
||||
exit;
|
||||
} else {
|
||||
$success_message = $result['message'];
|
||||
}
|
||||
} else {
|
||||
$error_message = $result['message'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config = require __DIR__ . '/config.rainforest.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Donate to <?php echo htmlspecialchars($streamer['ch_title'] ?: $streamer['usr_dname'] ?: $streamer['usr_user']); ?> - EasyStream</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.donation-container {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.donation-header {
|
||||
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.streamer-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid white;
|
||||
margin: 0 auto 1rem;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.streamer-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.donation-subtitle {
|
||||
opacity: 0.9;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.donation-form {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: #28a745;
|
||||
box-shadow: 0 0 0 3px rgba(40,167,69,0.1);
|
||||
}
|
||||
|
||||
.amount-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.amount-btn {
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.amount-btn:hover {
|
||||
border-color: #28a745;
|
||||
background: #f8fff9;
|
||||
}
|
||||
|
||||
.amount-btn.active {
|
||||
border-color: #28a745;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.payment-methods {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.payment-method {
|
||||
padding: 1rem;
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.payment-method:hover {
|
||||
border-color: #28a745;
|
||||
background: #f8fff9;
|
||||
}
|
||||
|
||||
.payment-method.active {
|
||||
border-color: #28a745;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.payment-method-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.payment-method-name {
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.donate-btn {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.donate-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(40,167,69,0.3);
|
||||
}
|
||||
|
||||
.donate-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.donation-info {
|
||||
background: #e3f2fd;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.875rem;
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
text-align: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.back-link a {
|
||||
color: #6c757d;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.back-link a:hover {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.donation-container {
|
||||
width: 95%;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.donation-form {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.amount-buttons {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.payment-methods {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="donation-container">
|
||||
<div class="donation-header">
|
||||
<img src="<?php echo $streamer['usr_photo'] ?: '/f_templates/tpl_frontend/img/default-avatar.png'; ?>"
|
||||
alt="<?php echo htmlspecialchars($streamer['usr_user']); ?>"
|
||||
class="streamer-avatar">
|
||||
<h1 class="streamer-name">
|
||||
<?php echo htmlspecialchars($streamer['ch_title'] ?: $streamer['usr_dname'] ?: $streamer['usr_user']); ?>
|
||||
</h1>
|
||||
<p class="donation-subtitle">Support this creator with a donation</p>
|
||||
</div>
|
||||
|
||||
<div class="donation-form">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="error-message">
|
||||
<?php echo htmlspecialchars($error_message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($success_message): ?>
|
||||
<div class="success-message">
|
||||
<?php echo htmlspecialchars($success_message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$error_message || $streamer): ?>
|
||||
<div class="donation-info">
|
||||
💡 Your donation helps support content creation.
|
||||
Platform fee: <?php echo $config['rainforest']['platform_fee_percentage']; ?>% +
|
||||
$<?php echo $config['rainforest']['platform_fee_fixed']; ?>
|
||||
</div>
|
||||
|
||||
<form method="POST" id="donation-form">
|
||||
<!-- Donation Type Selection -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">Donation Type</label>
|
||||
<div class="donation-type-selector">
|
||||
<button type="button" class="donation-type-btn active" data-type="usd">
|
||||
💵 USD Donation
|
||||
</button>
|
||||
<button type="button" class="donation-type-btn" data-type="tokens">
|
||||
🪙 Token Donation
|
||||
</button>
|
||||
</div>
|
||||
<input type="hidden" name="donation_type" id="donation_type" value="usd">
|
||||
</div>
|
||||
|
||||
<!-- USD Donation Section -->
|
||||
<div class="form-group" id="usd-section">
|
||||
<label class="form-label">Donation Amount (USD)</label>
|
||||
<div class="amount-buttons">
|
||||
<button type="button" class="amount-btn" data-amount="5">$5</button>
|
||||
<button type="button" class="amount-btn" data-amount="10">$10</button>
|
||||
<button type="button" class="amount-btn" data-amount="25">$25</button>
|
||||
<button type="button" class="amount-btn" data-amount="50">$50</button>
|
||||
<button type="button" class="amount-btn" data-amount="100">$100</button>
|
||||
<button type="button" class="amount-btn" data-amount="custom">Custom</button>
|
||||
</div>
|
||||
<input type="number"
|
||||
name="amount"
|
||||
id="amount"
|
||||
class="form-input"
|
||||
placeholder="Enter amount"
|
||||
min="<?php echo $config['rainforest']['min_donation']; ?>"
|
||||
max="<?php echo $config['rainforest']['max_donation']; ?>"
|
||||
step="0.01"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<!-- Token Donation Section -->
|
||||
<div class="form-group" id="token-section" style="display: none;">
|
||||
<label class="form-label">Token Amount</label>
|
||||
<div class="token-info">
|
||||
<img src="/f_templates/tpl_frontend/img/default-token.svg" alt="Token" style="width: 24px; height: 24px; margin-right: 8px;">
|
||||
<span>Send EasyCoins directly from your balance</span>
|
||||
</div>
|
||||
<div class="token-buttons">
|
||||
<button type="button" class="token-btn" data-tokens="10">10 EC</button>
|
||||
<button type="button" class="token-btn" data-tokens="25">25 EC</button>
|
||||
<button type="button" class="token-btn" data-tokens="50">50 EC</button>
|
||||
<button type="button" class="token-btn" data-tokens="100">100 EC</button>
|
||||
<button type="button" class="token-btn" data-tokens="250">250 EC</button>
|
||||
<button type="button" class="token-btn" data-tokens="custom">Custom</button>
|
||||
</div>
|
||||
<input type="number"
|
||||
name="token_amount"
|
||||
id="token_amount"
|
||||
class="form-input"
|
||||
placeholder="Enter token amount"
|
||||
min="1"
|
||||
step="1">
|
||||
<div class="user-balance" id="user-balance" style="margin-top: 10px; font-size: 0.9em; color: #666;">
|
||||
Your balance: <span id="balance-amount">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Your Name (Optional)</label>
|
||||
<input type="text"
|
||||
name="donor_name"
|
||||
class="form-input"
|
||||
placeholder="Enter your name or stay anonymous">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Message (Optional)</label>
|
||||
<textarea name="message"
|
||||
class="form-input"
|
||||
rows="3"
|
||||
placeholder="Leave a message for the creator"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Payment Method</label>
|
||||
<div class="payment-methods">
|
||||
<?php foreach ($payment_methods as $method): ?>
|
||||
<div class="payment-method" data-method="<?php echo $method['id']; ?>">
|
||||
<div class="payment-method-icon">
|
||||
<?php
|
||||
$icons = [
|
||||
'card' => '💳',
|
||||
'bank_transfer' => '🏦',
|
||||
'mobile_money' => '📱',
|
||||
'wallet' => '👛'
|
||||
];
|
||||
echo $icons[$method['id']] ?? '💳';
|
||||
?>
|
||||
</div>
|
||||
<div class="payment-method-name"><?php echo $method['name']; ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<input type="hidden" name="payment_method" id="payment_method" value="card" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="donate-btn" id="donate-btn">
|
||||
💖 Donate Now
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="back-link">
|
||||
<a href="javascript:history.back()">← Back to channel</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Amount button handling
|
||||
document.querySelectorAll('.amount-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
document.querySelectorAll('.amount-btn').forEach(b => b.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const amount = this.dataset.amount;
|
||||
const amountInput = document.getElementById('amount');
|
||||
|
||||
if (amount === 'custom') {
|
||||
amountInput.focus();
|
||||
} else {
|
||||
amountInput.value = amount;
|
||||
}
|
||||
|
||||
updateDonateButton();
|
||||
});
|
||||
});
|
||||
|
||||
// Payment method handling
|
||||
document.querySelectorAll('.payment-method').forEach(method => {
|
||||
method.addEventListener('click', function() {
|
||||
document.querySelectorAll('.payment-method').forEach(m => m.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
document.getElementById('payment_method').value = this.dataset.method;
|
||||
});
|
||||
});
|
||||
|
||||
// Set default payment method
|
||||
document.querySelector('.payment-method').classList.add('active');
|
||||
|
||||
// Amount input handling
|
||||
document.getElementById('amount').addEventListener('input', function() {
|
||||
document.querySelectorAll('.amount-btn').forEach(b => b.classList.remove('active'));
|
||||
updateDonateButton();
|
||||
});
|
||||
|
||||
function updateDonateButton() {
|
||||
const amount = parseFloat(document.getElementById('amount').value) || 0;
|
||||
const btn = document.getElementById('donate-btn');
|
||||
|
||||
if (amount > 0) {
|
||||
btn.textContent = `💖 Donate $${amount.toFixed(2)}`;
|
||||
btn.disabled = false;
|
||||
} else {
|
||||
btn.textContent = '💖 Donate Now';
|
||||
btn.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Form submission handling
|
||||
document.getElementById('donation-form').addEventListener('submit', function(e) {
|
||||
const amount = parseFloat(document.getElementById('amount').value) || 0;
|
||||
const minAmount = <?php echo $config['rainforest']['min_donation']; ?>;
|
||||
const maxAmount = <?php echo $config['rainforest']['max_donation']; ?>;
|
||||
|
||||
if (amount < minAmount || amount > maxAmount) {
|
||||
e.preventDefault();
|
||||
alert(`Donation amount must be between $${minAmount} and $${maxAmount}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
const btn = document.getElementById('donate-btn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Processing...';
|
||||
});
|
||||
|
||||
// Initialize
|
||||
updateDonateButton();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
/* Token System Styles */
|
||||
.donation-type-selector {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.donation-type-btn {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.donation-type-btn:hover {
|
||||
border-color: #28a745;
|
||||
background: #f8fff9;
|
||||
}
|
||||
|
||||
.donation-type-btn.active {
|
||||
border-color: #28a745;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.token-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.token-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.token-btn {
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #FFD700;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #FFD700, #FFA500);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.token-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(255,215,0,0.3);
|
||||
}
|
||||
|
||||
.token-btn.active {
|
||||
border-color: #B8860B;
|
||||
box-shadow: 0 0 0 3px rgba(255,215,0,0.2);
|
||||
}
|
||||
|
||||
.user-balance {
|
||||
background: #e3f2fd;
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.insufficient-balance {
|
||||
background: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.donation-type-selector {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.token-buttons {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced donation form JavaScript
|
||||
|
||||
// Donation type switching
|
||||
document.querySelectorAll('.donation-type-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
document.querySelectorAll('.donation-type-btn').forEach(b => b.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const type = this.dataset.type;
|
||||
document.getElementById('donation_type').value = type;
|
||||
|
||||
if (type === 'usd') {
|
||||
document.getElementById('usd-section').style.display = 'block';
|
||||
document.getElementById('token-section').style.display = 'none';
|
||||
document.getElementById('amount').required = true;
|
||||
document.getElementById('token_amount').required = false;
|
||||
} else {
|
||||
document.getElementById('usd-section').style.display = 'none';
|
||||
document.getElementById('token-section').style.display = 'block';
|
||||
document.getElementById('amount').required = false;
|
||||
document.getElementById('token_amount').required = true;
|
||||
loadUserBalance();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Token button handling
|
||||
document.querySelectorAll('.token-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
document.querySelectorAll('.token-btn').forEach(b => b.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const tokens = this.dataset.tokens;
|
||||
const tokenInput = document.getElementById('token_amount');
|
||||
|
||||
if (tokens === 'custom') {
|
||||
tokenInput.focus();
|
||||
} else {
|
||||
tokenInput.value = tokens;
|
||||
}
|
||||
|
||||
checkTokenBalance();
|
||||
});
|
||||
});
|
||||
|
||||
// Token amount input handling
|
||||
document.getElementById('token_amount').addEventListener('input', function() {
|
||||
document.querySelectorAll('.token-btn').forEach(b => b.classList.remove('active'));
|
||||
checkTokenBalance();
|
||||
});
|
||||
|
||||
function loadUserBalance() {
|
||||
// Load user's token balance via AJAX
|
||||
fetch('/api/user/token-balance')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const balanceElement = document.getElementById('balance-amount');
|
||||
if (data.success) {
|
||||
balanceElement.innerHTML = `<img src="/f_templates/tpl_frontend/img/default-token.svg" style="width: 16px; height: 16px; margin-right: 4px;">${data.balance} EasyCoins`;
|
||||
} else {
|
||||
balanceElement.textContent = 'Unable to load balance';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('balance-amount').textContent = 'Error loading balance';
|
||||
});
|
||||
}
|
||||
|
||||
function checkTokenBalance() {
|
||||
const tokenAmount = parseInt(document.getElementById('token_amount').value) || 0;
|
||||
const balanceElement = document.getElementById('user-balance');
|
||||
const donateBtn = document.getElementById('donate-btn');
|
||||
|
||||
// This would check against actual user balance
|
||||
// For now, we'll assume they have sufficient balance
|
||||
if (tokenAmount > 0) {
|
||||
balanceElement.classList.remove('insufficient-balance');
|
||||
donateBtn.disabled = false;
|
||||
donateBtn.textContent = `🪙 Donate ${tokenAmount} EasyCoins`;
|
||||
} else {
|
||||
donateBtn.textContent = '🪙 Donate Tokens';
|
||||
donateBtn.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the main donate button text based on type
|
||||
function updateDonateButton() {
|
||||
const donationType = document.getElementById('donation_type').value;
|
||||
const btn = document.getElementById('donate-btn');
|
||||
|
||||
if (donationType === 'tokens') {
|
||||
const tokenAmount = parseInt(document.getElementById('token_amount').value) || 0;
|
||||
if (tokenAmount > 0) {
|
||||
btn.textContent = `🪙 Donate ${tokenAmount} EasyCoins`;
|
||||
} else {
|
||||
btn.textContent = '🪙 Donate Tokens';
|
||||
}
|
||||
} else {
|
||||
const amount = parseFloat(document.getElementById('amount').value) || 0;
|
||||
if (amount > 0) {
|
||||
btn.textContent = `💖 Donate $${amount.toFixed(2)}`;
|
||||
} else {
|
||||
btn.textContent = '💖 Donate Now';
|
||||
}
|
||||
}
|
||||
}
|
||||
813
f_modules/m_frontend/m_donations/rainforest_pay.php
Normal file
813
f_modules/m_frontend/m_donations/rainforest_pay.php
Normal file
@@ -0,0 +1,813 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Rainforest Pay Integration for EasyStream
|
||||
| Complete payment processing with Rainforest Pay API
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
class RainforestPayHandler {
|
||||
private $config;
|
||||
private $class_database;
|
||||
private $api_base_url;
|
||||
private $headers;
|
||||
|
||||
public function __construct($class_database) {
|
||||
$this->class_database = $class_database;
|
||||
$this->config = require __DIR__ . '/config.rainforest.php';
|
||||
|
||||
$env = $this->config['rainforest']['environment'];
|
||||
$this->api_base_url = $this->config['rainforest']['api_base_url'][$env];
|
||||
|
||||
$this->headers = [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $this->config['rainforest']['api_key'],
|
||||
'X-Merchant-ID: ' . $this->config['rainforest']['merchant_id']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a donation payment
|
||||
*/
|
||||
public function createDonation($streamer_id, $amount, $donor_name = '', $message = '', $payment_method = 'card') {
|
||||
try {
|
||||
// Validate amount
|
||||
if ($amount < $this->config['rainforest']['min_donation'] ||
|
||||
$amount > $this->config['rainforest']['max_donation']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Invalid donation amount. Must be between $' .
|
||||
$this->config['rainforest']['min_donation'] . ' and $' .
|
||||
$this->config['rainforest']['max_donation']
|
||||
];
|
||||
}
|
||||
|
||||
// Get streamer information
|
||||
$streamer = $this->getStreamerInfo($streamer_id);
|
||||
if (!$streamer) {
|
||||
return ['success' => false, 'message' => 'Streamer not found'];
|
||||
}
|
||||
|
||||
// Calculate fees
|
||||
$platform_fee = $this->calculatePlatformFee($amount);
|
||||
$streamer_amount = $amount - $platform_fee;
|
||||
|
||||
// Create payment request
|
||||
$payment_data = [
|
||||
'amount' => $amount * 100, // Convert to cents
|
||||
'currency' => $this->config['rainforest']['currency'],
|
||||
'payment_method' => $payment_method,
|
||||
'description' => "Donation to {$streamer['username']}",
|
||||
'metadata' => [
|
||||
'type' => 'donation',
|
||||
'streamer_id' => $streamer_id,
|
||||
'streamer_username' => $streamer['username'],
|
||||
'donor_name' => $donor_name,
|
||||
'message' => $message,
|
||||
'platform_fee' => $platform_fee,
|
||||
'streamer_amount' => $streamer_amount
|
||||
],
|
||||
'webhook_url' => $this->config['rainforest']['webhook_url'],
|
||||
'return_url' => "https://{$_SERVER['HTTP_HOST']}/donations/success",
|
||||
'cancel_url' => "https://{$_SERVER['HTTP_HOST']}/donations/cancel"
|
||||
];
|
||||
|
||||
// Make API request
|
||||
$response = $this->makeApiRequest('POST', '/payments', $payment_data);
|
||||
|
||||
if ($response && isset($response['id'])) {
|
||||
// Store pending donation in database
|
||||
$donation_id = $this->storePendingDonation([
|
||||
'streamer_id' => $streamer_id,
|
||||
'amount' => $amount,
|
||||
'platform_fee' => $platform_fee,
|
||||
'streamer_amount' => $streamer_amount,
|
||||
'donor_name' => $donor_name,
|
||||
'message' => $message,
|
||||
'payment_method' => $payment_method,
|
||||
'rainforest_payment_id' => $response['id'],
|
||||
'status' => 'pending'
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'payment_id' => $response['id'],
|
||||
'donation_id' => $donation_id,
|
||||
'payment_url' => $response['payment_url'] ?? null,
|
||||
'message' => 'Payment created successfully'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Failed to create payment: ' . ($response['error'] ?? 'Unknown error')
|
||||
];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Create Donation Error', $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Error processing donation: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a payment (supports both donations and token purchases)
|
||||
*/
|
||||
public function createPayment($payment_data) {
|
||||
try {
|
||||
// Make API request
|
||||
$response = $this->makeApiRequest('POST', '/payments', $payment_data);
|
||||
|
||||
if ($response && isset($response['id'])) {
|
||||
return [
|
||||
'success' => true,
|
||||
'payment_id' => $response['id'],
|
||||
'payment_url' => $response['payment_url'] ?? null,
|
||||
'message' => 'Payment created successfully'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Failed to create payment: ' . ($response['error'] ?? 'Unknown error')
|
||||
];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Create Payment Error', $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Error creating payment: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a payout (for token redemptions)
|
||||
*/
|
||||
public function createPayout($payout_data) {
|
||||
try {
|
||||
// Make API request
|
||||
$response = $this->makeApiRequest('POST', '/payouts', $payout_data);
|
||||
|
||||
if ($response && isset($response['id'])) {
|
||||
return [
|
||||
'success' => true,
|
||||
'payout_id' => $response['id'],
|
||||
'message' => 'Payout created successfully'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Failed to create payout: ' . ($response['error'] ?? 'Unknown error')
|
||||
];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Create Payout Error', $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Error creating payout: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process webhook from Rainforest Pay
|
||||
*/
|
||||
public function processWebhook($payload, $signature) {
|
||||
try {
|
||||
// Verify webhook signature
|
||||
if (!$this->verifyWebhookSignature($payload, $signature)) {
|
||||
return ['success' => false, 'message' => 'Invalid webhook signature'];
|
||||
}
|
||||
|
||||
$data = json_decode($payload, true);
|
||||
if (!$data) {
|
||||
return ['success' => false, 'message' => 'Invalid webhook payload'];
|
||||
}
|
||||
|
||||
$event_type = $data['event'] ?? '';
|
||||
$payment_data = $data['data'] ?? [];
|
||||
|
||||
switch ($event_type) {
|
||||
case 'payment.completed':
|
||||
return $this->handlePaymentCompleted($payment_data);
|
||||
|
||||
case 'payment.failed':
|
||||
return $this->handlePaymentFailed($payment_data);
|
||||
|
||||
case 'payment.cancelled':
|
||||
return $this->handlePaymentCancelled($payment_data);
|
||||
|
||||
case 'payout.completed':
|
||||
return $this->handlePayoutCompleted($payment_data);
|
||||
|
||||
case 'payout.failed':
|
||||
return $this->handlePayoutFailed($payment_data);
|
||||
|
||||
default:
|
||||
$this->logError('Unknown Webhook Event', $event_type);
|
||||
return ['success' => false, 'message' => 'Unknown event type'];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Webhook Processing Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Webhook processing failed'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle completed payment
|
||||
*/
|
||||
private function handlePaymentCompleted($payment_data) {
|
||||
$payment_id = $payment_data['id'];
|
||||
$metadata = $payment_data['metadata'] ?? [];
|
||||
$payment_type = $metadata['type'] ?? 'donation';
|
||||
|
||||
try {
|
||||
if ($payment_type === 'token_purchase') {
|
||||
return $this->handleTokenPurchaseCompleted($payment_data, $metadata);
|
||||
} else {
|
||||
// Handle regular donation
|
||||
$sql = "UPDATE donations SET
|
||||
status = 'completed',
|
||||
completed_at = NOW(),
|
||||
rainforest_data = ?
|
||||
WHERE rainforest_payment_id = ?";
|
||||
|
||||
$this->class_database->execute($sql, [
|
||||
json_encode($payment_data),
|
||||
$payment_id
|
||||
]);
|
||||
|
||||
// Update streamer balance
|
||||
if (isset($metadata['streamer_id']) && isset($metadata['streamer_amount'])) {
|
||||
$this->updateStreamerBalance($metadata['streamer_id'], $metadata['streamer_amount']);
|
||||
}
|
||||
|
||||
// Send notification to streamer
|
||||
$this->sendDonationNotification($metadata);
|
||||
|
||||
return ['success' => true, 'message' => 'Payment completed successfully'];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Payment Completion Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Error updating payment status'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle completed token purchase
|
||||
*/
|
||||
private function handleTokenPurchaseCompleted($payment_data, $metadata) {
|
||||
$payment_id = $payment_data['id'];
|
||||
$purchase_id = $metadata['purchase_id'] ?? null;
|
||||
$user_id = $metadata['user_id'] ?? null;
|
||||
$token_amount = $metadata['token_amount'] ?? 0;
|
||||
|
||||
if (!$purchase_id || !$user_id || !$token_amount) {
|
||||
return ['success' => false, 'message' => 'Invalid token purchase metadata'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->class_database->beginTransaction();
|
||||
|
||||
// Update purchase record
|
||||
$sql = "UPDATE token_purchases SET status = 'completed', updated_at = NOW() WHERE id = ? AND payment_id = ?";
|
||||
$this->class_database->execute($sql, [$purchase_id, $payment_id]);
|
||||
|
||||
// Add tokens to user balance
|
||||
require_once '../../../f_core/f_classes/class.token.php';
|
||||
VToken::recordTransaction($user_id, $token_amount, 'purchase', "Token purchase #{$purchase_id}");
|
||||
|
||||
$this->class_database->commitTransaction();
|
||||
|
||||
$this->logInfo('Token Purchase Completed', "Purchase ID: {$purchase_id}, User: {$user_id}, Tokens: {$token_amount}");
|
||||
|
||||
return ['success' => true, 'message' => 'Token purchase completed successfully'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->class_database->rollbackTransaction();
|
||||
$this->logError('Token Purchase Completion Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Error completing token purchase'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle failed payment
|
||||
*/
|
||||
private function handlePaymentFailed($payment_data) {
|
||||
$payment_id = $payment_data['id'];
|
||||
|
||||
$sql = "UPDATE donations SET
|
||||
status = 'failed',
|
||||
failed_at = NOW(),
|
||||
failure_reason = ?,
|
||||
rainforest_data = ?
|
||||
WHERE rainforest_payment_id = ?";
|
||||
|
||||
$this->class_database->execute($sql, [
|
||||
$payment_data['failure_reason'] ?? 'Payment failed',
|
||||
json_encode($payment_data),
|
||||
$payment_id
|
||||
]);
|
||||
|
||||
return ['success' => true, 'message' => 'Payment failure recorded'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle completed payout
|
||||
*/
|
||||
private function handlePayoutCompleted($payout_data) {
|
||||
$payout_id = $payout_data['id'];
|
||||
$metadata = $payout_data['metadata'] ?? [];
|
||||
$payout_type = $metadata['type'] ?? 'streamer_payout';
|
||||
|
||||
try {
|
||||
if ($payout_type === 'token_redemption') {
|
||||
return $this->handleTokenRedemptionCompleted($payout_data, $metadata);
|
||||
} else {
|
||||
// Handle regular streamer payout
|
||||
$sql = "UPDATE payouts SET
|
||||
status = 'completed',
|
||||
completed_at = NOW(),
|
||||
rainforest_data = ?
|
||||
WHERE rainforest_payout_id = ?";
|
||||
|
||||
$this->class_database->execute($sql, [
|
||||
json_encode($payout_data),
|
||||
$payout_id
|
||||
]);
|
||||
|
||||
return ['success' => true, 'message' => 'Payout completed successfully'];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Payout Completion Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Error updating payout status'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle failed payout
|
||||
*/
|
||||
private function handlePayoutFailed($payout_data) {
|
||||
$payout_id = $payout_data['id'];
|
||||
$metadata = $payout_data['metadata'] ?? [];
|
||||
$payout_type = $metadata['type'] ?? 'streamer_payout';
|
||||
|
||||
try {
|
||||
if ($payout_type === 'token_redemption') {
|
||||
return $this->handleTokenRedemptionFailed($payout_data, $metadata);
|
||||
} else {
|
||||
// Handle regular streamer payout failure
|
||||
$sql = "UPDATE payouts SET
|
||||
status = 'failed',
|
||||
failed_at = NOW(),
|
||||
failure_reason = ?,
|
||||
rainforest_data = ?
|
||||
WHERE rainforest_payout_id = ?";
|
||||
|
||||
$this->class_database->execute($sql, [
|
||||
$payout_data['failure_reason'] ?? 'Payout failed',
|
||||
json_encode($payout_data),
|
||||
$payout_id
|
||||
]);
|
||||
|
||||
return ['success' => true, 'message' => 'Payout failure recorded'];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Payout Failure Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Error updating payout status'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle completed token redemption
|
||||
*/
|
||||
private function handleTokenRedemptionCompleted($payout_data, $metadata) {
|
||||
$payout_id = $payout_data['id'];
|
||||
$redemption_id = $metadata['redemption_id'] ?? null;
|
||||
$user_id = $metadata['user_id'] ?? null;
|
||||
|
||||
if (!$redemption_id || !$user_id) {
|
||||
return ['success' => false, 'message' => 'Invalid token redemption metadata'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->class_database->beginTransaction();
|
||||
|
||||
// Update payout record
|
||||
$sql = "UPDATE token_payouts SET status = 'completed', completed_at = NOW() WHERE rainforest_payout_id = ?";
|
||||
$this->class_database->execute($sql, [$payout_id]);
|
||||
|
||||
// Update redemption record
|
||||
$sql = "UPDATE token_redemptions SET status = 'completed', completed_at = NOW() WHERE id = ?";
|
||||
$this->class_database->execute($sql, [$redemption_id]);
|
||||
|
||||
$this->class_database->commitTransaction();
|
||||
|
||||
$this->logInfo('Token Redemption Completed', "Redemption ID: {$redemption_id}, User: {$user_id}");
|
||||
|
||||
return ['success' => true, 'message' => 'Token redemption completed successfully'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->class_database->rollbackTransaction();
|
||||
$this->logError('Token Redemption Completion Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Error completing token redemption'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle failed token redemption
|
||||
*/
|
||||
private function handleTokenRedemptionFailed($payout_data, $metadata) {
|
||||
$payout_id = $payout_data['id'];
|
||||
$redemption_id = $metadata['redemption_id'] ?? null;
|
||||
$user_id = $metadata['user_id'] ?? null;
|
||||
$token_amount = $metadata['token_amount'] ?? 0;
|
||||
|
||||
if (!$redemption_id || !$user_id || !$token_amount) {
|
||||
return ['success' => false, 'message' => 'Invalid token redemption metadata'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->class_database->beginTransaction();
|
||||
|
||||
// Update payout record
|
||||
$sql = "UPDATE token_payouts SET status = 'failed', failed_at = NOW(), failure_reason = ? WHERE rainforest_payout_id = ?";
|
||||
$this->class_database->execute($sql, [
|
||||
$payout_data['failure_reason'] ?? 'Payout failed',
|
||||
$payout_id
|
||||
]);
|
||||
|
||||
// Update redemption record
|
||||
$sql = "UPDATE token_redemptions SET status = 'failed', failed_at = NOW() WHERE id = ?";
|
||||
$this->class_database->execute($sql, [$redemption_id]);
|
||||
|
||||
// Refund tokens to user
|
||||
require_once '../../../f_core/f_classes/class.token.php';
|
||||
VToken::recordTransaction($user_id, $token_amount, 'refund', "Redemption refund #{$redemption_id}");
|
||||
|
||||
$this->class_database->commitTransaction();
|
||||
|
||||
$this->logInfo('Token Redemption Failed - Refunded', "Redemption ID: {$redemption_id}, User: {$user_id}, Tokens: {$token_amount}");
|
||||
|
||||
return ['success' => true, 'message' => 'Token redemption failed - tokens refunded'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->class_database->rollbackTransaction();
|
||||
$this->logError('Token Redemption Failure Error', $e->getMessage());
|
||||
return ['success' => false, 'message' => 'Error handling token redemption failure'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request payout for streamer
|
||||
*/
|
||||
public function requestPayout($streamer_id) {
|
||||
try {
|
||||
$balance = $this->getStreamerBalance($streamer_id);
|
||||
|
||||
if ($balance < $this->config['rainforest']['min_payout']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Insufficient balance for payout. Minimum: $' .
|
||||
$this->config['rainforest']['min_payout']
|
||||
];
|
||||
}
|
||||
|
||||
// Get streamer payout details
|
||||
$streamer = $this->getStreamerPayoutInfo($streamer_id);
|
||||
if (!$streamer || !$streamer['payout_method']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Payout method not configured. Please update your payout settings.'
|
||||
];
|
||||
}
|
||||
|
||||
// Calculate payout amount after fees
|
||||
$fee_amount = $this->calculatePayoutFee($balance);
|
||||
$payout_amount = $balance - $fee_amount;
|
||||
|
||||
// Create payout request
|
||||
$payout_data = [
|
||||
'amount' => $payout_amount * 100, // Convert to cents
|
||||
'currency' => $this->config['rainforest']['currency'],
|
||||
'recipient' => [
|
||||
'type' => $streamer['payout_method'],
|
||||
'details' => json_decode($streamer['payout_details'], true)
|
||||
],
|
||||
'description' => "Payout to {$streamer['username']}",
|
||||
'metadata' => [
|
||||
'type' => 'streamer_payout',
|
||||
'streamer_id' => $streamer_id,
|
||||
'original_balance' => $balance,
|
||||
'fee_amount' => $fee_amount,
|
||||
'payout_amount' => $payout_amount
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->makeApiRequest('POST', '/payouts', $payout_data);
|
||||
|
||||
if ($response && isset($response['id'])) {
|
||||
// Record payout request
|
||||
$this->recordPayoutRequest($streamer_id, $payout_amount, $fee_amount, $response['id']);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'payout_id' => $response['id'],
|
||||
'amount' => $payout_amount,
|
||||
'fee' => $fee_amount,
|
||||
'message' => 'Payout request submitted successfully'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Failed to create payout: ' . ($response['error'] ?? 'Unknown error')
|
||||
];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Payout Request Error', $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Error processing payout: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available payment methods
|
||||
*/
|
||||
public function getPaymentMethods() {
|
||||
$methods = [];
|
||||
$config_methods = $this->config['rainforest']['payment_methods'];
|
||||
|
||||
if ($config_methods['card']) {
|
||||
$methods[] = [
|
||||
'id' => 'card',
|
||||
'name' => 'Credit/Debit Card',
|
||||
'icon' => 'credit-card',
|
||||
'description' => 'Visa, Mastercard, American Express'
|
||||
];
|
||||
}
|
||||
|
||||
if ($config_methods['bank_transfer']) {
|
||||
$methods[] = [
|
||||
'id' => 'bank_transfer',
|
||||
'name' => 'Bank Transfer',
|
||||
'icon' => 'bank',
|
||||
'description' => 'Direct bank transfer'
|
||||
];
|
||||
}
|
||||
|
||||
if ($config_methods['mobile_money']) {
|
||||
$methods[] = [
|
||||
'id' => 'mobile_money',
|
||||
'name' => 'Mobile Money',
|
||||
'icon' => 'mobile',
|
||||
'description' => 'MTN, Airtel, Vodafone'
|
||||
];
|
||||
}
|
||||
|
||||
if ($config_methods['wallet']) {
|
||||
$methods[] = [
|
||||
'id' => 'wallet',
|
||||
'name' => 'Digital Wallet',
|
||||
'icon' => 'wallet',
|
||||
'description' => 'PayPal, Apple Pay, Google Pay'
|
||||
];
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get donation history for streamer
|
||||
*/
|
||||
public function getDonationHistory($streamer_id, $limit = 20, $offset = 0) {
|
||||
$sql = "SELECT d.*, u.usr_user as donor_username
|
||||
FROM donations d
|
||||
LEFT JOIN db_accountuser u ON d.donor_id = u.usr_id
|
||||
WHERE d.streamer_id = ? AND d.status = 'completed'
|
||||
ORDER BY d.completed_at DESC
|
||||
LIMIT ? OFFSET ?";
|
||||
|
||||
return $this->class_database->getRows($sql, [$streamer_id, $limit, $offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get streamer earnings summary
|
||||
*/
|
||||
public function getStreamerEarnings($streamer_id, $period = '30_days') {
|
||||
$date_condition = $this->getDateCondition($period);
|
||||
|
||||
$sql = "SELECT
|
||||
COUNT(*) as total_donations,
|
||||
SUM(amount) as total_amount,
|
||||
SUM(streamer_amount) as total_earned,
|
||||
SUM(platform_fee) as total_fees,
|
||||
AVG(amount) as average_donation
|
||||
FROM donations
|
||||
WHERE streamer_id = ? AND status = 'completed' AND $date_condition";
|
||||
|
||||
return $this->class_database->getRow($sql, [$streamer_id]);
|
||||
}
|
||||
|
||||
// Helper Methods
|
||||
|
||||
private function makeApiRequest($method, $endpoint, $data = null) {
|
||||
$url = $this->api_base_url . $endpoint;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => $this->headers,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_SSL_VERIFYPEER => true
|
||||
]);
|
||||
|
||||
if ($method === 'POST' && $data) {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception('API request failed');
|
||||
}
|
||||
|
||||
$decoded = json_decode($response, true);
|
||||
|
||||
if ($http_code >= 400) {
|
||||
$this->logError('API Error', "HTTP $http_code: " . ($decoded['message'] ?? $response));
|
||||
return ['error' => $decoded['message'] ?? 'API request failed'];
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
private function verifyWebhookSignature($payload, $signature) {
|
||||
$expected = hash_hmac('sha256', $payload, $this->config['rainforest']['webhook_secret']);
|
||||
return hash_equals($expected, $signature);
|
||||
}
|
||||
|
||||
private function calculatePlatformFee($amount) {
|
||||
$percentage_fee = $amount * ($this->config['rainforest']['platform_fee_percentage'] / 100);
|
||||
$fixed_fee = $this->config['rainforest']['platform_fee_fixed'];
|
||||
return $percentage_fee + $fixed_fee;
|
||||
}
|
||||
|
||||
private function calculatePayoutFee($amount) {
|
||||
$percentage_fee = $amount * ($this->config['rainforest']['payout_fee_percentage'] / 100);
|
||||
$fixed_fee = $this->config['rainforest']['payout_fee_fixed'];
|
||||
return $percentage_fee + $fixed_fee;
|
||||
}
|
||||
|
||||
private function getStreamerInfo($streamer_id) {
|
||||
$sql = "SELECT usr_id, usr_user as username, usr_dname as display_name
|
||||
FROM db_accountuser WHERE usr_id = ?";
|
||||
return $this->class_database->getRow($sql, [$streamer_id]);
|
||||
}
|
||||
|
||||
private function getStreamerBalance($streamer_id) {
|
||||
$sql = "SELECT COALESCE(SUM(streamer_amount), 0) as balance
|
||||
FROM donations
|
||||
WHERE streamer_id = ? AND status = 'completed' AND payout_id IS NULL";
|
||||
$result = $this->class_database->getRow($sql, [$streamer_id]);
|
||||
return $result['balance'] ?? 0;
|
||||
}
|
||||
|
||||
private function updateStreamerBalance($streamer_id, $amount) {
|
||||
// Balance is calculated dynamically, but we can update a cached value if needed
|
||||
$sql = "UPDATE db_accountuser SET
|
||||
donation_balance = COALESCE(donation_balance, 0) + ?
|
||||
WHERE usr_id = ?";
|
||||
$this->class_database->execute($sql, [$amount, $streamer_id]);
|
||||
}
|
||||
|
||||
private function storePendingDonation($data) {
|
||||
$sql = "INSERT INTO donations (
|
||||
streamer_id, amount, platform_fee, streamer_amount, donor_name,
|
||||
message, payment_method, rainforest_payment_id, status, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
|
||||
|
||||
$this->class_database->execute($sql, [
|
||||
$data['streamer_id'], $data['amount'], $data['platform_fee'],
|
||||
$data['streamer_amount'], $data['donor_name'], $data['message'],
|
||||
$data['payment_method'], $data['rainforest_payment_id'], $data['status']
|
||||
]);
|
||||
|
||||
return $this->class_database->lastInsertId();
|
||||
}
|
||||
|
||||
private function recordPayoutRequest($streamer_id, $amount, $fee, $payout_id) {
|
||||
$sql = "INSERT INTO payouts (
|
||||
streamer_id, amount, fee, rainforest_payout_id, status, created_at
|
||||
) VALUES (?, ?, ?, ?, 'pending', NOW())";
|
||||
|
||||
$this->class_database->execute($sql, [$streamer_id, $amount, $fee, $payout_id]);
|
||||
|
||||
// Mark donations as paid out
|
||||
$sql = "UPDATE donations SET payout_id = ?
|
||||
WHERE streamer_id = ? AND status = 'completed' AND payout_id IS NULL";
|
||||
$this->class_database->execute($sql, [$payout_id, $streamer_id]);
|
||||
}
|
||||
|
||||
private function logError($type, $message) {
|
||||
error_log(date('Y-m-d H:i:s') . " [RainforestPay] ERROR - $type: $message\n", 3, 'logs/rainforest_pay.log');
|
||||
}
|
||||
|
||||
private function logInfo($type, $message) {
|
||||
error_log(date('Y-m-d H:i:s') . " [RainforestPay] INFO - $type: $message\n", 3, 'logs/rainforest_pay.log');
|
||||
}
|
||||
|
||||
private function sendDonationNotification($metadata) {
|
||||
// Integration with EasyStream notification system
|
||||
// This would trigger notifications to the streamer about new donations
|
||||
}
|
||||
|
||||
private function getDateCondition($period) {
|
||||
switch ($period) {
|
||||
case '7_days':
|
||||
return "completed_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
|
||||
case '30_days':
|
||||
return "completed_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
|
||||
case '90_days':
|
||||
return "completed_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)";
|
||||
case '1_year':
|
||||
return "completed_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR)";
|
||||
default:
|
||||
return "completed_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize handler if accessed directly
|
||||
if (basename(__FILE__) == basename($_SERVER['SCRIPT_NAME'])) {
|
||||
$handler = new RainforestPayHandler($class_database);
|
||||
|
||||
// Handle different actions
|
||||
$action = $_GET['action'] ?? $_POST['action'] ?? '';
|
||||
|
||||
switch ($action) {
|
||||
case 'create_donation':
|
||||
$result = $handler->createDonation(
|
||||
$_POST['streamer_id'],
|
||||
floatval($_POST['amount']),
|
||||
$_POST['donor_name'] ?? '',
|
||||
$_POST['message'] ?? '',
|
||||
$_POST['payment_method'] ?? 'card'
|
||||
);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
break;
|
||||
|
||||
case 'webhook':
|
||||
$payload = file_get_contents('php://input');
|
||||
$signature = $_SERVER['HTTP_X_RAINFOREST_SIGNATURE'] ?? '';
|
||||
$result = $handler->processWebhook($payload, $signature);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
break;
|
||||
|
||||
case 'request_payout':
|
||||
$result = $handler->requestPayout($_POST['streamer_id']);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
break;
|
||||
|
||||
case 'get_payment_methods':
|
||||
$methods = $handler->getPaymentMethods();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true, 'methods' => $methods]);
|
||||
break;
|
||||
|
||||
case 'create_payment':
|
||||
$result = $handler->createPayment($_POST);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
break;
|
||||
|
||||
case 'create_payout':
|
||||
$result = $handler->createPayout($_POST);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
break;
|
||||
|
||||
default:
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid action']);
|
||||
}
|
||||
}
|
||||
?>
|
||||
55
f_modules/m_frontend/m_donations/rainforest_webhook.php
Normal file
55
f_modules/m_frontend/m_donations/rainforest_webhook.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Rainforest Pay Webhook Handler for EasyStream
|
||||
| Secure webhook processing for payment notifications
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
include_once '../../../f_core/config.core.php';
|
||||
|
||||
// Load Rainforest Pay handler
|
||||
require_once __DIR__ . '/rainforest_pay.php';
|
||||
|
||||
// Set response headers
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
// Get webhook payload
|
||||
$payload = file_get_contents('php://input');
|
||||
$signature = $_SERVER['HTTP_X_RAINFOREST_SIGNATURE'] ?? '';
|
||||
|
||||
// Log webhook received
|
||||
error_log(date('Y-m-d H:i:s') . " [RainforestPay] Webhook received\n", 3, 'logs/rainforest_webhooks.log');
|
||||
|
||||
if (empty($payload)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Empty payload']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($signature)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing signature']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Initialize handler and process webhook
|
||||
$handler = new RainforestPayHandler($class_database);
|
||||
$result = $handler->processWebhook($payload, $signature);
|
||||
|
||||
if ($result['success']) {
|
||||
http_response_code(200);
|
||||
echo json_encode(['status' => 'success', 'message' => $result['message']]);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo json_encode(['status' => 'error', 'message' => $result['message']]);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Log error
|
||||
error_log(date('Y-m-d H:i:s') . " [RainforestPay] Webhook error: " . $e->getMessage() . "\n", 3, 'logs/rainforest_webhooks.log');
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Internal server error']);
|
||||
}
|
||||
?>
|
||||
26
f_modules/m_frontend/m_donations/sql/api_keys.sql
Normal file
26
f_modules/m_frontend/m_donations/sql/api_keys.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- Create API Keys Table
|
||||
CREATE TABLE api_keys (
|
||||
key_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
api_key VARCHAR(64) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
last_used TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
-- Create API Rate Limiting Table
|
||||
CREATE TABLE api_rate_limits (
|
||||
rate_limit_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
api_key VARCHAR(64) NOT NULL,
|
||||
request_count INT DEFAULT 1,
|
||||
window_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (api_key) REFERENCES api_keys(api_key)
|
||||
);
|
||||
|
||||
-- Create Indexes
|
||||
CREATE INDEX idx_api_key_active ON api_keys(api_key, is_active);
|
||||
CREATE INDEX idx_rate_limit_window ON api_rate_limits(api_key, window_start);
|
||||
59
f_modules/m_frontend/m_donations/sql/donation_features.sql
Normal file
59
f_modules/m_frontend/m_donations/sql/donation_features.sql
Normal file
@@ -0,0 +1,59 @@
|
||||
-- Donation Goals Table
|
||||
CREATE TABLE donation_goals (
|
||||
goal_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
target_amount DECIMAL(10,2) NOT NULL,
|
||||
current_amount DECIMAL(10,2) DEFAULT 0.00,
|
||||
start_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
end_date TIMESTAMP NULL,
|
||||
status ENUM('active', 'completed', 'cancelled') DEFAULT 'active',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
-- Donation Milestones Table
|
||||
CREATE TABLE donation_milestones (
|
||||
milestone_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
goal_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
target_amount DECIMAL(10,2) NOT NULL,
|
||||
reward_description TEXT,
|
||||
is_achieved BOOLEAN DEFAULT FALSE,
|
||||
achieved_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (goal_id) REFERENCES donation_goals(goal_id)
|
||||
);
|
||||
|
||||
-- Donation Analytics Table
|
||||
CREATE TABLE donation_analytics (
|
||||
analytics_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
total_donations INT DEFAULT 0,
|
||||
total_amount DECIMAL(10,2) DEFAULT 0.00,
|
||||
average_donation DECIMAL(10,2) DEFAULT 0.00,
|
||||
unique_donors INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id),
|
||||
UNIQUE KEY unique_streamer_date (streamer_id, date)
|
||||
);
|
||||
|
||||
-- Donation Notifications Table
|
||||
CREATE TABLE donation_notifications (
|
||||
notification_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
donor_id INT,
|
||||
type ENUM('donation', 'goal_created', 'goal_completed', 'milestone_achieved') NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (donor_id) REFERENCES users(user_id)
|
||||
);
|
||||
143
f_modules/m_frontend/m_donations/sql/install.sql
Normal file
143
f_modules/m_frontend/m_donations/sql/install.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
-- Create tables
|
||||
CREATE TABLE IF NOT EXISTS donations (
|
||||
donation_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
donor_id INT NOT NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
payment_id VARCHAR(255) NOT NULL,
|
||||
status ENUM('pending', 'completed', 'failed', 'refunded') DEFAULT 'pending',
|
||||
message TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (donor_id) REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS donation_goals (
|
||||
goal_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
target_amount DECIMAL(10,2) NOT NULL,
|
||||
current_amount DECIMAL(10,2) DEFAULT 0.00,
|
||||
start_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
end_date TIMESTAMP NULL,
|
||||
status ENUM('active', 'completed', 'cancelled') DEFAULT 'active',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS donation_milestones (
|
||||
milestone_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
goal_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
target_amount DECIMAL(10,2) NOT NULL,
|
||||
reward_description TEXT,
|
||||
is_completed BOOLEAN DEFAULT FALSE,
|
||||
completed_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (goal_id) REFERENCES donation_goals(goal_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS donation_analytics (
|
||||
analytics_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
total_donations INT DEFAULT 0,
|
||||
total_amount DECIMAL(10,2) DEFAULT 0.00,
|
||||
average_donation DECIMAL(10,2) DEFAULT 0.00,
|
||||
unique_donors INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id),
|
||||
UNIQUE KEY unique_streamer_date (streamer_id, date)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS donation_notifications (
|
||||
notification_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
streamer_id INT NOT NULL,
|
||||
type ENUM('donation', 'goal', 'milestone', 'payout') NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
read_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (streamer_id) REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_keys (
|
||||
key_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
api_key VARCHAR(64) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
last_used TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_rate_limits (
|
||||
rate_limit_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
api_key VARCHAR(64) NOT NULL,
|
||||
request_count INT DEFAULT 1,
|
||||
window_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (api_key) REFERENCES api_keys(api_key)
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX idx_donations_streamer ON donations(streamer_id);
|
||||
CREATE INDEX idx_donations_donor ON donations(donor_id);
|
||||
CREATE INDEX idx_donations_status ON donations(status);
|
||||
CREATE INDEX idx_goals_streamer ON donation_goals(streamer_id);
|
||||
CREATE INDEX idx_goals_status ON donation_goals(status);
|
||||
CREATE INDEX idx_milestones_goal ON donation_milestones(goal_id);
|
||||
CREATE INDEX idx_analytics_streamer_date ON donation_analytics(streamer_id, date);
|
||||
CREATE INDEX idx_notifications_streamer ON donation_notifications(streamer_id);
|
||||
CREATE INDEX idx_notifications_read ON donation_notifications(is_read);
|
||||
CREATE INDEX idx_api_key_active ON api_keys(api_key, is_active);
|
||||
CREATE INDEX idx_rate_limit_window ON api_rate_limits(api_key, window_start);
|
||||
|
||||
-- Create views
|
||||
CREATE OR REPLACE VIEW v_streamer_donations AS
|
||||
SELECT d.*, u.username as donor_username, u.display_name as donor_display_name
|
||||
FROM donations d JOIN users u ON d.donor_id = u.user_id;
|
||||
|
||||
CREATE OR REPLACE VIEW v_streamer_goals AS
|
||||
SELECT g.*,
|
||||
COUNT(m.milestone_id) as milestone_count,
|
||||
SUM(CASE WHEN m.is_completed = 1 THEN 1 ELSE 0 END) as completed_milestones
|
||||
FROM donation_goals g
|
||||
LEFT JOIN donation_milestones m ON g.goal_id = m.goal_id
|
||||
GROUP BY g.goal_id;
|
||||
|
||||
-- Create triggers
|
||||
DELIMITER //
|
||||
|
||||
CREATE TRIGGER tr_update_goal_amount
|
||||
AFTER INSERT ON donations
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE donation_goals
|
||||
SET current_amount = current_amount + NEW.amount
|
||||
WHERE streamer_id = NEW.streamer_id
|
||||
AND status = 'active'
|
||||
AND (end_date IS NULL OR end_date > NOW());
|
||||
END//
|
||||
|
||||
CREATE TRIGGER tr_check_milestone_completion
|
||||
AFTER UPDATE ON donation_goals
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE donation_milestones
|
||||
SET is_completed = 1, completed_at = NOW()
|
||||
WHERE goal_id = NEW.goal_id
|
||||
AND is_completed = 0
|
||||
AND target_amount <= NEW.current_amount;
|
||||
END//
|
||||
|
||||
DELIMITER ;
|
||||
110
f_modules/m_frontend/m_donations/src/AnalyticsHandler.php
Normal file
110
f_modules/m_frontend/m_donations/src/AnalyticsHandler.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace Donations;
|
||||
|
||||
class AnalyticsHandler {
|
||||
private $db;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = db();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update analytics for a new donation
|
||||
*/
|
||||
public function updateAnalytics($streamer_id, $amount, $donor_id) {
|
||||
$date = date('Y-m-d');
|
||||
|
||||
// Get or create analytics record for today
|
||||
$sql = "INSERT INTO donation_analytics
|
||||
(streamer_id, date, total_donations, total_amount, unique_donors)
|
||||
VALUES (?, ?, 1, ?, 1)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
total_donations = total_donations + 1,
|
||||
total_amount = total_amount + ?,
|
||||
unique_donors = (
|
||||
SELECT COUNT(DISTINCT donor_id)
|
||||
FROM donations
|
||||
WHERE streamer_id = ? AND DATE(created_at) = ?
|
||||
)";
|
||||
|
||||
$this->db->query($sql, [$streamer_id, $date, $amount, $amount, $streamer_id, $date]);
|
||||
|
||||
// Update average donation
|
||||
$sql = "UPDATE donation_analytics
|
||||
SET average_donation = total_amount / total_donations
|
||||
WHERE streamer_id = ? AND date = ?";
|
||||
|
||||
$this->db->query($sql, [$streamer_id, $date]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get analytics for a specific period
|
||||
*/
|
||||
public function getAnalytics($streamer_id, $start_date, $end_date) {
|
||||
$sql = "SELECT
|
||||
date,
|
||||
total_donations,
|
||||
total_amount,
|
||||
average_donation,
|
||||
unique_donors
|
||||
FROM donation_analytics
|
||||
WHERE streamer_id = ?
|
||||
AND date BETWEEN ? AND ?
|
||||
ORDER BY date DESC";
|
||||
|
||||
return $this->db->getRows($sql, [$streamer_id, $start_date, $end_date]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get summary statistics
|
||||
*/
|
||||
public function getSummary($streamer_id) {
|
||||
$sql = "SELECT
|
||||
COUNT(*) as total_donations,
|
||||
SUM(amount) as total_amount,
|
||||
AVG(amount) as average_donation,
|
||||
COUNT(DISTINCT donor_id) as unique_donors,
|
||||
MAX(amount) as largest_donation,
|
||||
MIN(amount) as smallest_donation
|
||||
FROM donations
|
||||
WHERE streamer_id = ?";
|
||||
|
||||
return $this->db->getRow($sql, [$streamer_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get top donors
|
||||
*/
|
||||
public function getTopDonors($streamer_id, $limit = 10) {
|
||||
$sql = "SELECT
|
||||
u.username,
|
||||
u.display_name,
|
||||
COUNT(*) as donation_count,
|
||||
SUM(d.amount) as total_amount
|
||||
FROM donations d
|
||||
JOIN users u ON d.donor_id = u.user_id
|
||||
WHERE d.streamer_id = ?
|
||||
GROUP BY d.donor_id
|
||||
ORDER BY total_amount DESC
|
||||
LIMIT ?";
|
||||
|
||||
return $this->db->getRows($sql, [$streamer_id, $limit]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get donation trends
|
||||
*/
|
||||
public function getTrends($streamer_id, $days = 30) {
|
||||
$sql = "SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as count,
|
||||
SUM(amount) as total
|
||||
FROM donations
|
||||
WHERE streamer_id = ?
|
||||
AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date";
|
||||
|
||||
return $this->db->getRows($sql, [$streamer_id, $days]);
|
||||
}
|
||||
}
|
||||
160
f_modules/m_frontend/m_donations/src/ApiKeyManager.php
Normal file
160
f_modules/m_frontend/m_donations/src/ApiKeyManager.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace Donations;
|
||||
|
||||
class ApiKeyManager {
|
||||
private $db;
|
||||
private $rate_limit = 100; // requests per minute
|
||||
private $rate_window = 60; // seconds
|
||||
|
||||
public function __construct() {
|
||||
$this->db = db();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new API key
|
||||
*/
|
||||
public function generateKey($user_id, $name, $description = '') {
|
||||
$api_key = bin2hex(random_bytes(32));
|
||||
|
||||
$sql = "INSERT INTO api_keys (user_id, api_key, name, description)
|
||||
VALUES (?, ?, ?, ?)";
|
||||
|
||||
$this->db->query($sql, [$user_id, $api_key, $name, $description]);
|
||||
|
||||
return $api_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an API key
|
||||
*/
|
||||
public function validateKey($api_key) {
|
||||
$sql = "SELECT user_id FROM api_keys
|
||||
WHERE api_key = ? AND is_active = 1";
|
||||
|
||||
$result = $this->db->getRow($sql, [$api_key]);
|
||||
|
||||
if ($result) {
|
||||
$this->updateLastUsed($api_key);
|
||||
return $result['user_id'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update last used timestamp
|
||||
*/
|
||||
private function updateLastUsed($api_key) {
|
||||
$sql = "UPDATE api_keys
|
||||
SET last_used = CURRENT_TIMESTAMP
|
||||
WHERE api_key = ?";
|
||||
|
||||
$this->db->query($sql, [$api_key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check rate limit
|
||||
*/
|
||||
public function checkRateLimit($api_key) {
|
||||
$sql = "SELECT request_count, window_start
|
||||
FROM api_rate_limits
|
||||
WHERE api_key = ?
|
||||
ORDER BY window_start DESC
|
||||
LIMIT 1";
|
||||
|
||||
$result = $this->db->getRow($sql, [$api_key]);
|
||||
|
||||
if (!$result) {
|
||||
$this->createRateLimitWindow($api_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
$window_start = strtotime($result['window_start']);
|
||||
$current_time = time();
|
||||
|
||||
// If window has expired, create new window
|
||||
if ($current_time - $window_start >= $this->rate_window) {
|
||||
$this->createRateLimitWindow($api_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if rate limit exceeded
|
||||
if ($result['request_count'] >= $this->rate_limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Increment request count
|
||||
$sql = "UPDATE api_rate_limits
|
||||
SET request_count = request_count + 1
|
||||
WHERE api_key = ? AND window_start = ?";
|
||||
|
||||
$this->db->query($sql, [$api_key, $result['window_start']]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new rate limit window
|
||||
*/
|
||||
private function createRateLimitWindow($api_key) {
|
||||
$sql = "INSERT INTO api_rate_limits (api_key, request_count, window_start)
|
||||
VALUES (?, 1, CURRENT_TIMESTAMP)";
|
||||
|
||||
$this->db->query($sql, [$api_key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's API keys
|
||||
*/
|
||||
public function getUserKeys($user_id) {
|
||||
$sql = "SELECT key_id, api_key, name, description, is_active,
|
||||
last_used, created_at
|
||||
FROM api_keys
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC";
|
||||
|
||||
return $this->db->getRows($sql, [$user_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate API key
|
||||
*/
|
||||
public function deactivateKey($key_id, $user_id) {
|
||||
$sql = "UPDATE api_keys
|
||||
SET is_active = 0
|
||||
WHERE key_id = ? AND user_id = ?";
|
||||
|
||||
return $this->db->query($sql, [$key_id, $user_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactivate API key
|
||||
*/
|
||||
public function reactivateKey($key_id, $user_id) {
|
||||
$sql = "UPDATE api_keys
|
||||
SET is_active = 1
|
||||
WHERE key_id = ? AND user_id = ?";
|
||||
|
||||
return $this->db->query($sql, [$key_id, $user_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete API key
|
||||
*/
|
||||
public function deleteKey($key_id, $user_id) {
|
||||
$sql = "DELETE FROM api_keys
|
||||
WHERE key_id = ? AND user_id = ?";
|
||||
|
||||
return $this->db->query($sql, [$key_id, $user_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old rate limit records
|
||||
*/
|
||||
public function cleanupRateLimits() {
|
||||
$sql = "DELETE FROM api_rate_limits
|
||||
WHERE window_start < DATE_SUB(NOW(), INTERVAL 1 HOUR)";
|
||||
|
||||
return $this->db->query($sql);
|
||||
}
|
||||
}
|
||||
146
f_modules/m_frontend/m_donations/src/Core/Api.php
Normal file
146
f_modules/m_frontend/m_donations/src/Core/Api.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Api {
|
||||
protected $config;
|
||||
protected $db;
|
||||
protected $logger;
|
||||
protected $request;
|
||||
protected $response;
|
||||
|
||||
public function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->db = db();
|
||||
$this->logger = new Logger();
|
||||
$this->request = $this->parseRequest();
|
||||
$this->response = [
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'data' => null
|
||||
];
|
||||
}
|
||||
|
||||
protected function parseRequest() {
|
||||
$request = [
|
||||
'method' => $_SERVER['REQUEST_METHOD'],
|
||||
'path' => parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH),
|
||||
'query' => $_GET,
|
||||
'body' => json_decode(file_get_contents('php://input'), true) ?? [],
|
||||
'headers' => getallheaders()
|
||||
];
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
protected function validateApiKey() {
|
||||
$apiKey = $this->request['headers']['X-API-Key'] ?? null;
|
||||
|
||||
if (!$apiKey) {
|
||||
$this->error('API key is required', 401);
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1";
|
||||
$key = $this->db->fetch($sql, [$apiKey]);
|
||||
|
||||
if (!$key) {
|
||||
$this->error('Invalid API key', 401);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->checkRateLimit($key['id'])) {
|
||||
$this->error('Rate limit exceeded', 429);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkRateLimit($apiKeyId) {
|
||||
$timeframe = $this->config['api']['rate_limit']['timeframe'];
|
||||
$maxRequests = $this->config['api']['rate_limit']['max_requests'];
|
||||
|
||||
$sql = "SELECT COUNT(*) as count FROM api_rate_limits
|
||||
WHERE api_key_id = ? AND created_at >= DATE_SUB(NOW(), INTERVAL ? SECOND)";
|
||||
|
||||
$result = $this->db->fetch($sql, [$apiKeyId, $timeframe]);
|
||||
|
||||
if ($result['count'] >= $maxRequests) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO api_rate_limits (api_key_id) VALUES (?)";
|
||||
$this->db->execute($sql, [$apiKeyId]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function success($message, $data = null) {
|
||||
$this->response = [
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'data' => $data
|
||||
];
|
||||
$this->sendResponse();
|
||||
}
|
||||
|
||||
protected function error($message, $code = 400) {
|
||||
$this->response = [
|
||||
'success' => false,
|
||||
'message' => $message,
|
||||
'data' => null
|
||||
];
|
||||
$this->sendResponse($code);
|
||||
}
|
||||
|
||||
protected function sendResponse($code = 200) {
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($this->response);
|
||||
exit;
|
||||
}
|
||||
|
||||
protected function validateInput($data, $rules) {
|
||||
$errors = [];
|
||||
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (!isset($data[$field]) && strpos($rule, 'required') !== false) {
|
||||
$errors[$field] = "The $field field is required.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($data[$field])) {
|
||||
if (strpos($rule, 'numeric') !== false && !is_numeric($data[$field])) {
|
||||
$errors[$field] = "The $field must be a number.";
|
||||
}
|
||||
|
||||
if (strpos($rule, 'min:') !== false) {
|
||||
$min = substr($rule, strpos($rule, 'min:') + 4);
|
||||
if ($data[$field] < $min) {
|
||||
$errors[$field] = "The $field must be at least $min.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'max:') !== false) {
|
||||
$max = substr($rule, strpos($rule, 'max:') + 4);
|
||||
if ($data[$field] > $max) {
|
||||
$errors[$field] = "The $field must not be greater than $max.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$this->error('Validation failed', 422);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function sanitizeInput($input) {
|
||||
if (is_array($input)) {
|
||||
return array_map([$this, 'sanitizeInput'], $input);
|
||||
}
|
||||
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
||||
208
f_modules/m_frontend/m_donations/src/Core/Cache.php
Normal file
208
f_modules/m_frontend/m_donations/src/Core/Cache.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Cache {
|
||||
private static $instance = null;
|
||||
private $config;
|
||||
private $cache;
|
||||
|
||||
private function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function connect() {
|
||||
try {
|
||||
$this->cache = new \Memcached();
|
||||
$this->cache->addServer(
|
||||
$this->config['cache']['host'],
|
||||
$this->config['cache']['port']
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception("Cache connection failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key) {
|
||||
$value = $this->cache->get($key);
|
||||
return $value !== false ? $value : null;
|
||||
}
|
||||
|
||||
public function set($key, $value, $ttl = 3600) {
|
||||
return $this->cache->set($key, $value, time() + $ttl);
|
||||
}
|
||||
|
||||
public function delete($key) {
|
||||
return $this->cache->delete($key);
|
||||
}
|
||||
|
||||
public function increment($key, $value = 1) {
|
||||
return $this->cache->increment($key, $value);
|
||||
}
|
||||
|
||||
public function decrement($key, $value = 1) {
|
||||
return $this->cache->decrement($key, $value);
|
||||
}
|
||||
|
||||
public function flush() {
|
||||
return $this->cache->flush();
|
||||
}
|
||||
|
||||
public function getMulti($keys) {
|
||||
return $this->cache->getMulti($keys);
|
||||
}
|
||||
|
||||
public function setMulti($items, $ttl = 3600) {
|
||||
return $this->cache->setMulti($items, time() + $ttl);
|
||||
}
|
||||
|
||||
public function deleteMulti($keys) {
|
||||
return $this->cache->deleteMulti($keys);
|
||||
}
|
||||
|
||||
public function remember($key, $ttl, $callback) {
|
||||
$value = $this->get($key);
|
||||
|
||||
if ($value !== null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = $callback();
|
||||
$this->set($key, $value, $ttl);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function rememberForever($key, $callback) {
|
||||
return $this->remember($key, 0, $callback);
|
||||
}
|
||||
|
||||
public function has($key) {
|
||||
return $this->get($key) !== null;
|
||||
}
|
||||
|
||||
public function missing($key) {
|
||||
return !$this->has($key);
|
||||
}
|
||||
|
||||
public function pull($key) {
|
||||
$value = $this->get($key);
|
||||
$this->delete($key);
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function put($key, $value, $ttl = 3600) {
|
||||
return $this->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
public function add($key, $value, $ttl = 3600) {
|
||||
return $this->cache->add($key, $value, time() + $ttl);
|
||||
}
|
||||
|
||||
public function forever($key, $value) {
|
||||
return $this->set($key, $value, 0);
|
||||
}
|
||||
|
||||
public function forget($key) {
|
||||
return $this->delete($key);
|
||||
}
|
||||
|
||||
public function tags($names) {
|
||||
return new TaggedCache($this, (array) $names);
|
||||
}
|
||||
|
||||
public function flushTags($names) {
|
||||
foreach ((array) $names as $name) {
|
||||
$this->delete("tag:{$name}:keys");
|
||||
}
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
$this->cache = null;
|
||||
}
|
||||
|
||||
private function __clone() {}
|
||||
private function __wakeup() {}
|
||||
}
|
||||
|
||||
class TaggedCache {
|
||||
private $cache;
|
||||
private $names;
|
||||
|
||||
public function __construct($cache, $names) {
|
||||
$this->cache = $cache;
|
||||
$this->names = $names;
|
||||
}
|
||||
|
||||
public function get($key) {
|
||||
return $this->cache->get($this->taggedItemKey($key));
|
||||
}
|
||||
|
||||
public function put($key, $value, $ttl = 3600) {
|
||||
$this->cache->set($this->taggedItemKey($key), $value, $ttl);
|
||||
$this->pushKey($key);
|
||||
}
|
||||
|
||||
public function remember($key, $ttl, $callback) {
|
||||
$value = $this->get($key);
|
||||
|
||||
if ($value !== null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = $callback();
|
||||
$this->put($key, $value, $ttl);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function rememberForever($key, $callback) {
|
||||
return $this->remember($key, 0, $callback);
|
||||
}
|
||||
|
||||
public function forget($key) {
|
||||
$this->cache->delete($this->taggedItemKey($key));
|
||||
$this->pullKey($key);
|
||||
}
|
||||
|
||||
public function flush() {
|
||||
foreach ($this->names as $name) {
|
||||
$this->cache->flushTags($name);
|
||||
}
|
||||
}
|
||||
|
||||
private function taggedItemKey($key) {
|
||||
return implode(':', array_merge($this->names, [$key]));
|
||||
}
|
||||
|
||||
private function pushKey($key) {
|
||||
foreach ($this->names as $name) {
|
||||
$keys = $this->cache->get("tag:{$name}:keys") ?: [];
|
||||
if (!in_array($key, $keys)) {
|
||||
$keys[] = $key;
|
||||
$this->cache->forever("tag:{$name}:keys", $keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function pullKey($key) {
|
||||
foreach ($this->names as $name) {
|
||||
$keys = $this->cache->get("tag:{$name}:keys") ?: [];
|
||||
if (($index = array_search($key, $keys)) !== false) {
|
||||
unset($keys[$index]);
|
||||
$this->cache->forever("tag:{$name}:keys", array_values($keys));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
f_modules/m_frontend/m_donations/src/Core/Controller.php
Normal file
149
f_modules/m_frontend/m_donations/src/Core/Controller.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Controller {
|
||||
protected $config;
|
||||
protected $db;
|
||||
protected $logger;
|
||||
protected $view;
|
||||
protected $request;
|
||||
protected $response;
|
||||
|
||||
public function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->db = db();
|
||||
$this->logger = new Logger();
|
||||
$this->view = new View();
|
||||
$this->request = $this->parseRequest();
|
||||
$this->response = [
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'data' => null
|
||||
];
|
||||
}
|
||||
|
||||
protected function parseRequest() {
|
||||
$request = [
|
||||
'method' => $_SERVER['REQUEST_METHOD'],
|
||||
'path' => parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH),
|
||||
'query' => $_GET,
|
||||
'body' => json_decode(file_get_contents('php://input'), true) ?? [],
|
||||
'headers' => getallheaders()
|
||||
];
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
protected function validateCsrf() {
|
||||
if ($this->request['method'] === 'POST') {
|
||||
$token = $_POST['csrf_token'] ?? null;
|
||||
if (!$token || $token !== $_SESSION['csrf_token']) {
|
||||
$this->error('Invalid CSRF token', 403);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function requireAuth() {
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
$this->redirect('/login');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function requireAdmin() {
|
||||
if (!$this->requireAuth()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = "SELECT is_admin FROM users WHERE id = ?";
|
||||
$user = $this->db->fetch($sql, [$_SESSION['user_id']]);
|
||||
|
||||
if (!$user['is_admin']) {
|
||||
$this->error('Unauthorized access', 403);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function redirect($path) {
|
||||
header('Location: ' . $this->config['base_url'] . $path);
|
||||
exit;
|
||||
}
|
||||
|
||||
protected function json($data, $code = 200) {
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
protected function success($message, $data = null) {
|
||||
$this->response = [
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'data' => $data
|
||||
];
|
||||
$this->json($this->response);
|
||||
}
|
||||
|
||||
protected function error($message, $code = 400) {
|
||||
$this->response = [
|
||||
'success' => false,
|
||||
'message' => $message,
|
||||
'data' => null
|
||||
];
|
||||
$this->json($this->response, $code);
|
||||
}
|
||||
|
||||
protected function validateInput($data, $rules) {
|
||||
$errors = [];
|
||||
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (!isset($data[$field]) && strpos($rule, 'required') !== false) {
|
||||
$errors[$field] = "The $field field is required.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($data[$field])) {
|
||||
if (strpos($rule, 'numeric') !== false && !is_numeric($data[$field])) {
|
||||
$errors[$field] = "The $field must be a number.";
|
||||
}
|
||||
|
||||
if (strpos($rule, 'min:') !== false) {
|
||||
$min = substr($rule, strpos($rule, 'min:') + 4);
|
||||
if ($data[$field] < $min) {
|
||||
$errors[$field] = "The $field must be at least $min.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'max:') !== false) {
|
||||
$max = substr($rule, strpos($rule, 'max:') + 4);
|
||||
if ($data[$field] > $max) {
|
||||
$errors[$field] = "The $field must not be greater than $max.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$this->error('Validation failed', 422);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function sanitizeInput($input) {
|
||||
if (is_array($input)) {
|
||||
return array_map([$this, 'sanitizeInput'], $input);
|
||||
}
|
||||
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
protected function log($message, $level = 'info') {
|
||||
$this->logger->log($message, $level);
|
||||
}
|
||||
}
|
||||
96
f_modules/m_frontend/m_donations/src/Core/Database.php
Normal file
96
f_modules/m_frontend/m_donations/src/Core/Database.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Database {
|
||||
private static $instance = null;
|
||||
private $connection;
|
||||
private $config;
|
||||
|
||||
private function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function connect() {
|
||||
try {
|
||||
$dsn = "mysql:host={$this->config['db']['host']};dbname={$this->config['db']['name']};charset=utf8mb4";
|
||||
$options = [
|
||||
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
|
||||
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
||||
\PDO::ATTR_EMULATE_PREPARES => false
|
||||
];
|
||||
|
||||
$this->connection = new \PDO(
|
||||
$dsn,
|
||||
$this->config['db']['user'],
|
||||
$this->config['db']['pass'],
|
||||
$options
|
||||
);
|
||||
} catch (\PDOException $e) {
|
||||
throw new DatabaseException("Connection failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function query($sql, $params = []) {
|
||||
try {
|
||||
$stmt = $this->connection->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt;
|
||||
} catch (\PDOException $e) {
|
||||
throw new DatabaseException("Query failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function fetch($sql, $params = []) {
|
||||
$stmt = $this->query($sql, $params);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
public function fetchAll($sql, $params = []) {
|
||||
$stmt = $this->query($sql, $params);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public function execute($sql, $params = []) {
|
||||
$stmt = $this->query($sql, $params);
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
public function lastInsertId() {
|
||||
return $this->connection->lastInsertId();
|
||||
}
|
||||
|
||||
public function beginTransaction() {
|
||||
return $this->connection->beginTransaction();
|
||||
}
|
||||
|
||||
public function commit() {
|
||||
return $this->connection->commit();
|
||||
}
|
||||
|
||||
public function rollback() {
|
||||
return $this->connection->rollBack();
|
||||
}
|
||||
|
||||
public function quote($value) {
|
||||
return $this->connection->quote($value);
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
$this->connection = null;
|
||||
}
|
||||
|
||||
private function __clone() {}
|
||||
private function __wakeup() {}
|
||||
}
|
||||
115
f_modules/m_frontend/m_donations/src/Core/Event.php
Normal file
115
f_modules/m_frontend/m_donations/src/Core/Event.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Event {
|
||||
private static $instance = null;
|
||||
private $listeners = [];
|
||||
private $queue;
|
||||
|
||||
private function __construct() {
|
||||
$this->queue = Queue::getInstance();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function listen($event, $listener) {
|
||||
if (!isset($this->listeners[$event])) {
|
||||
$this->listeners[$event] = [];
|
||||
}
|
||||
$this->listeners[$event][] = $listener;
|
||||
}
|
||||
|
||||
public function fire($event, $data = []) {
|
||||
if (!isset($this->listeners[$event])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->listeners[$event] as $listener) {
|
||||
if (is_string($listener)) {
|
||||
$listener = new $listener();
|
||||
}
|
||||
|
||||
if (method_exists($listener, 'handle')) {
|
||||
$listener->handle($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function dispatch($event, $data = []) {
|
||||
$this->fire($event, $data);
|
||||
}
|
||||
|
||||
public function dispatchAsync($event, $data = []) {
|
||||
$this->queue->push('EventJob', [
|
||||
'event' => $event,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function dispatchDelayed($delay, $event, $data = []) {
|
||||
$this->queue->later($delay, 'EventJob', [
|
||||
'event' => $event,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function forget($event) {
|
||||
unset($this->listeners[$event]);
|
||||
}
|
||||
|
||||
public function flush() {
|
||||
$this->listeners = [];
|
||||
}
|
||||
|
||||
public function hasListeners($event) {
|
||||
return isset($this->listeners[$event]) && !empty($this->listeners[$event]);
|
||||
}
|
||||
|
||||
public function getListeners($event) {
|
||||
return $this->listeners[$event] ?? [];
|
||||
}
|
||||
|
||||
public function getEvents() {
|
||||
return array_keys($this->listeners);
|
||||
}
|
||||
|
||||
public function subscribe($subscriber) {
|
||||
if (method_exists($subscriber, 'subscribe')) {
|
||||
$subscriber->subscribe($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function unsubscribe($subscriber) {
|
||||
foreach ($this->listeners as $event => $listeners) {
|
||||
$this->listeners[$event] = array_filter($listeners, function($listener) use ($subscriber) {
|
||||
return $listener !== $subscriber;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EventJob {
|
||||
private $event;
|
||||
|
||||
public function handle($data) {
|
||||
$this->event = Event::getInstance();
|
||||
$this->event->fire($data['event'], $data['data']);
|
||||
}
|
||||
}
|
||||
|
||||
class EventSubscriber {
|
||||
public function subscribe($events) {
|
||||
// Override this method to subscribe to events
|
||||
}
|
||||
}
|
||||
|
||||
class EventListener {
|
||||
public function handle($data) {
|
||||
// Override this method to handle events
|
||||
}
|
||||
}
|
||||
82
f_modules/m_frontend/m_donations/src/Core/Exception.php
Normal file
82
f_modules/m_frontend/m_donations/src/Core/Exception.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Exception extends \Exception {
|
||||
protected $data;
|
||||
|
||||
public function __construct($message = "", $code = 0, $data = null) {
|
||||
parent::__construct($message, $code);
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $this->getMessage(),
|
||||
'code' => $this->getCode(),
|
||||
'data' => $this->getData()
|
||||
];
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
return json_encode($this->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
class ValidationException extends Exception {
|
||||
public function __construct($errors) {
|
||||
parent::__construct('Validation failed', 422, $errors);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthenticationException extends Exception {
|
||||
public function __construct($message = 'Authentication required') {
|
||||
parent::__construct($message, 401);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthorizationException extends Exception {
|
||||
public function __construct($message = 'Unauthorized access') {
|
||||
parent::__construct($message, 403);
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundException extends Exception {
|
||||
public function __construct($message = 'Resource not found') {
|
||||
parent::__construct($message, 404);
|
||||
}
|
||||
}
|
||||
|
||||
class RateLimitException extends Exception {
|
||||
public function __construct($message = 'Rate limit exceeded') {
|
||||
parent::__construct($message, 429);
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentException extends Exception {
|
||||
public function __construct($message, $data = null) {
|
||||
parent::__construct($message, 402, $data);
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseException extends Exception {
|
||||
public function __construct($message = 'Database error occurred') {
|
||||
parent::__construct($message, 500);
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigurationException extends Exception {
|
||||
public function __construct($message = 'Configuration error occurred') {
|
||||
parent::__construct($message, 500);
|
||||
}
|
||||
}
|
||||
|
||||
class WebhookException extends Exception {
|
||||
public function __construct($message = 'Webhook error occurred') {
|
||||
parent::__construct($message, 500);
|
||||
}
|
||||
}
|
||||
97
f_modules/m_frontend/m_donations/src/Core/Handler.php
Normal file
97
f_modules/m_frontend/m_donations/src/Core/Handler.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
abstract class Handler {
|
||||
protected $config;
|
||||
protected $db;
|
||||
protected $logger;
|
||||
|
||||
public function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->db = db();
|
||||
$this->logger = new Logger();
|
||||
}
|
||||
|
||||
protected function validate($data, $rules) {
|
||||
$errors = [];
|
||||
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (!isset($data[$field]) && strpos($rule, 'required') !== false) {
|
||||
$errors[$field] = "The $field field is required.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($data[$field])) {
|
||||
if (strpos($rule, 'numeric') !== false && !is_numeric($data[$field])) {
|
||||
$errors[$field] = "The $field must be a number.";
|
||||
}
|
||||
|
||||
if (strpos($rule, 'min:') !== false) {
|
||||
$min = substr($rule, strpos($rule, 'min:') + 4);
|
||||
if ($data[$field] < $min) {
|
||||
$errors[$field] = "The $field must be at least $min.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'max:') !== false) {
|
||||
$max = substr($rule, strpos($rule, 'max:') + 4);
|
||||
if ($data[$field] > $max) {
|
||||
$errors[$field] = "The $field must not be greater than $max.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'email') !== false && !filter_var($data[$field], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[$field] = "The $field must be a valid email address.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
protected function log($message, $level = 'info') {
|
||||
$this->logger->log($message, $level);
|
||||
}
|
||||
|
||||
protected function error($message, $code = 500) {
|
||||
$this->log($message, 'error');
|
||||
throw new \Exception($message, $code);
|
||||
}
|
||||
|
||||
protected function success($message, $data = null) {
|
||||
$this->log($message, 'info');
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
protected function formatAmount($amount) {
|
||||
return number_format($amount, 2, '.', '');
|
||||
}
|
||||
|
||||
protected function sanitizeInput($input) {
|
||||
if (is_array($input)) {
|
||||
return array_map([$this, 'sanitizeInput'], $input);
|
||||
}
|
||||
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
protected function generateUniqueId($prefix = '') {
|
||||
return $prefix . uniqid() . bin2hex(random_bytes(8));
|
||||
}
|
||||
|
||||
protected function getClientIp() {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
protected function isAllowedIp($ip) {
|
||||
return empty($this->config['api']['allowed_ips']) ||
|
||||
in_array($ip, $this->config['api']['allowed_ips']);
|
||||
}
|
||||
}
|
||||
65
f_modules/m_frontend/m_donations/src/Core/Logger.php
Normal file
65
f_modules/m_frontend/m_donations/src/Core/Logger.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Logger {
|
||||
private $logFile;
|
||||
private $logLevels = ['debug', 'info', 'warning', 'error', 'critical'];
|
||||
|
||||
public function __construct() {
|
||||
$this->logFile = DONATIONS_PATH . '/logs/donations.log';
|
||||
$this->ensureLogDirectory();
|
||||
}
|
||||
|
||||
public function log($message, $level = 'info') {
|
||||
if (!in_array($level, $this->logLevels)) {
|
||||
$level = 'info';
|
||||
}
|
||||
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$logMessage = "[$timestamp] [$level] $message" . PHP_EOL;
|
||||
|
||||
file_put_contents($this->logFile, $logMessage, FILE_APPEND);
|
||||
}
|
||||
|
||||
private function ensureLogDirectory() {
|
||||
$logDir = dirname($this->logFile);
|
||||
if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
}
|
||||
|
||||
public function debug($message) {
|
||||
$this->log($message, 'debug');
|
||||
}
|
||||
|
||||
public function info($message) {
|
||||
$this->log($message, 'info');
|
||||
}
|
||||
|
||||
public function warning($message) {
|
||||
$this->log($message, 'warning');
|
||||
}
|
||||
|
||||
public function error($message) {
|
||||
$this->log($message, 'error');
|
||||
}
|
||||
|
||||
public function critical($message) {
|
||||
$this->log($message, 'critical');
|
||||
}
|
||||
|
||||
public function getLogContents($lines = 100) {
|
||||
if (!file_exists($this->logFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$logs = file($this->logFile);
|
||||
return array_slice($logs, -$lines);
|
||||
}
|
||||
|
||||
public function clearLog() {
|
||||
if (file_exists($this->logFile)) {
|
||||
unlink($this->logFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
190
f_modules/m_frontend/m_donations/src/Core/Mail.php
Normal file
190
f_modules/m_frontend/m_donations/src/Core/Mail.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
class Mail {
|
||||
private static $instance = null;
|
||||
private $config;
|
||||
private $mailer;
|
||||
private $queue;
|
||||
|
||||
private function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->queue = Queue::getInstance();
|
||||
$this->setupMailer();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function setupMailer() {
|
||||
$this->mailer = new PHPMailer(true);
|
||||
|
||||
try {
|
||||
$this->mailer->isSMTP();
|
||||
$this->mailer->Host = $this->config['mail']['host'];
|
||||
$this->mailer->SMTPAuth = true;
|
||||
$this->mailer->Username = $this->config['mail']['username'];
|
||||
$this->mailer->Password = $this->config['mail']['password'];
|
||||
$this->mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||
$this->mailer->Port = $this->config['mail']['port'];
|
||||
$this->mailer->CharSet = 'UTF-8';
|
||||
|
||||
$this->mailer->setFrom(
|
||||
$this->config['mail']['from']['address'],
|
||||
$this->config['mail']['from']['name']
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
throw new \Exception("Mail setup failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function to($address, $name = '') {
|
||||
$this->mailer->addAddress($address, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cc($address, $name = '') {
|
||||
$this->mailer->addCC($address, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function bcc($address, $name = '') {
|
||||
$this->mailer->addBCC($address, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function subject($subject) {
|
||||
$this->mailer->Subject = $subject;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function body($body) {
|
||||
$this->mailer->Body = $body;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function html($html) {
|
||||
$this->mailer->isHTML(true);
|
||||
$this->mailer->Body = $html;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function text($text) {
|
||||
$this->mailer->AltBody = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function attach($path, $name = '') {
|
||||
$this->mailer->addAttachment($path, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function embed($path, $cid) {
|
||||
$this->mailer->addEmbeddedImage($path, $cid);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send() {
|
||||
try {
|
||||
return $this->mailer->send();
|
||||
} catch (Exception $e) {
|
||||
throw new \Exception("Mail send failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function queue($delay = 0) {
|
||||
$data = [
|
||||
'to' => $this->mailer->getAllRecipientAddresses(),
|
||||
'subject' => $this->mailer->Subject,
|
||||
'body' => $this->mailer->Body,
|
||||
'html' => $this->mailer->isHTML(),
|
||||
'text' => $this->mailer->AltBody,
|
||||
'attachments' => $this->getAttachments(),
|
||||
'embedded' => $this->getEmbedded()
|
||||
];
|
||||
|
||||
if ($delay > 0) {
|
||||
return $this->queue->later($delay, 'MailJob', $data);
|
||||
}
|
||||
|
||||
return $this->queue->push('MailJob', $data);
|
||||
}
|
||||
|
||||
protected function getAttachments() {
|
||||
$attachments = [];
|
||||
foreach ($this->mailer->getAttachments() as $attachment) {
|
||||
$attachments[] = [
|
||||
'path' => $attachment[0],
|
||||
'name' => $attachment[1]
|
||||
];
|
||||
}
|
||||
return $attachments;
|
||||
}
|
||||
|
||||
protected function getEmbedded() {
|
||||
$embedded = [];
|
||||
foreach ($this->mailer->getEmbeddedImages() as $image) {
|
||||
$embedded[] = [
|
||||
'path' => $image[0],
|
||||
'cid' => $image[1]
|
||||
];
|
||||
}
|
||||
return $embedded;
|
||||
}
|
||||
|
||||
public function reset() {
|
||||
$this->mailer->clearAllRecipients();
|
||||
$this->mailer->clearAttachments();
|
||||
$this->mailer->clearCustomHeaders();
|
||||
$this->mailer->clearReplyTos();
|
||||
$this->mailer->Subject = '';
|
||||
$this->mailer->Body = '';
|
||||
$this->mailer->AltBody = '';
|
||||
$this->mailer->isHTML(false);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMailer() {
|
||||
return $this->mailer;
|
||||
}
|
||||
}
|
||||
|
||||
class MailJob {
|
||||
private $mail;
|
||||
|
||||
public function handle($data) {
|
||||
$this->mail = Mail::getInstance();
|
||||
|
||||
foreach ($data['to'] as $address => $name) {
|
||||
$this->mail->to($address, $name);
|
||||
}
|
||||
|
||||
$this->mail->subject($data['subject'])
|
||||
->body($data['body']);
|
||||
|
||||
if ($data['html']) {
|
||||
$this->mail->html($data['body']);
|
||||
}
|
||||
|
||||
if ($data['text']) {
|
||||
$this->mail->text($data['text']);
|
||||
}
|
||||
|
||||
foreach ($data['attachments'] as $attachment) {
|
||||
$this->mail->attach($attachment['path'], $attachment['name']);
|
||||
}
|
||||
|
||||
foreach ($data['embedded'] as $image) {
|
||||
$this->mail->embed($image['path'], $image['cid']);
|
||||
}
|
||||
|
||||
$this->mail->send();
|
||||
}
|
||||
}
|
||||
105
f_modules/m_frontend/m_donations/src/Core/Model.php
Normal file
105
f_modules/m_frontend/m_donations/src/Core/Model.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
abstract class Model {
|
||||
protected $db;
|
||||
protected $table;
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
public function __construct() {
|
||||
$this->db = db();
|
||||
}
|
||||
|
||||
public function find($id) {
|
||||
$sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?";
|
||||
return $this->db->fetch($sql, [$id]);
|
||||
}
|
||||
|
||||
public function all($conditions = [], $orderBy = null, $limit = null) {
|
||||
$sql = "SELECT * FROM {$this->table}";
|
||||
$params = [];
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$sql .= " WHERE " . $this->buildWhereClause($conditions, $params);
|
||||
}
|
||||
|
||||
if ($orderBy) {
|
||||
$sql .= " ORDER BY " . $orderBy;
|
||||
}
|
||||
|
||||
if ($limit) {
|
||||
$sql .= " LIMIT " . $limit;
|
||||
}
|
||||
|
||||
return $this->db->fetchAll($sql, $params);
|
||||
}
|
||||
|
||||
public function create($data) {
|
||||
$fields = array_keys($data);
|
||||
$values = array_values($data);
|
||||
$placeholders = str_repeat('?,', count($fields) - 1) . '?';
|
||||
|
||||
$sql = "INSERT INTO {$this->table} (" . implode(',', $fields) . ")
|
||||
VALUES ($placeholders)";
|
||||
|
||||
$this->db->execute($sql, $values);
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
public function update($id, $data) {
|
||||
$fields = array_keys($data);
|
||||
$values = array_values($data);
|
||||
$set = implode('=?,', $fields) . '=?';
|
||||
$values[] = $id;
|
||||
|
||||
$sql = "UPDATE {$this->table} SET $set
|
||||
WHERE {$this->primaryKey} = ?";
|
||||
|
||||
return $this->db->execute($sql, $values);
|
||||
}
|
||||
|
||||
public function delete($id) {
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?";
|
||||
return $this->db->execute($sql, [$id]);
|
||||
}
|
||||
|
||||
public function count($conditions = []) {
|
||||
$sql = "SELECT COUNT(*) as count FROM {$this->table}";
|
||||
$params = [];
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$sql .= " WHERE " . $this->buildWhereClause($conditions, $params);
|
||||
}
|
||||
|
||||
$result = $this->db->fetch($sql, $params);
|
||||
return $result['count'];
|
||||
}
|
||||
|
||||
protected function buildWhereClause($conditions, &$params) {
|
||||
$clauses = [];
|
||||
foreach ($conditions as $field => $value) {
|
||||
if (is_array($value)) {
|
||||
$operator = $value[0];
|
||||
$val = $value[1];
|
||||
} else {
|
||||
$operator = '=';
|
||||
$val = $value;
|
||||
}
|
||||
$clauses[] = "$field $operator ?";
|
||||
$params[] = $val;
|
||||
}
|
||||
return implode(' AND ', $clauses);
|
||||
}
|
||||
|
||||
public function beginTransaction() {
|
||||
return $this->db->beginTransaction();
|
||||
}
|
||||
|
||||
public function commit() {
|
||||
return $this->db->commit();
|
||||
}
|
||||
|
||||
public function rollback() {
|
||||
return $this->db->rollback();
|
||||
}
|
||||
}
|
||||
170
f_modules/m_frontend/m_donations/src/Core/Queue.php
Normal file
170
f_modules/m_frontend/m_donations/src/Core/Queue.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Queue {
|
||||
private static $instance = null;
|
||||
private $config;
|
||||
private $connection;
|
||||
private $channel;
|
||||
private $queue;
|
||||
|
||||
private function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function connect() {
|
||||
try {
|
||||
$this->connection = new \AMQPConnection([
|
||||
'host' => $this->config['queue']['host'],
|
||||
'port' => $this->config['queue']['port'],
|
||||
'username' => $this->config['queue']['user'],
|
||||
'password' => $this->config['queue']['pass'],
|
||||
'vhost' => $this->config['queue']['vhost']
|
||||
]);
|
||||
|
||||
$this->channel = $this->connection->channel();
|
||||
$this->queue = $this->config['queue']['name'];
|
||||
|
||||
$this->channel->queue_declare($this->queue, false, true, false, false);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception("Queue connection failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function push($job, $data = [], $delay = 0) {
|
||||
$message = [
|
||||
'job' => $job,
|
||||
'data' => $data,
|
||||
'attempts' => 0,
|
||||
'created_at' => time()
|
||||
];
|
||||
|
||||
$msg = new \AMQPMessage(
|
||||
json_encode($message),
|
||||
['delivery_mode' => \AMQPMessage::DELIVERY_MODE_PERSISTENT]
|
||||
);
|
||||
|
||||
if ($delay > 0) {
|
||||
$this->channel->basic_publish($msg, '', $this->queue . '_delayed');
|
||||
} else {
|
||||
$this->channel->basic_publish($msg, '', $this->queue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function later($delay, $job, $data = []) {
|
||||
return $this->push($job, $data, $delay);
|
||||
}
|
||||
|
||||
public function pop() {
|
||||
$msg = $this->channel->basic_get($this->queue);
|
||||
|
||||
if ($msg) {
|
||||
$message = json_decode($msg->getBody(), true);
|
||||
$message['attempts']++;
|
||||
|
||||
$this->channel->basic_ack($msg->getDeliveryTag());
|
||||
return $message;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function process() {
|
||||
$this->channel->basic_qos(null, 1, null);
|
||||
|
||||
$callback = function($msg) {
|
||||
try {
|
||||
$message = json_decode($msg->getBody(), true);
|
||||
$job = $message['job'];
|
||||
$data = $message['data'];
|
||||
|
||||
$class = "Donations\\Jobs\\{$job}";
|
||||
$instance = new $class();
|
||||
$instance->handle($data);
|
||||
|
||||
$this->channel->basic_ack($msg->getDeliveryTag());
|
||||
} catch (\Exception $e) {
|
||||
$this->handleFailedJob($msg, $e);
|
||||
}
|
||||
};
|
||||
|
||||
$this->channel->basic_consume($this->queue, '', false, false, false, false, $callback);
|
||||
|
||||
while ($this->channel->is_consuming()) {
|
||||
$this->channel->wait();
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleFailedJob($msg, $exception) {
|
||||
$message = json_decode($msg->getBody(), true);
|
||||
$message['attempts']++;
|
||||
|
||||
if ($message['attempts'] >= $this->config['queue']['max_attempts']) {
|
||||
$this->channel->basic_nack($msg->getDeliveryTag(), false, false);
|
||||
$this->logFailedJob($message, $exception);
|
||||
} else {
|
||||
$this->channel->basic_nack($msg->getDeliveryTag(), false, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected function logFailedJob($message, $exception) {
|
||||
$sql = "INSERT INTO failed_jobs (job, data, error, failed_at) VALUES (?, ?, ?, NOW())";
|
||||
$this->db->execute($sql, [
|
||||
$message['job'],
|
||||
json_encode($message['data']),
|
||||
$exception->getMessage()
|
||||
]);
|
||||
}
|
||||
|
||||
public function size() {
|
||||
$queueInfo = $this->channel->queue_declare($this->queue, false, true, false, false);
|
||||
return $queueInfo[1];
|
||||
}
|
||||
|
||||
public function flush() {
|
||||
$this->channel->queue_purge($this->queue);
|
||||
}
|
||||
|
||||
public function retry($id) {
|
||||
$sql = "SELECT * FROM failed_jobs WHERE id = ?";
|
||||
$job = $this->db->fetch($sql, [$id]);
|
||||
|
||||
if ($job) {
|
||||
$this->push($job['job'], json_decode($job['data'], true));
|
||||
$this->db->execute("DELETE FROM failed_jobs WHERE id = ?", [$id]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function forget($id) {
|
||||
return $this->db->execute("DELETE FROM failed_jobs WHERE id = ?", [$id]);
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if ($this->channel) {
|
||||
$this->channel->close();
|
||||
}
|
||||
if ($this->connection) {
|
||||
$this->connection->close();
|
||||
}
|
||||
}
|
||||
|
||||
private function __clone() {}
|
||||
private function __wakeup() {}
|
||||
}
|
||||
108
f_modules/m_frontend/m_donations/src/Core/Router.php
Normal file
108
f_modules/m_frontend/m_donations/src/Core/Router.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Router {
|
||||
protected $routes = [];
|
||||
protected $config;
|
||||
|
||||
public function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
}
|
||||
|
||||
public function add($method, $path, $handler) {
|
||||
$this->routes[] = [
|
||||
'method' => strtoupper($method),
|
||||
'path' => $path,
|
||||
'handler' => $handler
|
||||
];
|
||||
}
|
||||
|
||||
public function get($path, $handler) {
|
||||
$this->add('GET', $path, $handler);
|
||||
}
|
||||
|
||||
public function post($path, $handler) {
|
||||
$this->add('POST', $path, $handler);
|
||||
}
|
||||
|
||||
public function put($path, $handler) {
|
||||
$this->add('PUT', $path, $handler);
|
||||
}
|
||||
|
||||
public function delete($path, $handler) {
|
||||
$this->add('DELETE', $path, $handler);
|
||||
}
|
||||
|
||||
public function dispatch() {
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$path = str_replace($this->config['base_url'], '', $path);
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
if ($route['method'] !== $method) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pattern = $this->convertPathToRegex($route['path']);
|
||||
if (preg_match($pattern, $path, $matches)) {
|
||||
array_shift($matches); // Remove the full match
|
||||
return $this->executeHandler($route['handler'], $matches);
|
||||
}
|
||||
}
|
||||
|
||||
// No route found
|
||||
http_response_code(404);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Route not found',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
protected function convertPathToRegex($path) {
|
||||
return '#^' . preg_replace('#\{([a-zA-Z0-9_]+)\}#', '([^/]+)', $path) . '$#';
|
||||
}
|
||||
|
||||
protected function executeHandler($handler, $params) {
|
||||
if (is_callable($handler)) {
|
||||
return call_user_func_array($handler, $params);
|
||||
}
|
||||
|
||||
if (is_string($handler)) {
|
||||
list($controller, $method) = explode('@', $handler);
|
||||
$controllerClass = "Donations\\Controllers\\{$controller}";
|
||||
$controllerInstance = new $controllerClass();
|
||||
return call_user_func_array([$controllerInstance, $method], $params);
|
||||
}
|
||||
|
||||
throw new \Exception('Invalid handler');
|
||||
}
|
||||
|
||||
public function group($prefix, $routes) {
|
||||
foreach ($routes as $route) {
|
||||
$this->add(
|
||||
$route['method'],
|
||||
$prefix . $route['path'],
|
||||
$route['handler']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function resource($name, $controller) {
|
||||
$this->get("/{$name}", "{$controller}@index");
|
||||
$this->get("/{$name}/create", "{$controller}@create");
|
||||
$this->post("/{$name}", "{$controller}@store");
|
||||
$this->get("/{$name}/{id}", "{$controller}@show");
|
||||
$this->get("/{$name}/{id}/edit", "{$controller}@edit");
|
||||
$this->put("/{$name}/{id}", "{$controller}@update");
|
||||
$this->delete("/{$name}/{id}", "{$controller}@destroy");
|
||||
}
|
||||
|
||||
public function apiResource($name, $controller) {
|
||||
$this->get("/api/{$name}", "{$controller}@index");
|
||||
$this->post("/api/{$name}", "{$controller}@store");
|
||||
$this->get("/api/{$name}/{id}", "{$controller}@show");
|
||||
$this->put("/api/{$name}/{id}", "{$controller}@update");
|
||||
$this->delete("/api/{$name}/{id}", "{$controller}@destroy");
|
||||
}
|
||||
}
|
||||
114
f_modules/m_frontend/m_donations/src/Core/Service.php
Normal file
114
f_modules/m_frontend/m_donations/src/Core/Service.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
abstract class Service {
|
||||
protected $config;
|
||||
protected $db;
|
||||
protected $logger;
|
||||
|
||||
public function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->db = db();
|
||||
$this->logger = new Logger();
|
||||
}
|
||||
|
||||
protected function beginTransaction() {
|
||||
return $this->db->beginTransaction();
|
||||
}
|
||||
|
||||
protected function commit() {
|
||||
return $this->db->commit();
|
||||
}
|
||||
|
||||
protected function rollback() {
|
||||
return $this->db->rollback();
|
||||
}
|
||||
|
||||
protected function validate($data, $rules) {
|
||||
$errors = [];
|
||||
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (!isset($data[$field]) && strpos($rule, 'required') !== false) {
|
||||
$errors[$field] = "The $field field is required.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($data[$field])) {
|
||||
if (strpos($rule, 'numeric') !== false && !is_numeric($data[$field])) {
|
||||
$errors[$field] = "The $field must be a number.";
|
||||
}
|
||||
|
||||
if (strpos($rule, 'min:') !== false) {
|
||||
$min = substr($rule, strpos($rule, 'min:') + 4);
|
||||
if ($data[$field] < $min) {
|
||||
$errors[$field] = "The $field must be at least $min.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'max:') !== false) {
|
||||
$max = substr($rule, strpos($rule, 'max:') + 4);
|
||||
if ($data[$field] > $max) {
|
||||
$errors[$field] = "The $field must not be greater than $max.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
throw new \Exception(json_encode($errors));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function sanitizeInput($input) {
|
||||
if (is_array($input)) {
|
||||
return array_map([$this, 'sanitizeInput'], $input);
|
||||
}
|
||||
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
protected function log($message, $level = 'info') {
|
||||
$this->logger->log($message, $level);
|
||||
}
|
||||
|
||||
protected function formatAmount($amount) {
|
||||
return number_format($amount, 2, '.', '');
|
||||
}
|
||||
|
||||
protected function generateUniqueId($prefix = '') {
|
||||
return $prefix . uniqid() . bin2hex(random_bytes(8));
|
||||
}
|
||||
|
||||
protected function getClientIp() {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
protected function isAllowedIp($ip) {
|
||||
return empty($this->config['api']['allowed_ips']) ||
|
||||
in_array($ip, $this->config['api']['allowed_ips']);
|
||||
}
|
||||
|
||||
protected function formatDate($date, $format = 'Y-m-d H:i:s') {
|
||||
return date($format, strtotime($date));
|
||||
}
|
||||
|
||||
protected function truncate($text, $length = 100) {
|
||||
if (strlen($text) <= $length) {
|
||||
return $text;
|
||||
}
|
||||
return substr($text, 0, $length) . '...';
|
||||
}
|
||||
|
||||
protected function escape($text) {
|
||||
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
protected function isActive($path) {
|
||||
return strpos($_SERVER['REQUEST_URI'], $path) !== false ? 'active' : '';
|
||||
}
|
||||
}
|
||||
209
f_modules/m_frontend/m_donations/src/Core/Session.php
Normal file
209
f_modules/m_frontend/m_donations/src/Core/Session.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
namespace Donations\Core;
|
||||
|
||||
class Session {
|
||||
private static $instance = null;
|
||||
private $config;
|
||||
private $started = false;
|
||||
|
||||
private function __construct() {
|
||||
$this->config = require DONATIONS_PATH . '/config/config.php';
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function start() {
|
||||
if (!$this->started) {
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start([
|
||||
'cookie_httponly' => true,
|
||||
'cookie_secure' => $this->config['session']['secure'],
|
||||
'cookie_samesite' => 'Lax',
|
||||
'gc_maxlifetime' => $this->config['session']['lifetime'] * 60
|
||||
]);
|
||||
}
|
||||
$this->started = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key, $default = null) {
|
||||
return $_SESSION[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function set($key, $value) {
|
||||
$_SESSION[$key] = $value;
|
||||
}
|
||||
|
||||
public function has($key) {
|
||||
return isset($_SESSION[$key]);
|
||||
}
|
||||
|
||||
public function remove($key) {
|
||||
if (isset($_SESSION[$key])) {
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
public function all() {
|
||||
return $_SESSION;
|
||||
}
|
||||
|
||||
public function flush() {
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
public function regenerate($destroy = false) {
|
||||
session_regenerate_id($destroy);
|
||||
}
|
||||
|
||||
public function destroy() {
|
||||
session_destroy();
|
||||
$this->started = false;
|
||||
}
|
||||
|
||||
public function flash($key, $value = null) {
|
||||
if ($value !== null) {
|
||||
$_SESSION['_flash'][$key] = $value;
|
||||
} else {
|
||||
$value = $_SESSION['_flash'][$key] ?? null;
|
||||
unset($_SESSION['_flash'][$key]);
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function reflash() {
|
||||
if (isset($_SESSION['_flash'])) {
|
||||
$_SESSION['_flash'] = array_merge($_SESSION['_flash'], $_SESSION['_flash']);
|
||||
}
|
||||
}
|
||||
|
||||
public function keep($keys = null) {
|
||||
if ($keys === null) {
|
||||
$_SESSION['_flash'] = [];
|
||||
} else {
|
||||
foreach ((array) $keys as $key) {
|
||||
if (isset($_SESSION['_flash'][$key])) {
|
||||
$_SESSION['_flash'][$key] = $_SESSION['_flash'][$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function token() {
|
||||
if (!isset($_SESSION['_token'])) {
|
||||
$_SESSION['_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION['_token'];
|
||||
}
|
||||
|
||||
public function regenerateToken() {
|
||||
$_SESSION['_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
public function previousUrl() {
|
||||
return $_SESSION['_previous_url'] ?? null;
|
||||
}
|
||||
|
||||
public function intended($default = null) {
|
||||
return $_SESSION['_intended_url'] ?? $default;
|
||||
}
|
||||
|
||||
public function setIntendedUrl($url) {
|
||||
$_SESSION['_intended_url'] = $url;
|
||||
}
|
||||
|
||||
public function pull($key, $default = null) {
|
||||
$value = $this->get($key, $default);
|
||||
$this->remove($key);
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function increment($key, $value = 1) {
|
||||
if (!isset($_SESSION[$key])) {
|
||||
$_SESSION[$key] = 0;
|
||||
}
|
||||
$_SESSION[$key] += $value;
|
||||
return $_SESSION[$key];
|
||||
}
|
||||
|
||||
public function decrement($key, $value = 1) {
|
||||
return $this->increment($key, -$value);
|
||||
}
|
||||
|
||||
public function put($key, $value) {
|
||||
return $this->set($key, $value);
|
||||
}
|
||||
|
||||
public function forget($key) {
|
||||
return $this->remove($key);
|
||||
}
|
||||
|
||||
public function exists($key) {
|
||||
return $this->has($key);
|
||||
}
|
||||
|
||||
public function missing($key) {
|
||||
return !$this->has($key);
|
||||
}
|
||||
|
||||
public function save() {
|
||||
if ($this->started) {
|
||||
session_write_close();
|
||||
$this->started = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return session_id();
|
||||
}
|
||||
|
||||
public function setId($id) {
|
||||
if ($this->started) {
|
||||
session_write_close();
|
||||
}
|
||||
session_id($id);
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return session_name();
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
if ($this->started) {
|
||||
session_write_close();
|
||||
}
|
||||
session_name($name);
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public function getHandler() {
|
||||
return session_get_handler();
|
||||
}
|
||||
|
||||
public function setHandler($handler) {
|
||||
if ($this->started) {
|
||||
session_write_close();
|
||||
}
|
||||
session_set_save_handler($handler);
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public function getCookieParams() {
|
||||
return session_get_cookie_params();
|
||||
}
|
||||
|
||||
public function setCookieParams($lifetime, $path = '/', $domain = '', $secure = false, $httponly = true) {
|
||||
if ($this->started) {
|
||||
session_write_close();
|
||||
}
|
||||
session_set_cookie_params($lifetime, $path, $domain, $secure, $httponly);
|
||||
$this->start();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user