mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-11 18:56:21 +00:00
added admin panel with stats and logs
This commit is contained in:
35
DEV.md
Normal file
35
DEV.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Folder structure
|
||||||
|
All uploads will be created as folders with the name of the file in the `./data` directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
data/
|
||||||
|
data/<file_name>/
|
||||||
|
data/<file_name>/<file_name> # the actual file eg abc1
|
||||||
|
data/<file_name>/meta.json # file containing the metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metadata file
|
||||||
|
The metadata file will contain data like in the following example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mime": "image\/jpeg",
|
||||||
|
"size": 504921,
|
||||||
|
"size_human": "493.09 KB",
|
||||||
|
"original_filename": "PXL_20250511_122019042-POP_OUT.jpg",
|
||||||
|
"hash": "0gfmeo.jpg",
|
||||||
|
"sha1": "7dc14ad3f0c273ce0188aeef19a6104e81ba67dd",
|
||||||
|
"uploaded": 1747557347,
|
||||||
|
"ip": "::1",
|
||||||
|
"useragent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/136.0.0.0 Safari\/537.36",
|
||||||
|
"delete_code": "z94fd5tyto1u2bww23kec0f52irb7x49",
|
||||||
|
"delete_url": "http:\/\/localhost:8080\/delete_z94fd5tyto1u2bww23kec0f52irb7x49\/0gfmeo.jpg",
|
||||||
|
"remote_port": "35856"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Redis index
|
||||||
|
If redis caching is enabled, this structure will be created on the fly:
|
||||||
|
|
||||||
|
- cache:byurl:<url> => <content controller>;<file_name> # cached response
|
||||||
|
- served:<file_name> => number of views # view count
|
||||||
3
TODO.md
3
TODO.md
@@ -7,4 +7,5 @@
|
|||||||
- [ ] Add a way for users to report content
|
- [ ] Add a way for users to report content
|
||||||
- [ ] Review reports
|
- [ ] Review reports
|
||||||
- [ ] Delete content if needed in a way that the user can't reupload it
|
- [ ] Delete content if needed in a way that the user can't reupload it
|
||||||
- [ ] Add a way to delete content that is older than X days
|
- [ ] Add a way to delete content that is older than X days
|
||||||
|
- [ ] Bruteforce protection for admin login
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ services:
|
|||||||
pictshare:
|
pictshare:
|
||||||
image: 'HaschekSolutions/pictshare:3'
|
image: 'HaschekSolutions/pictshare:3'
|
||||||
environment:
|
environment:
|
||||||
- SERVER_NAME=:80 # required by caddy
|
|
||||||
- URL=http://localhost:8080/
|
- URL=http://localhost:8080/
|
||||||
- MAX_UPLOAD_SIZE=20 #in MB
|
- MAX_UPLOAD_SIZE=20 #in MB
|
||||||
# Security settings
|
# Security settings
|
||||||
- ADMIN_PASSWORD= # password for the admin panel. if empty, admin panel is disabled
|
- ADMIN_PASSWORD= # password for the admin panel. if empty, admin panel is disabled
|
||||||
- ALLOWED_SUBNET= #IP address or subnet mask to allow upload to the server
|
- ALLOWED_SUBNET= #IP address or subnet mask to allow upload and access to the admin panel
|
||||||
- CONTENTCONTROLLERS= # limit uploaded file types
|
- CONTENTCONTROLLERS= # limit uploaded file types
|
||||||
- MASTER_DELETE_CODE= # code to delete all files
|
- MASTER_DELETE_CODE= # code to delete all files
|
||||||
- MASTER_DELETE_IP= # IP address to delete all files even without delete code
|
- MASTER_DELETE_IP= # IP address to delete all files even without delete code
|
||||||
@@ -43,6 +42,7 @@ services:
|
|||||||
- REDIS_CACHING=true
|
- REDIS_CACHING=true
|
||||||
- REDIS_SERVER=/run/redis/redis.sock
|
- REDIS_SERVER=/run/redis/redis.sock
|
||||||
- REDIS_PORT=0
|
- REDIS_PORT=0
|
||||||
|
- SERVER_NAME=:80 # required by caddy
|
||||||
ports:
|
ports:
|
||||||
- 8080:80
|
- 8080:80
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
102
src/inc/core.php
102
src/inc/core.php
@@ -29,26 +29,54 @@ function architect($u)
|
|||||||
{
|
{
|
||||||
$forbidden = true;
|
$forbidden = true;
|
||||||
}
|
}
|
||||||
return renderTemplate('main.html.php',['forbidden'=>$forbidden]);
|
return renderTemplate('index.html.php',['main'=>renderTemplate('main.html.php',['forbidden'=>$forbidden])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// admin logic
|
// admin logic
|
||||||
if($u[0] == 'admin' && defined('ADMIN_PASSWORD') && ADMIN_PASSWORD != '')
|
if($u[0] == 'admin' && defined('ADMIN_PASSWORD') && ADMIN_PASSWORD != '')
|
||||||
{
|
{
|
||||||
|
// block checks
|
||||||
|
if (defined('ALLOWED_SUBNET') && ALLOWED_SUBNET != '' && !isIPInRange(getUserIP(), ALLOWED_SUBNET))
|
||||||
|
return;
|
||||||
|
else if (defined('MASTER_DELETE_IP') && MASTER_DELETE_IP != '' && !isIPInRange(getUserIP(), MASTER_DELETE_IP))
|
||||||
|
return;
|
||||||
session_start();
|
session_start();
|
||||||
if($_REQUEST['password'] && $_REQUEST['password']== ADMIN_PASSWORD)
|
switch($u[1]){
|
||||||
{
|
case 'stats':
|
||||||
$_SESSION['admin'] = true;
|
if(!$_SESSION['admin'])
|
||||||
|
header('Location: /admin');
|
||||||
|
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.stats.html.php',['stats'=>getStats()])]);
|
||||||
|
case 'logs':
|
||||||
|
if(!$_SESSION['admin'])
|
||||||
|
header('Location: /admin');
|
||||||
|
switch($u[2])
|
||||||
|
{
|
||||||
|
case 'app':
|
||||||
|
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs-table.html.php',['type'=>'app','logs'=>getLogs('app',$u[3])])]);
|
||||||
|
case 'error':
|
||||||
|
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs-table.html.php',['type'=>'error','logs'=>getLogs('error',$u[3])])]);
|
||||||
|
case 'views':
|
||||||
|
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs-table.html.php',['type'=>'views','logs'=>getLogs('views',$u[3])])]);
|
||||||
|
default:
|
||||||
|
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs.html.php')]);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if($_REQUEST['password'] && $_REQUEST['password']== ADMIN_PASSWORD)
|
||||||
|
{
|
||||||
|
$_SESSION['admin'] = true;
|
||||||
|
}
|
||||||
|
if($_SESSION['admin'])
|
||||||
|
{
|
||||||
|
if(isset($_REQUEST['logout']))
|
||||||
|
{
|
||||||
|
unset($_SESSION['admin']);
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.html.php')]);
|
||||||
}
|
}
|
||||||
if($_SESSION['admin'])
|
|
||||||
{
|
|
||||||
if(isset($_REQUEST['logout']))
|
|
||||||
{
|
|
||||||
unset($_SESSION['admin']);
|
|
||||||
session_destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return renderTemplate('admin.html.php');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//check cache
|
//check cache
|
||||||
@@ -59,7 +87,7 @@ function architect($u)
|
|||||||
{
|
{
|
||||||
list($cc, $hash) = explode(';', $cache_data);
|
list($cc, $hash) = explode(';', $cache_data);
|
||||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||||
addToLog("Cache hit: ".getUserIP()." viewed $hash\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
addToLog(getUserIP()."\tviewed\t$hash\tFrom cache. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||||
$GLOBALS['redis']->incr("served:$hash");
|
$GLOBALS['redis']->incr("served:$hash");
|
||||||
return (new $cc())->handleHash($hash,$u);
|
return (new $cc())->handleHash($hash,$u);
|
||||||
}
|
}
|
||||||
@@ -73,7 +101,7 @@ function architect($u)
|
|||||||
{
|
{
|
||||||
$hash = $el;
|
$hash = $el;
|
||||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||||
addToLog(getUserIP()." viewed $hash\tIt was locally found\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
addToLog(getUserIP()."\tviewed\t$hash\tIt was locally found. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// if we don't have a hash yet but the element looks like it could be a hash
|
// if we don't have a hash yet but the element looks like it could be a hash
|
||||||
@@ -93,7 +121,7 @@ function architect($u)
|
|||||||
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
||||||
|
|
||||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||||
addToLog(getUserIP()." viewed $hash\tIt was found in Storage Controller $contr\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
addToLog(getUserIP()."\tviewed\t$hash\tIt was found in Storage Controller $contr. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||||
|
|
||||||
break; // we break here because we already have the file. no need to check other storage controllers
|
break; // we break here because we already have the file. no need to check other storage controllers
|
||||||
}
|
}
|
||||||
@@ -110,7 +138,7 @@ function architect($u)
|
|||||||
unlink(ROOT.DS.'tmp'.DS.$el.'.enc');
|
unlink(ROOT.DS.'tmp'.DS.$el.'.enc');
|
||||||
|
|
||||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||||
addToLog(getUserIP()." viewed $hash\tIt was found encrypted in Storage Controller $contr\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
addToLog(getUserIP()."\tviewed\t$hash\tIt was found encrypted in Storage Controller $contr. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||||
|
|
||||||
break; // we break here because we already have the file. no need to check other storage controllers
|
break; // we break here because we already have the file. no need to check other storage controllers
|
||||||
}
|
}
|
||||||
@@ -125,7 +153,7 @@ function architect($u)
|
|||||||
if((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u) )
|
if((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u) )
|
||||||
{
|
{
|
||||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||||
addToLog(getUserIP()." requested ".implode("/",$u)."\tIt's a dynamic image handled by $cc\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
addToLog(getUserIP()." requested ".implode("/",$u)."\tIt's a dynamic image handled by $cc. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||||
$hash = true;
|
$hash = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -833,7 +861,7 @@ function getDeleteCodeOfHash($hash)
|
|||||||
|
|
||||||
function getMetadataOfHash($hash)
|
function getMetadataOfHash($hash)
|
||||||
{
|
{
|
||||||
$metadata = array();
|
$metadata = [];
|
||||||
$metadatafile = getDataDir().DS.$hash.DS.'meta.json';
|
$metadatafile = getDataDir().DS.$hash.DS.'meta.json';
|
||||||
if(file_exists($metadatafile))
|
if(file_exists($metadatafile))
|
||||||
{
|
{
|
||||||
@@ -1107,4 +1135,40 @@ function addToLog($data,$logfile=ROOT.DS.'logs/app.log')
|
|||||||
$fp = fopen($logfile,'a');
|
$fp = fopen($logfile,'a');
|
||||||
fwrite($fp,date("d.m.y H:i")."\t[".getURL()."] | ".$data."\n");
|
fwrite($fp,date("d.m.y H:i")."\t[".getURL()."] | ".$data."\n");
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStats(){
|
||||||
|
$stats = array();
|
||||||
|
$stats['total_files'] = 0;
|
||||||
|
$stats['total_size'] = 0;
|
||||||
|
$stats['total_files'] = count(glob(getDataDir().DS.'*', GLOB_ONLYDIR));
|
||||||
|
foreach (glob(getDataDir().DS.'*') as $dir) {
|
||||||
|
if (is_dir($dir)) {
|
||||||
|
$stats['hashes'][basename($dir)] = [
|
||||||
|
'size' => filesize($dir.DS.basename($dir)),
|
||||||
|
'files' => count(glob($dir.DS.'*')),
|
||||||
|
'views' => $GLOBALS['redis']->get('served:'.basename($dir)),
|
||||||
|
'metadata' => getMetadataOfHash(basename($dir)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogs($type='app',$filter=false)
|
||||||
|
{
|
||||||
|
$logs = array();
|
||||||
|
$logfile = ROOT.DS.'logs/'.$type.'.log';
|
||||||
|
if($type && file_exists($logfile))
|
||||||
|
{
|
||||||
|
$handle = fopen($logfile, "r");
|
||||||
|
if ($handle) {
|
||||||
|
while (($line = fgets($handle)) !== false) {
|
||||||
|
if($filter && strpos($line,$filter)===false) continue;
|
||||||
|
$logs[] = $line;
|
||||||
|
}
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $logs;
|
||||||
}
|
}
|
||||||
@@ -1,93 +1,25 @@
|
|||||||
<!DOCTYPE html>
|
<h2>Admin Panel</h2>
|
||||||
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
|
<?php if (!$_SESSION['admin']) { ?>
|
||||||
<!--[if (gt IEMobile 7)|!(IEMobile)]><!-->
|
<form method="post" action="/admin">
|
||||||
<html class="no-js"> <!--<![endif]-->
|
<div class="input-group mb-3">
|
||||||
|
<input type="password" class="form-control" name="password" placeholder="Password" aria-label="Password" aria-describedby="btn-addn">
|
||||||
<head>
|
<button class="btn btn-outline-secondary" type="submit" id="btn-addn">Login</button>
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>PictShare - the smart CDN</title>
|
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
|
||||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- PictShare overwrites -->
|
|
||||||
<link href="/css/pictshare.css" rel="stylesheet">
|
|
||||||
<link href="/css/dropzone.css" rel="stylesheet">
|
|
||||||
<link href="/css/hljs-dracula.css" rel="stylesheet">
|
|
||||||
|
|
||||||
|
|
||||||
<!-- github-fork-ribbon-css
|
|
||||||
https://simonwhitaker.github.io/github-fork-ribbon-css/ -->
|
|
||||||
<link href="/css/gh-fork-ribbon.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
|
||||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var maxUploadFileSize = <?php echo (int)(ini_get('upload_max_filesize')); ?>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<meta name="description" content="Free image sharing, linking and tracking">
|
|
||||||
<meta name="keywords" content="image, share, hosting, free">
|
|
||||||
<meta name="robots" content="index, follow">
|
|
||||||
<meta name="copyright" content="Haschek Solutions">
|
|
||||||
<meta name="language" content="EN,DE">
|
|
||||||
<meta name="author" content="Haschek Solutions">
|
|
||||||
<meta name="distribution" content="global">
|
|
||||||
<meta name="rating" content="general">
|
|
||||||
|
|
||||||
</HEAD>
|
|
||||||
|
|
||||||
<BODY>
|
|
||||||
|
|
||||||
<div class="container" id="headcontainer">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<a href="/"><img src="/css/imgs/logo/horizontalv3.png" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($_SESSION['admin']) { ?>
|
||||||
|
<div class="alert alert-success" role="alert">You are logged in as admin</div>
|
||||||
|
<form method="post" action="/admin">
|
||||||
|
<button type="submit" name="logout" class="btn btn-danger">Logout</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
<div class="container">
|
<ul class="nav">
|
||||||
<h2>Admin Panel</h2>
|
<li class="nav-item">
|
||||||
<?php if (!$_SESSION['admin']) { ?>
|
<a class="nav-link" aria-current="page" href="/admin/stats">Stats</a>
|
||||||
<form method="post" action="/admin">
|
</li>
|
||||||
<div class="input-group mb-3">
|
<li class="nav-item">
|
||||||
<input type="password" class="form-control" name="password" placeholder="Password" aria-label="Password" aria-describedby="btn-addn">
|
<a class="nav-link" href="/admin/logs">Logs</a>
|
||||||
<button class="btn btn-outline-secondary" type="submit" id="btn-addn">Login</button>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</form>
|
|
||||||
<?php } ?>
|
|
||||||
<?php if ($_SESSION['admin']) { ?>
|
|
||||||
<div class="alert alert-success" role="alert">You are logged in as admin</div>
|
|
||||||
<form method="post" action="/admin">
|
|
||||||
<button type="submit" name="logout" class="btn btn-danger">Logout</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<ul class="nav">
|
<?php } ?>
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" aria-current="page" href="#">Stats</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#">Review</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<footer class="text-center">(c)<?php echo date("y"); ?> by<br /><a href="https://haschek.solutions" target="_blank"><img height="30" src="/css/imgs/hs_logo.png" /></a></footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="/js/pictshare.js"></script>
|
|
||||||
</BODY>
|
|
||||||
|
|
||||||
</HTML>
|
|
||||||
15
src/templates/admin.logs-table.html.php
Normal file
15
src/templates/admin.logs-table.html.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="/admin/logs">Logs</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page"><?=$type?></li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1><?= ucfirst($type) ?></h1>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<code><?php foreach($logs as $log):
|
||||||
|
echo $log;
|
||||||
|
endforeach; ?></code>
|
||||||
|
</pre>
|
||||||
10
src/templates/admin.logs.html.php
Normal file
10
src/templates/admin.logs.html.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Logs</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<a class="btn btn-primary" href="/admin/logs/app">General App logs</a>
|
||||||
|
<a class="btn btn-primary" href="/admin/logs/views">Views</a>
|
||||||
|
<a class="btn btn-primary" href="/admin/logs/error">Errors</a>
|
||||||
41
src/templates/admin.stats.html.php
Normal file
41
src/templates/admin.stats.html.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Stats</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1>Stats</h1>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover" data-toggle="table" data-search="true">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" data-sortable="true">Hash</th>
|
||||||
|
<th scope="col" data-sortable="true">Views</th>
|
||||||
|
<th scope="col" data-sortable="true">Original Filename</th>
|
||||||
|
<th scope="col" data-sortable="true">MIME type</th>
|
||||||
|
<th scope="col" data-sortable="true">Created at</th>
|
||||||
|
<th scope="col" data-sortable="true">Uploader IP</th>
|
||||||
|
<?php if(defined('LOG_VIEWS') && LOG_VIEWS==true): ?>
|
||||||
|
<th scope="col" data-sortable="true">List views</th>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach($stats['hashes'] as $hash => $data): ?>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><a href="/<?=$hash?>"><?=$hash?></a></th>
|
||||||
|
<td><?=$data['views']?:0?></td>
|
||||||
|
<td><?=$data['metadata']['original_filename']?></td>
|
||||||
|
<td><?=$data['metadata']['mime']?></td>
|
||||||
|
<td><?=date("Y-m-d H:i",$data['metadata']['uploaded'])?></td>
|
||||||
|
<td><?=$data['metadata']['ip']?></td>
|
||||||
|
<?php if(defined('LOG_VIEWS') && LOG_VIEWS==true): ?>
|
||||||
|
<td scope="col" data-sortable="true"><a class="btn btn-secondary" href="/admin/logs/views/<?=$hash?>">View logs</a></td>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
75
src/templates/index.html.php
Normal file
75
src/templates/index.html.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="no-js"> <!--<![endif]-->
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>PictShare - the smart CDN</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap -->
|
||||||
|
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- PictShare overwrites -->
|
||||||
|
<link href="/css/pictshare.css" rel="stylesheet">
|
||||||
|
<link href="/css/dropzone.css" rel="stylesheet">
|
||||||
|
<link href="/css/bootstrap-table.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/hljs-dracula.css" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- github-fork-ribbon-css
|
||||||
|
https://simonwhitaker.github.io/github-fork-ribbon-css/ -->
|
||||||
|
<link href="/css/gh-fork-ribbon.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||||
|
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
|
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var maxUploadFileSize = <?php echo (int)(ini_get('upload_max_filesize')); ?>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta name="description" content="Free image sharing, linking and tracking">
|
||||||
|
<meta name="keywords" content="image, share, hosting, free">
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<meta name="copyright" content="Haschek Solutions">
|
||||||
|
<meta name="language" content="EN,DE">
|
||||||
|
<meta name="author" content="Haschek Solutions">
|
||||||
|
<meta name="distribution" content="global">
|
||||||
|
<meta name="rating" content="general">
|
||||||
|
|
||||||
|
</HEAD>
|
||||||
|
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<div class="container" id="headcontainer">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<a href="/"><img src="/css/imgs/logo/horizontalv3.png" /></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="main" class="container hv-100">
|
||||||
|
<?=$main;?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<div class="container text-center">
|
||||||
|
<p>created by <a href="https://haschek.solutions" target="_blank"><img height="30" src="/css/imgs/hs_logo.png" /></a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/js/jquery.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="/js/bootstrap-table.min.js"></script>
|
||||||
|
<script src="/js/dropzone.js"></script>
|
||||||
|
<script src="/js/highlight.pack.js"></script>
|
||||||
|
<script src="/js/pictshare.js"></script>
|
||||||
|
</BODY>
|
||||||
|
|
||||||
|
</HTML>
|
||||||
@@ -1,161 +1,100 @@
|
|||||||
<!DOCTYPE html>
|
<?php
|
||||||
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
|
if (file_exists(ROOT . DS . 'notice.txt'))
|
||||||
<!--[if (gt IEMobile 7)|!(IEMobile)]><!-->
|
echo '<div class="alert alert-warning" role="alert">' . file_get_contents(ROOT . DS . 'notice.txt') . '</div>';
|
||||||
<html class="no-js"> <!--<![endif]-->
|
?>
|
||||||
|
<div class="well">
|
||||||
|
<?php if ($forbidden === true) { ?>
|
||||||
|
|
||||||
<head>
|
<h2>Upload forbidden</h2>
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>PictShare - the smart CDN</title>
|
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<p>Due to configured restrictions, you are not allowed to upload files at this time</p>
|
||||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- PictShare overwrites -->
|
<?php } else { ?>
|
||||||
<link href="/css/pictshare.css" rel="stylesheet">
|
<div id="uploadinfo"></div>
|
||||||
<link href="/css/dropzone.css" rel="stylesheet">
|
<p>
|
||||||
<link href="/css/hljs-dracula.css" rel="stylesheet">
|
Max Upload size: <?= (int)(ini_get('upload_max_filesize')) ?>MB / File<br />
|
||||||
|
Allowed file types: <?= implode(', ', getAllContentFiletypes()) ?>
|
||||||
|
<?php
|
||||||
<!-- github-fork-ribbon-css
|
if (defined('UPLOAD_CODE') && UPLOAD_CODE != ''): ?>
|
||||||
https://simonwhitaker.github.io/github-fork-ribbon-css/ -->
|
<br>Upload Code: <input type="password" id="uploadcode" />
|
||||||
<link href="/css/gh-fork-ribbon.min.css" rel="stylesheet">
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
<form class="dropzone well" id="dropzone" method="post" action="/api/upload" enctype="multipart/form-data">
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
<div class="fallback">
|
||||||
<!--[if lt IE 9]>
|
<input name="file" type="file" multiple />
|
||||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
|
||||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var maxUploadFileSize = <?php echo (int)(ini_get('upload_max_filesize')); ?>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<meta name="description" content="Free image sharing, linking and tracking">
|
|
||||||
<meta name="keywords" content="image, share, hosting, free">
|
|
||||||
<meta name="robots" content="index, follow">
|
|
||||||
<meta name="copyright" content="Haschek Solutions">
|
|
||||||
<meta name="language" content="EN,DE">
|
|
||||||
<meta name="author" content="Haschek Solutions">
|
|
||||||
<meta name="distribution" content="global">
|
|
||||||
<meta name="rating" content="general">
|
|
||||||
|
|
||||||
</HEAD>
|
|
||||||
|
|
||||||
<BODY>
|
|
||||||
<a class="github-fork-ribbon left-top" href="https://github.com/HaschekSolutions/pictshare" data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a>
|
|
||||||
|
|
||||||
<div class="container" id="headcontainer">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<a href="/"><img src="/css/imgs/logo/horizontalv3.png" /></a>
|
|
||||||
<?php
|
|
||||||
if (file_exists(ROOT . DS . 'notice.txt'))
|
|
||||||
echo '<div class="alert alert-warning" role="alert">' . file_get_contents(ROOT . DS . 'notice.txt') . '</div>';
|
|
||||||
?>
|
|
||||||
<div class="well">
|
|
||||||
<?php if ($forbidden === true) { ?>
|
|
||||||
|
|
||||||
<h2>Upload forbidden</h2>
|
|
||||||
|
|
||||||
<p>Due to configured restrictions, you are not allowed to upload files at this time</p>
|
|
||||||
|
|
||||||
<?php } else { ?>
|
|
||||||
<div id="uploadinfo"></div>
|
|
||||||
<p>
|
|
||||||
Max Upload size: <?=(int)(ini_get('upload_max_filesize'))?>MB / File<br/>
|
|
||||||
Allowed file types: <?= implode(', ', getAllContentFiletypes()) ?>
|
|
||||||
<?php
|
|
||||||
if (defined('UPLOAD_CODE') && UPLOAD_CODE != ''):?>
|
|
||||||
<br>Upload Code: <input type="password" id="uploadcode" />
|
|
||||||
<?php endif; ?>
|
|
||||||
</p>
|
|
||||||
<form class="dropzone well" id="dropzone" method="post" action="/api/upload" enctype="multipart/form-data">
|
|
||||||
<div class="fallback">
|
|
||||||
<input name="file" type="file" multiple />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<h2 id="api" class="section-heading">Using PictShare</h2>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h3>Basics</h3>
|
||||||
|
<p>
|
||||||
|
When you upload an image you'll get a link like this:
|
||||||
|
<pre><code class="url"><?= getURL() ?>abcef123.jpg</code></pre>
|
||||||
|
You can modify the size of the image by adding a size parameter to the URL. For example this will render the image in 800x600:
|
||||||
|
<pre><code class="url"><?= getURL() ?>800x600/abcef123.jpg</code></pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you want to force the size to really be 800x600, you can use the "forcesize" parameter. It will still keep the aspect ratio but zoom in as needed to fit the dimensions
|
||||||
|
<pre><code class="url"><?= getURL() ?>800x600/forcesize/abcef123.jpg</code></pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>There are many more of these <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/MODIFIERS.md">modifiers</a> and even <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/IMAGEFILTERS.md">filters</a> available</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="no-print border border-primary border-2 opacity-50">
|
||||||
|
|
||||||
|
<h2 id="api" class="section-heading">Using the API</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h2>Basics</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
All API calls are done via GET or POST requests. The API will return JSON encoded data.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Base URL
|
||||||
|
<pre><code class="url"><?= getURL() ?>api</code></pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="col-6">
|
||||||
<h2 id="api" class="section-heading">Using PictShare</h2>
|
<h2>Error handling</h2>
|
||||||
|
When the status is err there will always be a field "reason" that explains what went wrong.
|
||||||
<div class="row">
|
<pre><code class="json">
|
||||||
<div class="col-6">
|
|
||||||
<h3>Basics</h3>
|
|
||||||
<p>
|
|
||||||
When you upload an image you'll get a link like this:<pre><code class="url"><?= getURL() ?>abcef123.jpg</code></pre>
|
|
||||||
You can modify the size of the image by adding a size parameter to the URL. For example this will render the image in 800x600:
|
|
||||||
<pre><code class="url"><?= getURL() ?>800x600/abcef123.jpg</code></pre>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
If you want to force the size to really be 800x600, you can use the "forcesize" parameter. It will still keep the aspect ratio but zoom in as needed to fit the dimensions
|
|
||||||
<pre><code class="url"><?= getURL() ?>800x600/forcesize/abcef123.jpg</code></pre>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>There are many more of these <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/MODIFIERS.md">modifiers</a> and even <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/IMAGEFILTERS.md">filters</a> available</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="no-print border border-primary border-2 opacity-50">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<h2 id="api" class="section-heading">Using the API</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<h2>Basics</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
All API calls are done via GET or POST requests. The API will return JSON encoded data.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
Base URL
|
|
||||||
<pre><code class="url"><?= getURL() ?>api</code></pre>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-6">
|
|
||||||
<h2>Error handling</h2>
|
|
||||||
When the status is err there will always be a field "reason" that explains what went wrong.
|
|
||||||
<pre><code class="json">
|
|
||||||
{
|
{
|
||||||
"status": "err",
|
"status": "err",
|
||||||
"reason": "File not a valid image"
|
"reason": "File not a valid image"
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<h2>Uploading an image</h2>
|
<h2>Uploading an image</h2>
|
||||||
|
|
||||||
API call
|
API call
|
||||||
<pre><code class="url">/upload</code></pre>
|
<pre><code class="url">/upload</code></pre>
|
||||||
|
|
||||||
<p>You can post a file using the POST variable <span class="badge text-bg-secondary">file</span></p>
|
<p>You can post a file using the POST variable <span class="badge text-bg-secondary">file</span></p>
|
||||||
|
|
||||||
CURL example
|
CURL example
|
||||||
<pre><code class="bash">curl -s -F "file=@myphoto.jpg" "<?= getURL() ?>upload"</code></pre>
|
<pre><code class="bash">curl -s -F "file=@myphoto.jpg" "<?= getURL() ?>upload"</code></pre>
|
||||||
|
|
||||||
Output
|
Output
|
||||||
<pre><code class="json">
|
<pre><code class="json">
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"hash": "7eli4d.jpg",
|
"hash": "7eli4d.jpg",
|
||||||
@@ -164,21 +103,21 @@
|
|||||||
"delete_code": "jxgat3wze8lmn9sqwxy4x32p2xm7211g",
|
"delete_code": "jxgat3wze8lmn9sqwxy4x32p2xm7211g",
|
||||||
"delete_url": "http://localhost:8080/delete_jxgat3wze8lmn9sqwxy4x32p2xm7211g/7eli4d.jpg"
|
"delete_url": "http://localhost:8080/delete_jxgat3wze8lmn9sqwxy4x32p2xm7211g/7eli4d.jpg"
|
||||||
}</code></pre>
|
}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<h2>Grabbing a URL</h2>
|
<h2>Grabbing a URL</h2>
|
||||||
|
|
||||||
API call
|
API call
|
||||||
<pre><code class="url">/upload?url=<span class="badge text-bg-secondary">url of image</span></code></pre>
|
<pre><code class="url">/upload?url=<span class="badge text-bg-secondary">url of image</span></code></pre>
|
||||||
|
|
||||||
<p>You can specify a URL in the POST or GET variable <span class="badge text-bg-secondary">url</span>
|
<p>You can specify a URL in the POST or GET variable <span class="badge text-bg-secondary">url</span>
|
||||||
that the call will try to download and process.</p>
|
that the call will try to download and process.</p>
|
||||||
|
|
||||||
CURL example
|
CURL example
|
||||||
<pre><code class="bash">curl -s "<?= getURL() ?>api/upload?url=https://pictshare.net/d2j1e1.png</code></pre>
|
<pre><code class="bash">curl -s "<?= getURL() ?>api/upload?url=https://pictshare.net/d2j1e1.png</code></pre>
|
||||||
|
|
||||||
Output
|
Output
|
||||||
<pre><code class="json">
|
<pre><code class="json">
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"hash": "ysj455.webp",
|
"hash": "ysj455.webp",
|
||||||
@@ -188,24 +127,24 @@
|
|||||||
"delete_url": "http://localhost:8080/delete_4l0w04l4s42xddt2s5mrj1wikxz11l5z/ysj455.webp"
|
"delete_url": "http://localhost:8080/delete_4l0w04l4s42xddt2s5mrj1wikxz11l5z/ysj455.webp"
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<h2>Uploading Base64 encoded content</h2>
|
<h2>Uploading Base64 encoded content</h2>
|
||||||
<p>It's also possible to upload supported files via Base64 strings by providing the <span class="badge text-bg-secondary">base64</span> http parameter</p>
|
<p>It's also possible to upload supported files via Base64 strings by providing the <span class="badge text-bg-secondary">base64</span> http parameter</p>
|
||||||
|
|
||||||
API call
|
API call
|
||||||
<pre><code class="url">/upload/?base64=<span class="badge text-bg-secondary">base64 encoded string</span></code></pre>
|
<pre><code class="url">/upload/?base64=<span class="badge text-bg-secondary">base64 encoded string</span></code></pre>
|
||||||
|
|
||||||
CURL example
|
CURL example
|
||||||
<pre><code class="bash">(echo -n "base64="; echo -n "data:image/jpeg;base64,$(base64 -w 0 Screenshot3.jpg)") | curl --data @- <?= getURL() ?>api/upload</code></pre>
|
<pre><code class="bash">(echo -n "base64="; echo -n "data:image/jpeg;base64,$(base64 -w 0 Screenshot3.jpg)") | curl --data @- <?= getURL() ?>api/upload</code></pre>
|
||||||
|
|
||||||
Output
|
Output
|
||||||
<pre><code class="json">
|
<pre><code class="json">
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"hash": "5e6alk.jpg",
|
"hash": "5e6alk.jpg",
|
||||||
@@ -215,23 +154,9 @@
|
|||||||
"delete_url": "http://localhost:8080/delete_7ha2b5ccvsuvdd3qdnegzb2zqa9zxb5t/5e6alk.jpg"
|
"delete_url": "http://localhost:8080/delete_7ha2b5ccvsuvdd3qdnegzb2zqa9zxb5t/5e6alk.jpg"
|
||||||
}</code></pre>
|
}</code></pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<div class="container">
|
|
||||||
<footer>(c)<?php echo date("y"); ?> by<br /><a href="https://haschek.solutions" target="_blank"><img height="30" src="/css/imgs/hs_logo.png" /></a></footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="/js/dropzone.js"></script>
|
|
||||||
<script src="/js/highlight.pack.js"></script>
|
|
||||||
<script src="/js/pictshare.js"></script>
|
|
||||||
</BODY>
|
|
||||||
|
|
||||||
</HTML>
|
|
||||||
10
web/css/bootstrap-table.min.css
vendored
Normal file
10
web/css/bootstrap-table.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ body {
|
|||||||
background-image: url('imgs/bg.png');
|
background-image: url('imgs/bg.png');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
padding-bottom: 60px; /* Space for the footer */
|
||||||
}
|
}
|
||||||
|
|
||||||
#headcontainer
|
#headcontainer
|
||||||
@@ -32,4 +33,13 @@ body {
|
|||||||
.box__success,
|
.box__success,
|
||||||
.box__error {
|
.box__error {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 60px; /* Set the fixed height of the footer here */
|
||||||
|
line-height: 60px; /* Vertically center the text there */
|
||||||
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
10
web/js/bootstrap-table.min.js
vendored
Normal file
10
web/js/bootstrap-table.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
web/js/bootstrap.bundle.min.js
vendored
Normal file
7
web/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
web/js/jquery.min.js
vendored
Normal file
2
web/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -2,90 +2,92 @@ Dropzone.autoDiscover = false;
|
|||||||
hljs.initHighlightingOnLoad();
|
hljs.initHighlightingOnLoad();
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
var myDropzone = new Dropzone("#dropzone");
|
if (document.getElementById("dropzone") != null) {
|
||||||
if (typeof maxUploadFileSize !== "undefined")
|
var myDropzone = new Dropzone("#dropzone");
|
||||||
myDropzone.options.maxFilesize = maxUploadFileSize;
|
if (typeof maxUploadFileSize !== "undefined")
|
||||||
|
myDropzone.options.maxFilesize = maxUploadFileSize;
|
||||||
|
|
||||||
myDropzone.options.timeout = 0;
|
myDropzone.options.timeout = 0;
|
||||||
|
|
||||||
myDropzone.on("sending", function(file, xhr, formData) {
|
myDropzone.on("sending", function (file, xhr, formData) {
|
||||||
var uploadCodeElem = document.getElementById("uploadcode");
|
var uploadCodeElem = document.getElementById("uploadcode");
|
||||||
if (uploadCodeElem)
|
if (uploadCodeElem)
|
||||||
formData.append("uploadcode", uploadCodeElem.value);
|
formData.append("uploadcode", uploadCodeElem.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
myDropzone.on('error', function(file, response) {
|
myDropzone.on('error', function (file, response) {
|
||||||
var uploadInfo = document.getElementById("uploadinfo");
|
var uploadInfo = document.getElementById("uploadinfo");
|
||||||
if (response == null || response == "null") {
|
if (response == null || response == "null") {
|
||||||
uploadInfo.insertAdjacentHTML("beforeend",
|
|
||||||
renderMessage(
|
|
||||||
"Error uploading " + file.name,
|
|
||||||
"Reason is unknown :(",
|
|
||||||
"danger"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (response.status == 'err') {
|
|
||||||
uploadInfo.insertAdjacentHTML("beforeend",
|
uploadInfo.insertAdjacentHTML("beforeend",
|
||||||
renderMessage(
|
renderMessage(
|
||||||
"Error uploading " + file.name,
|
"Error uploading " + file.name,
|
||||||
"Reason: " + response.reason,
|
"Reason is unknown :(",
|
||||||
"danger"
|
"danger"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
if (response.status == 'err') {
|
||||||
|
uploadInfo.insertAdjacentHTML("beforeend",
|
||||||
|
renderMessage(
|
||||||
|
"Error uploading " + file.name,
|
||||||
|
"Reason: " + response.reason,
|
||||||
|
"danger"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
uploadInfo.insertAdjacentHTML("beforeend",
|
||||||
|
renderMessage(
|
||||||
|
"Error uploading " + file.name,
|
||||||
|
"Reason: " + response,
|
||||||
|
"danger"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
myDropzone.on("success", function (file, response) {
|
||||||
|
console.log("raw response: " + response);
|
||||||
|
var uploadInfo = document.getElementById("uploadinfo");
|
||||||
|
if (response == null || response == "null") {
|
||||||
uploadInfo.insertAdjacentHTML("beforeend",
|
uploadInfo.insertAdjacentHTML("beforeend",
|
||||||
renderMessage(
|
renderMessage(
|
||||||
"Error uploading " + file.name,
|
"Error uploading " + file.name,
|
||||||
"Reason: " + response,
|
"Reason is unknown :(",
|
||||||
"danger"
|
"danger"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
if (response.status == 'ok') {
|
||||||
|
uploadInfo.insertAdjacentHTML("beforeend",
|
||||||
|
renderMessage(file.name + " uploaded as <a target='_blank' href='/" + response.hash + "'>" + response.hash + "</a>", "URL: <a target='_blank' href='" + response.url + "'>" + response.url + "</a> <button class='btn btn-primary btn-sm' onClick='navigator.clipboard.writeText(\"" + response.url + "\");'>Copy URL</button>", "success")
|
||||||
|
);
|
||||||
|
} else if (response.status == 'err') {
|
||||||
|
uploadInfo.insertAdjacentHTML("beforeend",
|
||||||
|
renderMessage(
|
||||||
|
"Error uploading " + file.name,
|
||||||
|
response.reason,
|
||||||
|
"danger"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
myDropzone.on("success", function (file, response) {
|
document.onpaste = function (event) {
|
||||||
console.log("raw response: " + response);
|
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||||
var uploadInfo = document.getElementById("uploadinfo");
|
for (var index in items) {
|
||||||
if (response == null || response == "null") {
|
var item = items[index];
|
||||||
uploadInfo.insertAdjacentHTML("beforeend",
|
if (item.kind === 'file') {
|
||||||
renderMessage(
|
myDropzone.addFile(item.getAsFile());
|
||||||
"Error uploading " + file.name,
|
}
|
||||||
"Reason is unknown :(",
|
|
||||||
"danger"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (response.status == 'ok') {
|
|
||||||
uploadInfo.insertAdjacentHTML("beforeend",
|
|
||||||
renderMessage(file.name+" uploaded as <a target='_blank' href='/" + response.hash + "'>" + response.hash + "</a>","URL: <a target='_blank' href='" + response.url + "'>" + response.url + "</a> <button class='btn btn-primary btn-sm' onClick='navigator.clipboard.writeText(\"" + response.url + "\");'>Copy URL</button>","success")
|
|
||||||
);
|
|
||||||
} else if (response.status == 'err') {
|
|
||||||
uploadInfo.insertAdjacentHTML("beforeend",
|
|
||||||
renderMessage(
|
|
||||||
"Error uploading " + file.name,
|
|
||||||
response.reason,
|
|
||||||
"danger"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
document.onpaste = function (event) {
|
|
||||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
|
||||||
for (var index in items) {
|
|
||||||
var item = items[index];
|
|
||||||
if (item.kind === 'file') {
|
|
||||||
myDropzone.addFile(item.getAsFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderMessage(title,message,type){
|
function renderMessage(title, message, type) {
|
||||||
if(!type)
|
if (!type)
|
||||||
type = "danger";
|
type = "danger";
|
||||||
return `<div class='alert alert-${type}' role='alert'><strong>${title}</strong><br/>${message}</div>`;
|
return `<div class='alert alert-${type}' role='alert'><strong>${title}</strong><br/>${message}</div>`;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user