mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-14 20:18:00 +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
|
||||
1
TODO.md
1
TODO.md
@@ -8,3 +8,4 @@
|
||||
- [ ] Review reports
|
||||
- [ ] 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
|
||||
- [ ] Bruteforce protection for admin login
|
||||
|
||||
@@ -3,12 +3,11 @@ services:
|
||||
pictshare:
|
||||
image: 'HaschekSolutions/pictshare:3'
|
||||
environment:
|
||||
- SERVER_NAME=:80 # required by caddy
|
||||
- URL=http://localhost:8080/
|
||||
- MAX_UPLOAD_SIZE=20 #in MB
|
||||
# Security settings
|
||||
- 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
|
||||
- MASTER_DELETE_CODE= # code to delete all files
|
||||
- MASTER_DELETE_IP= # IP address to delete all files even without delete code
|
||||
@@ -43,6 +42,7 @@ services:
|
||||
- REDIS_CACHING=true
|
||||
- REDIS_SERVER=/run/redis/redis.sock
|
||||
- REDIS_PORT=0
|
||||
- SERVER_NAME=:80 # required by caddy
|
||||
ports:
|
||||
- 8080:80
|
||||
volumes:
|
||||
|
||||
@@ -29,13 +29,38 @@ function architect($u)
|
||||
{
|
||||
$forbidden = true;
|
||||
}
|
||||
return renderTemplate('main.html.php',['forbidden'=>$forbidden]);
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('main.html.php',['forbidden'=>$forbidden])]);
|
||||
}
|
||||
|
||||
// admin logic
|
||||
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();
|
||||
switch($u[1]){
|
||||
case 'stats':
|
||||
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;
|
||||
@@ -48,7 +73,10 @@ function architect($u)
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
return renderTemplate('admin.html.php');
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.html.php')]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//check cache
|
||||
@@ -59,7 +87,7 @@ function architect($u)
|
||||
{
|
||||
list($cc, $hash) = explode(';', $cache_data);
|
||||
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");
|
||||
return (new $cc())->handleHash($hash,$u);
|
||||
}
|
||||
@@ -73,7 +101,7 @@ function architect($u)
|
||||
{
|
||||
$hash = $el;
|
||||
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;
|
||||
}
|
||||
// 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);
|
||||
|
||||
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
|
||||
}
|
||||
@@ -110,7 +138,7 @@ function architect($u)
|
||||
unlink(ROOT.DS.'tmp'.DS.$el.'.enc');
|
||||
|
||||
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
|
||||
}
|
||||
@@ -125,7 +153,7 @@ function architect($u)
|
||||
if((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u) )
|
||||
{
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@@ -833,7 +861,7 @@ function getDeleteCodeOfHash($hash)
|
||||
|
||||
function getMetadataOfHash($hash)
|
||||
{
|
||||
$metadata = array();
|
||||
$metadata = [];
|
||||
$metadatafile = getDataDir().DS.$hash.DS.'meta.json';
|
||||
if(file_exists($metadatafile))
|
||||
{
|
||||
@@ -1108,3 +1136,39 @@ function addToLog($data,$logfile=ROOT.DS.'logs/app.log')
|
||||
fwrite($fp,date("d.m.y H:i")."\t[".getURL()."] | ".$data."\n");
|
||||
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,70 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
|
||||
<!--[if (gt IEMobile 7)|!(IEMobile)]><!-->
|
||||
<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/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 class="container">
|
||||
<h2>Admin Panel</h2>
|
||||
<?php if (!$_SESSION['admin']) { ?>
|
||||
<h2>Admin Panel</h2>
|
||||
<?php if (!$_SESSION['admin']) { ?>
|
||||
<form method="post" action="/admin">
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" class="form-control" name="password" placeholder="Password" aria-label="Password" aria-describedby="btn-addn">
|
||||
<button class="btn btn-outline-secondary" type="submit" id="btn-addn">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
<?php if ($_SESSION['admin']) { ?>
|
||||
<?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>
|
||||
@@ -72,22 +15,11 @@
|
||||
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="#">Stats</a>
|
||||
<a class="nav-link" aria-current="page" href="/admin/stats">Stats</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Review</a>
|
||||
<a class="nav-link" href="/admin/logs">Logs</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>
|
||||
<?php } ?>
|
||||
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,61 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
|
||||
<!--[if (gt IEMobile 7)|!(IEMobile)]><!-->
|
||||
<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/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>
|
||||
<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'))
|
||||
<?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">
|
||||
?>
|
||||
<div class="well">
|
||||
<?php if ($forbidden === true) { ?>
|
||||
|
||||
<h2>Upload forbidden</h2>
|
||||
@@ -65,10 +12,10 @@
|
||||
<?php } else { ?>
|
||||
<div id="uploadinfo"></div>
|
||||
<p>
|
||||
Max Upload size: <?=(int)(ini_get('upload_max_filesize'))?>MB / File<br/>
|
||||
Max Upload size: <?= (int)(ini_get('upload_max_filesize')) ?>MB / File<br />
|
||||
Allowed file types: <?= implode(', ', getAllContentFiletypes()) ?>
|
||||
<?php
|
||||
if (defined('UPLOAD_CODE') && UPLOAD_CODE != ''):?>
|
||||
if (defined('UPLOAD_CODE') && UPLOAD_CODE != ''): ?>
|
||||
<br>Upload Code: <input type="password" id="uploadcode" />
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
@@ -78,21 +25,17 @@
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<h2 id="api" class="section-heading">Using PictShare</h2>
|
||||
|
||||
<div class="container">
|
||||
<h2 id="api" class="section-heading">Using PictShare</h2>
|
||||
|
||||
<div class="row">
|
||||
<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>
|
||||
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>
|
||||
@@ -105,16 +48,12 @@
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<hr class="no-print border border-primary border-2 opacity-50">
|
||||
</div>
|
||||
<hr class="no-print border border-primary border-2 opacity-50">
|
||||
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h2 id="api" class="section-heading">Using the API</h2>
|
||||
<div class="row">
|
||||
<h2 id="api" class="section-heading">Using the API</h2>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h2>Basics</h2>
|
||||
|
||||
@@ -220,18 +159,4 @@
|
||||
|
||||
</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>
|
||||
</div>
|
||||
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-repeat: no-repeat;
|
||||
background-color: #fff;
|
||||
padding-bottom: 60px; /* Space for the footer */
|
||||
}
|
||||
|
||||
#headcontainer
|
||||
@@ -33,3 +34,12 @@ body {
|
||||
.box__error {
|
||||
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,19 +2,20 @@ Dropzone.autoDiscover = false;
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
if (document.getElementById("dropzone") != null) {
|
||||
var myDropzone = new Dropzone("#dropzone");
|
||||
if (typeof maxUploadFileSize !== "undefined")
|
||||
myDropzone.options.maxFilesize = maxUploadFileSize;
|
||||
|
||||
myDropzone.options.timeout = 0;
|
||||
|
||||
myDropzone.on("sending", function(file, xhr, formData) {
|
||||
myDropzone.on("sending", function (file, xhr, formData) {
|
||||
var uploadCodeElem = document.getElementById("uploadcode");
|
||||
if (uploadCodeElem)
|
||||
formData.append("uploadcode", uploadCodeElem.value);
|
||||
});
|
||||
|
||||
myDropzone.on('error', function(file, response) {
|
||||
myDropzone.on('error', function (file, response) {
|
||||
var uploadInfo = document.getElementById("uploadinfo");
|
||||
if (response == null || response == "null") {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
@@ -59,7 +60,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
} 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")
|
||||
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",
|
||||
@@ -82,10 +83,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function renderMessage(title,message,type){
|
||||
if(!type)
|
||||
function renderMessage(title, message, type) {
|
||||
if (!type)
|
||||
type = "danger";
|
||||
return `<div class='alert alert-${type}' role='alert'><strong>${title}</strong><br/>${message}</div>`;
|
||||
}
|
||||
Reference in New Issue
Block a user