mirror of
https://git.v0l.io/Kieran/void.cat.git
synced 2025-04-10 06:19:03 +02:00
add stats and faq
This commit is contained in:
parent
e964422096
commit
b3c2f72146
0
examples/embed/README.md
Normal file
0
examples/embed/README.md
Normal file
40
index.html
40
index.html
@ -82,7 +82,7 @@
|
||||
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="header">
|
||||
<div class="header" onclick="window.location.href = '/'">
|
||||
void.cat
|
||||
</div>
|
||||
<div id="page-upload">
|
||||
@ -109,12 +109,44 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="page-stats">
|
||||
|
||||
</div>
|
||||
<div id="page-faq">
|
||||
|
||||
<div class="faq-section">
|
||||
<div class="faq-header">
|
||||
Are there any restrictions on what can be uploaded
|
||||
</div>
|
||||
<div class="faq-content">
|
||||
No.
|
||||
</div>
|
||||
</div>
|
||||
<div class="faq-section">
|
||||
<div class="faq-header">
|
||||
How can I embed content in my site
|
||||
</div>
|
||||
<div class="faq-content">
|
||||
I have created a service worker which you can host on your site which will decrypt the links automatically, find
|
||||
more info <a href="https://github.com/v0l/void.cat/blob/v3-b2b/examples/embed" target="_blank">here on github.</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="faq-section">
|
||||
<div class="faq-header">
|
||||
I have more questions
|
||||
</div>
|
||||
<div class="faq-content">
|
||||
Feel free to join <a href="https://discord.gg/8BkxTGs" target="_blank">Discord</a> or send a mail to <a href="mailto:admin@void.cat">admin@void.cat</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer-stats">
|
||||
<div><b>Files:</b><span>0</span></div>
|
||||
<div><b>Total Size:</b><span>0 B</span></div>
|
||||
<div><b>24Hr Transfer:</b><span>0 B</span></div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<a href="javascript:(function() { window.location.href='/#faq'; App.Init(); })()">FAQ</a> | <a href="http://rv6omygg3ksi3dys.onion/" target="_blank">Tor</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<ins class="adsbygoogle" style="display:block; margin-left: auto; margin-right: auto;" data-ad-client="ca-pub-3289062345896209"
|
||||
data-ad-slot="9187315106" data-ad-format="auto" data-full-width-responsive="true"></ins>
|
||||
<script>
|
||||
|
@ -18,6 +18,10 @@ html, body {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
a { text-decoration: none; color: rgb(0, 9, 94); font-weight: bold; }
|
||||
a:visited { text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
.page {
|
||||
width: $page-width;
|
||||
margin-left: auto;
|
||||
@ -32,7 +36,7 @@ html, body {
|
||||
|
||||
#page-view {
|
||||
display:none;
|
||||
padding: $page-padding;
|
||||
padding: 10px 10px 0px 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@ -54,7 +58,7 @@ html, body {
|
||||
|
||||
#page-upload {
|
||||
display: none;
|
||||
padding: $page-padding;
|
||||
padding: 10px 10px 0px 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@ -141,10 +145,61 @@ html, body {
|
||||
background-color: #790e00;
|
||||
}
|
||||
|
||||
#footer {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#footer-stats {
|
||||
display: grid;
|
||||
grid-template-columns: 33.3% 33.3% 33.3%;
|
||||
text-align: center;
|
||||
border: 1px solid #aaa;
|
||||
margin: 0px 10px 10px 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#page-stats {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#page-faq {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#page-faq .faq-section {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#page-faq .faq-section .faq-header {
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #aaa;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#page-faq .faq-section .faq-content {
|
||||
display: none;
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #eee;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
#page-view .file-info {
|
||||
display: grid;
|
||||
grid-template-columns: 25% 25% 25% 25%;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
border: 1px solid #aaa;
|
||||
margin: 10px 0px 0px 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
@ -2,13 +2,12 @@
|
||||
* @constant {Object}
|
||||
*/
|
||||
const App = {
|
||||
get Version() { return AppVersion },
|
||||
|
||||
Elements: {
|
||||
get Dropzone() { return $('#dropzone') },
|
||||
get Uploads() { return $('#uploads') },
|
||||
get PageView() { return $('#page-view') },
|
||||
get PageUpload() { return $('#page-upload') }
|
||||
get PageUpload() { return $('#page-upload') },
|
||||
get PageFaq() { return $('#page-faq') }
|
||||
},
|
||||
|
||||
Templates: {
|
||||
@ -35,15 +34,34 @@ const App = {
|
||||
/**
|
||||
* Sets up the page
|
||||
*/
|
||||
Init: function () {
|
||||
Init: async function () {
|
||||
if (location.hash !== "") {
|
||||
App.Elements.PageUpload.style.display = "none";
|
||||
App.Elements.PageView.style.display = "block";
|
||||
new ViewManager();
|
||||
if (location.hash == "#faq") {
|
||||
let faq_headers = document.querySelectorAll('#page-faq .faq-header');
|
||||
for (let x = 0; x < faq_headers.length; x++) {
|
||||
faq_headers[x].addEventListener('click', function() {
|
||||
this.nextElementSibling.classList.toggle("show");
|
||||
}.bind(faq_headers[x]));
|
||||
}
|
||||
App.Elements.PageUpload.style.display = "none";
|
||||
App.Elements.PageFaq.style.display = "block";
|
||||
} else {
|
||||
App.Elements.PageUpload.style.display = "none";
|
||||
App.Elements.PageView.style.display = "block";
|
||||
new ViewManager();
|
||||
}
|
||||
} else {
|
||||
App.Elements.PageUpload.style.display = "block";
|
||||
App.Elements.PageView.style.display = "none";
|
||||
new DropzoneManager(App.Elements.Dropzone);
|
||||
|
||||
let stats = await Api.GetSiteInfo();
|
||||
if(stats.ok){
|
||||
let elms = document.querySelectorAll("#footer-stats div span");
|
||||
elms[0].textContent = stats.data.basic_stats.Files;
|
||||
elms[1].textContent = Utils.FormatBytes(stats.data.basic_stats.Size, 2);
|
||||
elms[2].textContent = Utils.FormatBytes(stats.data.basic_stats.Transfer_24h, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -81,6 +81,12 @@ const Api = {
|
||||
return JSON.parse((await JsonXHR('POST', '/api', req)).response);
|
||||
},
|
||||
|
||||
GetSiteInfo: async function (id) {
|
||||
return await Api.DoRequest({
|
||||
cmd: 'site_info'
|
||||
});
|
||||
},
|
||||
|
||||
GetFileInfo: async function (id) {
|
||||
return await Api.DoRequest({
|
||||
cmd: 'file_info',
|
||||
|
@ -9,7 +9,7 @@
|
||||
class Api implements RequestHandler {
|
||||
|
||||
public function __construct(){
|
||||
Config::LoadConfig(array('upload_folder', 'recaptcha_site_key', 'recaptcha_secret'));
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder', 'recaptcha_site_key', 'recaptcha_secret'));
|
||||
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
}
|
||||
@ -23,6 +23,14 @@
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
|
||||
switch($cmd->cmd){
|
||||
case "site_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = array(
|
||||
"max_upload_size" => Config::$Instance->max_upload_size,
|
||||
"basic_stats" => Stats::Get()
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "file_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = $fs->GetFileInfo($cmd->id);
|
||||
|
13
src/php/cron.php
Normal file
13
src/php/cron.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
include_once("init.php");
|
||||
|
||||
StaticRedis::Connect();
|
||||
Config::LoadConfig(array("upload_folder"));
|
||||
|
||||
$fs = new FileStore(Config::$Instance->upload_folder, "/usr/local/nginx/html");
|
||||
|
||||
echo "Loading stats for: " . $fs->GetUploadDirAbsolute() . "\n";
|
||||
//echo "\n\t" . implode("\n\t", $fs->ListFiles()) . "\n";
|
||||
|
||||
Stats::Collect($fs);
|
||||
?>
|
@ -29,7 +29,7 @@
|
||||
$tracking = new Tracking();
|
||||
|
||||
$abuse->CheckDownload($id);
|
||||
$tracking->TrackDownload($id);
|
||||
$tracking->TrackDownload($this->Fs, $id);
|
||||
|
||||
//allow embeded header from preflight check
|
||||
if($_SERVER["REQUEST_METHOD"] === "OPTIONS"){
|
||||
|
@ -1,9 +1,11 @@
|
||||
<?php
|
||||
class FileStore {
|
||||
private $UploadFolder;
|
||||
private $DocumentRoot;
|
||||
|
||||
public function __construct($path) {
|
||||
$this->UploadFolder = $path;
|
||||
public function __construct($dir, $root = null) {
|
||||
$this->UploadFolder = $dir;
|
||||
$this->DocumentRoot = $root === null ? $_SERVER["DOCUMENT_ROOT"] : $root;
|
||||
}
|
||||
|
||||
public function SetFileStats($info) : void {
|
||||
@ -27,12 +29,16 @@
|
||||
);
|
||||
}
|
||||
|
||||
public function GetUploadDirAbsolute() : string {
|
||||
return "$this->DocumentRoot/$this->UploadFolder";
|
||||
}
|
||||
|
||||
public function GetRelativeFilePath($id) : string {
|
||||
return "$this->UploadFolder/$id";
|
||||
}
|
||||
|
||||
public function GetAbsoluteFilePath($id) : string {
|
||||
return "$_SERVER[DOCUMENT_ROOT]/$this->UploadFolder/$id";
|
||||
return "$this->GetUploadDirAbsolute()/$this->UploadFolder/$id";
|
||||
}
|
||||
|
||||
public function GetFileInfo($id) : ?FileInfo {
|
||||
@ -67,5 +73,13 @@
|
||||
fclose($fout);
|
||||
fclose($input);
|
||||
}
|
||||
|
||||
public function GetFileSize($id) : int {
|
||||
return filesize($this->GetAbsoluteFilePath($id));
|
||||
}
|
||||
|
||||
public function ListFiles() : array {
|
||||
return glob($this->GetUploadDirAbsolute() . "/*");
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,15 +1,5 @@
|
||||
<?php
|
||||
define('REDIS_CONFIG', 'redis-host');
|
||||
define('REDIS_PREFIX', 'vc:');
|
||||
define('USER_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if(!isset($_COOKIE["VC:UID"])) {
|
||||
setcookie("VC:UID", uniqid());
|
||||
}
|
||||
|
||||
spl_autoload_register(function ($class_name) {
|
||||
include dirname(__FILE__) . '/' . strtolower($class_name) . '.php';
|
||||
});
|
||||
include_once("init.php");
|
||||
|
||||
//Startup
|
||||
if(StaticRedis::Connect() == True) {
|
||||
|
13
src/php/init.php
Normal file
13
src/php/init.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
define('REDIS_CONFIG', 'redis-host');
|
||||
define('REDIS_PREFIX', 'vc:');
|
||||
define('USER_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "127.0.0.1"));
|
||||
|
||||
if(!isset($_COOKIE["VC:UID"])) {
|
||||
setcookie("VC:UID", uniqid());
|
||||
}
|
||||
|
||||
spl_autoload_register(function ($class_name) {
|
||||
include dirname(__FILE__) . '/' . strtolower($class_name) . '.php';
|
||||
});
|
||||
?>
|
53
src/php/stats.php
Normal file
53
src/php/stats.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
class Stats {
|
||||
public $Files;
|
||||
public $Size;
|
||||
public $Transfer_24h;
|
||||
|
||||
private static $TransferStatsKey = REDIS_PREFIX . "stats-transfer-all";
|
||||
private static $GeneralStatsKey = REDIS_PREFIX . "stats-general";
|
||||
|
||||
public static function Get() : Stats {
|
||||
$redis = StaticRedis::$Instance;
|
||||
|
||||
//calculate 24hr transfer stats
|
||||
$tx_24h_array = $redis->zRange(self::$TransferStatsKey, 0, 24, true); //stats are 1hr interval
|
||||
$tx_24h = 0;
|
||||
foreach($tx_24h_array as $tx_key => $tx_bytes) {
|
||||
$tx_24h += $tx_bytes;
|
||||
}
|
||||
|
||||
//get general stats
|
||||
$general = (object)$redis->hMGet(self::$GeneralStatsKey, array("files", "size"));
|
||||
|
||||
$ret = new Stats();
|
||||
$ret->Transfer_24h = $tx_24h;
|
||||
$ret->Files = intval($general->files !== False ? $general->files : 0);
|
||||
$ret->Size = intval($general->size !== False ? $general->size : 0);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function TrackTransfer($id, $size) : void {
|
||||
$redis = StaticRedis::$Instance;
|
||||
|
||||
$stat_member = date("YmdH");
|
||||
$redis->zIncrBy(self::$TransferStatsKey, $size, $stat_member);
|
||||
}
|
||||
|
||||
public static function Collect($fs) : void {
|
||||
$redis = StaticRedis::$Instance;
|
||||
|
||||
$files = $fs->ListFiles();
|
||||
$total_size = 0;
|
||||
foreach($files as $file) {
|
||||
$total_size += filesize($file);
|
||||
}
|
||||
|
||||
$redis->hMSet(self::$GeneralStatsKey, array(
|
||||
"files" => count($files),
|
||||
"size" => $total_size
|
||||
));
|
||||
}
|
||||
}
|
||||
?>
|
@ -6,7 +6,6 @@
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
|
||||
if(isset($_SERVER["HTTP_X_FILE_ID"])) {
|
||||
$id = $_SERVER["HTTP_X_FILE_ID"];
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
@ -14,7 +13,7 @@
|
||||
//resolve the hostnames to ips
|
||||
$redis = StaticRedis::$Instance;
|
||||
$sync_hosts = $redis->sMembers(REDIS_PREFIX . 'sync-hosts');
|
||||
|
||||
|
||||
$sync_hosts_ips = array();
|
||||
foreach($sync_hosts as $host) {
|
||||
$sync_hosts_ips[] = gethostbyname($host);
|
||||
@ -23,16 +22,19 @@
|
||||
//check the ip of the host submitting the file for sync
|
||||
if(in_array(USER_IP, $sync_hosts_ips)) {
|
||||
$fs->StoreFile("php://input", $id);
|
||||
http_response_code(201);
|
||||
} else {
|
||||
http_response_code(401);
|
||||
}
|
||||
} else {
|
||||
http_response_code(200);
|
||||
}
|
||||
} else {
|
||||
http_response_code(400);
|
||||
}
|
||||
}
|
||||
|
||||
public static function SyncFile($id, $filename, $host) : void {
|
||||
public static function SyncFile($id, $filename, $host) : int {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://$host/sync");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
@ -42,7 +44,10 @@
|
||||
"X-File-Id: " . $id
|
||||
));
|
||||
curl_exec($ch);
|
||||
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
curl_close ($ch);
|
||||
|
||||
return intval($status);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,27 +1,45 @@
|
||||
<?php
|
||||
class Tracking {
|
||||
public function TrackDownload($id) : void {
|
||||
public function TrackDownload($fs, $id) : void {
|
||||
$redis = StaticRedis::$Instance;
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
|
||||
$file_size = $fs->GetFileSize($id);
|
||||
|
||||
if(!$this->IsRangeRequest()) {
|
||||
$redis->hIncrBy($file_key, 'views', 1);
|
||||
$redis->hSet($file_key, 'lastview', time());
|
||||
|
||||
Stats::TrackTransfer($id, $file_size);
|
||||
} else {
|
||||
$range = $this->GetRequestRange($file_size);
|
||||
Stats::TrackTransfer($id, $range->end - $range->start);
|
||||
}
|
||||
|
||||
$this->SendMatomoEvent();
|
||||
}
|
||||
|
||||
function IsRangeRequest() : bool {
|
||||
function GetRequestRange($len) : ?object {
|
||||
if(isset($_SERVER['HTTP_RANGE'])) {
|
||||
$rby = explode('=', $_SERVER['HTTP_RANGE']);
|
||||
$rbv = explode('-', $rby[1]);
|
||||
if($rbv[0] != '0'){
|
||||
return True;
|
||||
}
|
||||
return (object)array(
|
||||
"start" => intval($rbv[0]),
|
||||
"end" => intval($rbv[1] == "" ? $len : $rbv[1])
|
||||
);
|
||||
}
|
||||
|
||||
return False;
|
||||
return null;
|
||||
}
|
||||
|
||||
function IsRangeRequest() : bool {
|
||||
$range = $this->GetRequestRange(0);
|
||||
if($range !== null){
|
||||
if($range->start != 0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function SendMatomoEvent() : void {
|
||||
|
@ -3,6 +3,7 @@
|
||||
public $status = 0;
|
||||
public $msg;
|
||||
public $id;
|
||||
public $sync;
|
||||
}
|
||||
|
||||
class Upload implements RequestHandler {
|
||||
@ -39,8 +40,7 @@
|
||||
$id = $this->SaveUpload($bf);
|
||||
|
||||
//sync to other servers
|
||||
$this->SyncFileUpload($id);
|
||||
|
||||
$rsp->sync = $this->SyncFileUpload($id);
|
||||
$rsp->status = 200;
|
||||
$rsp->id = $id;
|
||||
} else {
|
||||
@ -52,16 +52,18 @@
|
||||
echo json_encode($rsp);
|
||||
}
|
||||
|
||||
function SyncFileUpload($id) : void {
|
||||
function SyncFileUpload($id) : array {
|
||||
$redis = StaticRedis::$Instance;
|
||||
$sync_hosts = $redis->sMembers(REDIS_PREFIX . 'sync-hosts');
|
||||
if($sync_hosts !== False) {
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
|
||||
$status_codes = [];
|
||||
foreach($sync_hosts as $host) {
|
||||
Sync::SyncFile($id, $fs->GetAbsoluteFilePath($id), $host);
|
||||
$status_codes[] = Sync::SyncFile($id, $fs->GetAbsoluteFilePath($id), $host);
|
||||
}
|
||||
|
||||
return $status_codes;
|
||||
/*
|
||||
$sync_threads = array();
|
||||
foreach($sync_hosts as $host) {
|
||||
@ -77,6 +79,8 @@
|
||||
$thread->join();
|
||||
}*/
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
function SaveUpload($bf) : string {
|
||||
|
Loading…
x
Reference in New Issue
Block a user