上传文件至 /
This commit is contained in:
286
about.php
Normal file
286
about.php
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<?php echo $lang; ?>" data-theme="<?php echo $currentUserSettings['dark_mode'] ? 'dark' : 'light'; ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>关于我们 - <?php echo SITE_NAME; ?></title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include 'components/navbar.php'; ?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="about-container">
|
||||||
|
<div class="about-header text-center">
|
||||||
|
<h1><i class="fas fa-info-circle"></i> 关于 <?php echo SITE_NAME; ?></h1>
|
||||||
|
<p class="lead">安全、快速、免费的图片托管服务</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-content">
|
||||||
|
<div class="about-section card">
|
||||||
|
<h2><i class="fas fa-rocket"></i> 我们的使命</h2>
|
||||||
|
<p>为用户提供简单易用、功能强大的图片托管解决方案,让每个人都能轻松分享和管理自己的图片资源。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section card">
|
||||||
|
<h2><i class="fas fa-star"></i> 主要特性</h2>
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-item">
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i>
|
||||||
|
<h3>批量上传</h3>
|
||||||
|
<p>支持多文件同时上传,拖拽操作</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<i class="fas fa-tags"></i>
|
||||||
|
<h3>标签分类</h3>
|
||||||
|
<p>智能标签系统,方便图片管理</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<i class="fas fa-code"></i>
|
||||||
|
<h3>API 支持</h3>
|
||||||
|
<p>完整的 RESTful API 接口</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<i class="fas fa-palette"></i>
|
||||||
|
<h3>深色模式</h3>
|
||||||
|
<p>支持明暗主题切换</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<i class="fas fa-shield-alt"></i>
|
||||||
|
<h3>安全可靠</h3>
|
||||||
|
<p>多重安全验证机制</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<i class="fas fa-mobile-alt"></i>
|
||||||
|
<h3>响应式设计</h3>
|
||||||
|
<p>完美适配各种设备</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section card">
|
||||||
|
<h2><i class="fas fa-cogs"></i> 技术栈</h2>
|
||||||
|
<div class="tech-stack">
|
||||||
|
<div class="tech-item">
|
||||||
|
<i class="fab fa-php"></i>
|
||||||
|
<span>PHP</span>
|
||||||
|
</div>
|
||||||
|
<div class="tech-item">
|
||||||
|
<i class="fas fa-database"></i>
|
||||||
|
<span>MySQL</span>
|
||||||
|
</div>
|
||||||
|
<div class="tech-item">
|
||||||
|
<i class="fab fa-js"></i>
|
||||||
|
<span>JavaScript</span>
|
||||||
|
</div>
|
||||||
|
<div class="tech-item">
|
||||||
|
<i class="fab fa-html5"></i>
|
||||||
|
<span>HTML5</span>
|
||||||
|
</div>
|
||||||
|
<div class="tech-item">
|
||||||
|
<i class="fab fa-css3-alt"></i>
|
||||||
|
<span>CSS3</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section card">
|
||||||
|
<h2><i class="fas fa-chart-line"></i> 统计数据</h2>
|
||||||
|
<div class="stats-grid">
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("SELECT COUNT(*) as total FROM users");
|
||||||
|
$totalUsers = $stmt->fetch(PDO::FETCH_ASSOC)['total'];
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT COUNT(*) as total FROM images");
|
||||||
|
$totalImages = $stmt->fetch(PDO::FETCH_ASSOC)['total'];
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT SUM(file_size) as total FROM images");
|
||||||
|
$totalSize = $stmt->fetch(PDO::FETCH_ASSOC)['total'] ?: 0;
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT COUNT(*) as total FROM images WHERE DATE(uploaded_at) = CURDATE()");
|
||||||
|
$todayImages = $stmt->fetch(PDO::FETCH_ASSOC)['total'];
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
$totalUsers = $totalImages = $totalSize = $todayImages = 0;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number"><?php echo $totalUsers; ?></div>
|
||||||
|
<div class="stat-label">注册用户</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number"><?php echo $totalImages; ?></div>
|
||||||
|
<div class="stat-label">托管图片</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number"><?php echo formatFileSize($totalSize); ?></div>
|
||||||
|
<div class="stat-label">存储空间</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number"><?php echo $todayImages; ?></div>
|
||||||
|
<div class="stat-label">今日上传</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section card">
|
||||||
|
<h2><i class="fas fa-question-circle"></i> 常见问题</h2>
|
||||||
|
<div class="faq-list">
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>支持哪些图片格式?</h3>
|
||||||
|
<p>支持 JPG、PNG、GIF、WebP 格式,单文件最大 5MB。</p>
|
||||||
|
</div>
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>图片会永久保存吗?</h3>
|
||||||
|
<p>是的,所有上传的图片都会永久保存,除非用户主动删除。</p>
|
||||||
|
</div>
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>如何获取 API 密钥?</h3>
|
||||||
|
<p>登录后在 API 文档页面可以生成和管理您的 API 密钥。</p>
|
||||||
|
</div>
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>支持外链吗?</h3>
|
||||||
|
<p>支持,每张图片都提供直接链接,可以嵌入到其他网站。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section card">
|
||||||
|
<h2><i class="fas fa-envelope"></i> 联系我们</h2>
|
||||||
|
<div class="contact-info">
|
||||||
|
<p>如果您有任何问题或建议,欢迎通过以下方式联系我们:</p>
|
||||||
|
<div class="contact-methods">
|
||||||
|
<div class="contact-method">
|
||||||
|
<i class="fas fa-envelope"></i>
|
||||||
|
<span>邮箱: support@66ghz.com</span>
|
||||||
|
</div>
|
||||||
|
<div class="contact-method">
|
||||||
|
<i class="fas fa-bug"></i>
|
||||||
|
<span>问题反馈: <a href="feedback.php">反馈页面</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.about-container {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.about-header {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
.about-header h1 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.lead {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.about-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.about-section h2 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 2px solid var(--primary-color);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.features-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.feature-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.feature-item i {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.feature-item h3 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
.tech-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 15px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.tech-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
.tech-item i {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.stat-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
.stat-number {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.faq-list {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.faq-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
.faq-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.faq-item h3 {
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.contact-methods {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.contact-method {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.contact-method i {
|
||||||
|
color: var(--primary-color);
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
152
api-docs.php
Normal file
152
api-docs.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT api_key FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
$user = ['api_key' => null];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<?php echo $lang; ?>" data-theme="<?php echo $currentUserSettings['dark_mode'] ? 'dark' : 'light'; ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo t('api_docs'); ?> - <?php echo SITE_NAME; ?></title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include 'components/navbar.php'; ?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1><i class="fas fa-code"></i> <?php echo t('api_documentation'); ?></h1>
|
||||||
|
|
||||||
|
<div class="api-docs-container">
|
||||||
|
<section class="api-section card">
|
||||||
|
<h2><i class="fas fa-key"></i> <?php echo t('api_management'); ?></h2>
|
||||||
|
<?php if(empty($user['api_key'])): ?>
|
||||||
|
<p><?php echo t('no_api_key_message'); ?></p>
|
||||||
|
<form method="POST" action="generate-api-key.php">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-plus"></i> <?php echo t('generate_api_key'); ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="api-key-display">
|
||||||
|
<label><strong><i class="fas fa-key"></i> <?php echo t('your_api_key'); ?>:</strong></label>
|
||||||
|
<div class="api-key-input">
|
||||||
|
<input type="text" value="<?php echo $user['api_key']; ?>" id="apiKey" readonly>
|
||||||
|
<button onclick="copyApiKey()" class="btn">
|
||||||
|
<i class="fas fa-copy"></i> <?php echo t('copy'); ?>
|
||||||
|
</button>
|
||||||
|
<form method="POST" action="generate-api-key.php" style="display: inline;">
|
||||||
|
<button type="submit" class="btn btn-secondary" onclick="return confirm('<?php echo t('confirm_regenerate_api_key'); ?>')">
|
||||||
|
<i class="fas fa-sync"></i> <?php echo t('regenerate_key'); ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<small><i class="fas fa-shield-alt"></i> <?php echo t('keep_secret'); ?></small>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="api-section card mt-3">
|
||||||
|
<h2><i class="fas fa-plug"></i> <?php echo t('api_endpoints'); ?></h2>
|
||||||
|
|
||||||
|
<div class="api-endpoint">
|
||||||
|
<h3><i class="fas fa-cloud-upload-alt"></i> 1. <?php echo t('upload_image'); ?></h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<span class="endpoint">POST</span> <?php echo SITE_URL; ?>/api/upload.php<br><br>
|
||||||
|
<span class="comment">// <?php echo t('headers'); ?>:</span><br>
|
||||||
|
Content-Type: multipart/form-data<br><br>
|
||||||
|
<span class="comment">// <?php echo t('parameters'); ?>:</span><br>
|
||||||
|
api_key = <span class="param">string</span> (<?php echo t('required'); ?>)<br>
|
||||||
|
image = <span class="param">file</span> (<?php echo t('required'); ?>)<br>
|
||||||
|
title = <span class="param">string</span> (<?php echo t('optional'); ?>)<br>
|
||||||
|
tags = <span class="param">string</span> (<?php echo t('optional'); ?>)<br>
|
||||||
|
is_public = <span class="param">integer</span> (<?php echo t('optional'); ?>)<br><br>
|
||||||
|
<span class="comment">// <?php echo t('success_response'); ?>:</span><br>
|
||||||
|
{<br>
|
||||||
|
"success": true,<br>
|
||||||
|
"data": {<br>
|
||||||
|
"id": 123,<br>
|
||||||
|
"title": "<?php echo t('image_title'); ?>",<br>
|
||||||
|
"url": "<?php echo SITE_URL; ?>/view-image.php?id=123",<br>
|
||||||
|
"direct_url": "<?php echo SITE_URL; ?>/uploads/filename.jpg",<br>
|
||||||
|
"tags": ["<?php echo t('landscape'); ?>", "<?php echo t('nature'); ?>"]<br>
|
||||||
|
}<br>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="api-endpoint mt-3">
|
||||||
|
<h3><i class="fas fa-images"></i> 2. <?php echo t('get_images'); ?></h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<span class="endpoint">GET</span> <?php echo SITE_URL; ?>/api/images.php?api_key=<?php echo $user['api_key'] ?? 'YOUR_API_KEY'; ?>&page=1&limit=20<br><br>
|
||||||
|
<span class="comment">// <?php echo t('parameters'); ?>:</span><br>
|
||||||
|
api_key = <span class="param">string</span> (<?php echo t('required'); ?>)<br>
|
||||||
|
page = <span class="param">integer</span> (<?php echo t('optional'); ?>)<br>
|
||||||
|
limit = <span class="param">integer</span> (<?php echo t('optional'); ?>)<br><br>
|
||||||
|
<span class="comment">// <?php echo t('success_response'); ?>:</span><br>
|
||||||
|
{<br>
|
||||||
|
"success": true,<br>
|
||||||
|
"data": [<br>
|
||||||
|
{<br>
|
||||||
|
"id": 123,<br>
|
||||||
|
"title": "<?php echo t('image_title'); ?>",<br>
|
||||||
|
"url": "<?php echo SITE_URL; ?>/view-image.php?id=123",<br>
|
||||||
|
"direct_url": "<?php echo SITE_URL; ?>/uploads/filename.jpg",<br>
|
||||||
|
"is_public": 1,<br>
|
||||||
|
"views": 45,<br>
|
||||||
|
"file_size_formatted": "2.5 MB"<br>
|
||||||
|
}<br>
|
||||||
|
],<br>
|
||||||
|
"pagination": {<br>
|
||||||
|
"page": 1,<br>
|
||||||
|
"limit": 20,<br>
|
||||||
|
"total": 150,<br>
|
||||||
|
"pages": 8<br>
|
||||||
|
}<br>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="api-endpoint mt-3">
|
||||||
|
<h3><i class="fas fa-terminal"></i> 3. <?php echo t('curl_examples'); ?></h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<span class="comment"># <?php echo t('upload_image'); ?></span><br>
|
||||||
|
curl -X POST \<br>
|
||||||
|
-F "api_key=<?php echo $user['api_key'] ?? 'YOUR_API_KEY'; ?>" \<br>
|
||||||
|
-F "title=<?php echo t('my_image'); ?>" \<br>
|
||||||
|
-F "is_public=1" \<br>
|
||||||
|
-F "image=@/path/to/your/image.jpg" \<br>
|
||||||
|
"<?php echo SITE_URL; ?>/api/upload.php"
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="code-block mt-2">
|
||||||
|
<span class="comment"># <?php echo t('get_images'); ?></span><br>
|
||||||
|
curl "<?php echo SITE_URL; ?>/api/images.php?api_key=<?php echo $user['api_key'] ?? 'YOUR_API_KEY'; ?>&page=1&limit=10"
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyApiKey() {
|
||||||
|
const apiKey = document.getElementById('apiKey');
|
||||||
|
apiKey.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
alert('<?php echo t('api_key_copied'); ?>');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
43
captcha.php
Normal file
43
captcha.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
header('Content-type: image/png');
|
||||||
|
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
header('Expires: 0');
|
||||||
|
|
||||||
|
$width = 120;
|
||||||
|
$height = 40;
|
||||||
|
$chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
|
||||||
|
$code = '';
|
||||||
|
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$code .= $chars[rand(0, strlen($chars) - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['captcha'] = $code;
|
||||||
|
|
||||||
|
$image = imagecreate($width, $height);
|
||||||
|
$bg_color = imagecolorallocate($image, 255, 255, 255);
|
||||||
|
$text_color = imagecolorallocate($image, 0, 0, 0);
|
||||||
|
$noise_color = imagecolorallocate($image, 200, 200, 200);
|
||||||
|
|
||||||
|
for ($i = 0; $i < 100; $i++) {
|
||||||
|
imagesetpixel($image, rand(0, $width), rand(0, $height), $noise_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i < 5; $i++) {
|
||||||
|
imageline($image, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $noise_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
$font = 5;
|
||||||
|
$x = 10;
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$char_color = imagecolorallocate($image, rand(0, 150), rand(0, 150), rand(0, 150));
|
||||||
|
imagestring($image, $font, $x, rand(8, 12), $code[$i], $char_color);
|
||||||
|
$x += 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
imagepng($image);
|
||||||
|
imagedestroy($image);
|
||||||
|
?>
|
||||||
402
config.php
Normal file
402
config.php
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use PHPMailer\PHPMailer\SMTP;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
|
||||||
|
require_once 'phpmailer/src/Exception.php';
|
||||||
|
require_once 'phpmailer/src/PHPMailer.php';
|
||||||
|
require_once 'phpmailer/src/SMTP.php';
|
||||||
|
|
||||||
|
define('DB_HOST', 'HOST');
|
||||||
|
define('DB_NAME', 'NAME');
|
||||||
|
define('DB_USER', 'USER');
|
||||||
|
define('DB_PASS', 'PASS');
|
||||||
|
|
||||||
|
define('SITE_URL', 'WEB');
|
||||||
|
define('SITE_NAME', 'SITE');
|
||||||
|
define('MAX_FILE_SIZE', 5 * 1024 * 1024);
|
||||||
|
define('ALLOWED_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'webp']);
|
||||||
|
define('API_KEY_LENGTH', 32);
|
||||||
|
|
||||||
|
define('SMTP_HOST', 'EHOST');
|
||||||
|
define('SMTP_PORT', NUM);
|
||||||
|
define('SMTP_USERNAME', 'EUSER');
|
||||||
|
define('SMTP_PASSWORD', 'PASS');
|
||||||
|
define('SMTP_FROM_EMAIL', 'FUSER');
|
||||||
|
define('SMTP_FROM_NAME', 'FNAME');
|
||||||
|
define('SMTP_SECURE', 'SECURE');
|
||||||
|
define('SMTP_DEBUG', false);
|
||||||
|
|
||||||
|
define('BING_API_URL', 'https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN');
|
||||||
|
|
||||||
|
define('CAPTCHA_ENABLED', true);
|
||||||
|
define('PASSWORD_RESET_EXPIRE', 3600);
|
||||||
|
|
||||||
|
define('DEFAULT_LANGUAGE', 'zh-CN');
|
||||||
|
$supported_languages = ['zh-CN', 'en'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4", DB_USER, DB_PASS);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
die("数据库连接失败: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists('uploads')) {
|
||||||
|
mkdir('uploads', 0755, true);
|
||||||
|
file_put_contents('uploads/index.html', '');
|
||||||
|
file_put_contents('uploads/.htaccess', 'Deny from all');
|
||||||
|
}
|
||||||
|
if (!file_exists('languages')) mkdir('languages', 0755, true);
|
||||||
|
if (!file_exists('admin')) mkdir('admin', 0755, true);
|
||||||
|
if (!file_exists('components')) mkdir('components', 0755, true);
|
||||||
|
if (!file_exists('api')) mkdir('api', 0755, true);
|
||||||
|
|
||||||
|
function getLanguage() {
|
||||||
|
global $supported_languages;
|
||||||
|
|
||||||
|
if (isset($_SESSION['language'])) {
|
||||||
|
return $_SESSION['language'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||||
|
$browser_lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
|
||||||
|
if ($browser_lang === 'zh') return 'zh-CN';
|
||||||
|
if ($browser_lang === 'en') return 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_LANGUAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLanguage($lang) {
|
||||||
|
$lang_file = "languages/{$lang}.php";
|
||||||
|
if (file_exists($lang_file)) {
|
||||||
|
return include $lang_file;
|
||||||
|
}
|
||||||
|
return include "languages/" . DEFAULT_LANGUAGE . ".php";
|
||||||
|
}
|
||||||
|
|
||||||
|
$lang = getLanguage();
|
||||||
|
$translations = loadLanguage($lang);
|
||||||
|
$_SESSION['language'] = $lang;
|
||||||
|
|
||||||
|
function t($key) {
|
||||||
|
global $translations;
|
||||||
|
return $translations[$key] ?? $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserSettings($user_id) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM user_settings WHERE user_id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$settings = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$settings) {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO user_settings (user_id) VALUES (?)");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
return [
|
||||||
|
'dark_mode' => false,
|
||||||
|
'language' => 'zh-CN',
|
||||||
|
'items_per_page' => 20,
|
||||||
|
'email_notifications' => true,
|
||||||
|
'browser_notifications' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUserSettings($user_id, $settings) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO user_settings (user_id, dark_mode, language, items_per_page, email_notifications, browser_notifications)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
dark_mode = VALUES(dark_mode),
|
||||||
|
language = VALUES(language),
|
||||||
|
items_per_page = VALUES(items_per_page),
|
||||||
|
email_notifications = VALUES(email_notifications),
|
||||||
|
browser_notifications = VALUES(browser_notifications)");
|
||||||
|
|
||||||
|
return $stmt->execute([
|
||||||
|
$user_id,
|
||||||
|
$settings['dark_mode'] ? 1 : 0,
|
||||||
|
$settings['language'],
|
||||||
|
$settings['items_per_page'],
|
||||||
|
$settings['email_notifications'] ? 1 : 0,
|
||||||
|
$settings['browser_notifications'] ? 1 : 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserNotificationSettings($user_id) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT nt.name, uns.enabled FROM user_notification_settings uns
|
||||||
|
JOIN notification_types nt ON uns.notification_type_id = nt.id
|
||||||
|
WHERE uns.user_id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||||
|
|
||||||
|
if (empty($settings)) {
|
||||||
|
initUserNotificationSettings($user_id);
|
||||||
|
return getUserNotificationSettings($user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initUserNotificationSettings($user_id) {
|
||||||
|
global $pdo;
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO user_notification_settings (user_id, notification_type_id, enabled)
|
||||||
|
SELECT ?, id, TRUE FROM notification_types");
|
||||||
|
return $stmt->execute([$user_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUserNotificationSettings($user_id, $settings) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
foreach ($settings as $type_id => $enabled) {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO user_notification_settings (user_id, notification_type_id, enabled)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON DUPLICATE KEY UPDATE enabled = VALUES(enabled)");
|
||||||
|
$stmt->execute([$user_id, $type_id, $enabled ? 1 : 0]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendNotification($user_id, $type_name, $title, $message, $related_url = null) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT uns.enabled FROM user_notification_settings uns
|
||||||
|
JOIN notification_types nt ON uns.notification_type_id = nt.id
|
||||||
|
WHERE uns.user_id = ? AND nt.name = ?");
|
||||||
|
$stmt->execute([$user_id, $type_name]);
|
||||||
|
$setting = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$setting || !$setting['enabled']) return false;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM notification_types WHERE name = ?");
|
||||||
|
$stmt->execute([$type_name]);
|
||||||
|
$type = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$type) return false;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO notifications (user_id, type_id, title, message, related_url)
|
||||||
|
VALUES (?, ?, ?, ?, ?)");
|
||||||
|
return $stmt->execute([$user_id, $type['id'], $title, $message, $related_url]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnreadNotificationCount($user_id) {
|
||||||
|
global $pdo;
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) as count FROM notifications WHERE user_id = ? AND is_read = FALSE");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC)['count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserNotifications($user_id, $limit = 10) {
|
||||||
|
global $pdo;
|
||||||
|
$stmt = $pdo->prepare("SELECT n.*, nt.name as type_name FROM notifications n
|
||||||
|
JOIN notification_types nt ON n.type_id = nt.id
|
||||||
|
WHERE n.user_id = ?
|
||||||
|
ORDER BY n.created_at DESC
|
||||||
|
LIMIT ?");
|
||||||
|
$stmt->bindValue(1, $user_id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(2, $limit, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRandomString($length = 10) {
|
||||||
|
return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
ceil($length / strlen($x)))), 1, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFileSize($bytes) {
|
||||||
|
if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2) . ' GB';
|
||||||
|
elseif ($bytes >= 1048576) return number_format($bytes / 1048576, 2) . ' MB';
|
||||||
|
elseif ($bytes >= 1024) return number_format($bytes / 1024, 2) . ' KB';
|
||||||
|
else return $bytes . ' bytes';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBingDailyImage() {
|
||||||
|
$response = @file_get_contents(BING_API_URL);
|
||||||
|
if ($response) {
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
if ($data && isset($data['images'][0])) {
|
||||||
|
$image = $data['images'][0];
|
||||||
|
return [
|
||||||
|
'url' => 'https://www.bing.com' . $image['url'],
|
||||||
|
'title' => $image['title'],
|
||||||
|
'copyright' => $image['copyright'],
|
||||||
|
'copyrightlink' => $image['copyrightlink']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendEmail($to, $subject, $message) {
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 服务器设置
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = SMTP_HOST;
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = SMTP_USERNAME;
|
||||||
|
$mail->Password = SMTP_PASSWORD;
|
||||||
|
$mail->SMTPSecure = SMTP_SECURE;
|
||||||
|
$mail->Port = SMTP_PORT;
|
||||||
|
|
||||||
|
if (SMTP_DEBUG) {
|
||||||
|
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收件人
|
||||||
|
$mail->setFrom(SMTP_FROM_EMAIL, SMTP_FROM_NAME);
|
||||||
|
$mail->addAddress($to);
|
||||||
|
|
||||||
|
// 内容
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->Body = $message;
|
||||||
|
$mail->AltBody = strip_tags($message);
|
||||||
|
|
||||||
|
return $mail->send();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("邮件发送失败: " . $mail->ErrorInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendVerificationEmail($email, $username, $verification_code) {
|
||||||
|
$verification_link = SITE_URL . "/verify.php?code=" . $verification_code;
|
||||||
|
|
||||||
|
$subject = SITE_NAME . " - 邮箱验证";
|
||||||
|
$message = "
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||||
|
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||||
|
.header { background: #3498db; color: white; padding: 20px; text-align: center; }
|
||||||
|
.content { padding: 20px; background: #f9f9f9; }
|
||||||
|
.button { display: inline-block; padding: 12px 24px; background: #3498db; color: white; text-decoration: none; border-radius: 5px; }
|
||||||
|
.footer { text-align: center; padding: 20px; color: #666; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class='container'>
|
||||||
|
<div class='header'>
|
||||||
|
<h1>" . SITE_NAME . "</h1>
|
||||||
|
</div>
|
||||||
|
<div class='content'>
|
||||||
|
<h2>亲爱的 {$username},</h2>
|
||||||
|
<p>感谢您注册" . SITE_NAME . "图床!</p>
|
||||||
|
<p>请点击下面的按钮验证您的邮箱地址:</p>
|
||||||
|
<p style='text-align: center;'>
|
||||||
|
<a href='{$verification_link}' class='button'>验证邮箱</a>
|
||||||
|
</p>
|
||||||
|
<p>如果按钮无法点击,请复制以下链接到浏览器地址栏:</p>
|
||||||
|
<p><small>{$verification_link}</small></p>
|
||||||
|
<p>此验证链接 24 小时内有效。</p>
|
||||||
|
</div>
|
||||||
|
<div class='footer'>
|
||||||
|
<p>© " . date('Y') . " " . SITE_NAME . ". 保留所有权利.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>";
|
||||||
|
|
||||||
|
return sendEmail($email, $subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendPasswordResetEmail($email, $username, $token) {
|
||||||
|
$reset_link = SITE_URL . "/reset-password.php?token=" . $token;
|
||||||
|
|
||||||
|
$subject = SITE_NAME . " - 密码重置";
|
||||||
|
$message = "
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||||
|
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||||
|
.header { background: #e74c3c; color: white; padding: 20px; text-align: center; }
|
||||||
|
.content { padding: 20px; background: #f9f9f9; }
|
||||||
|
.button { display: inline-block; padding: 12px 24px; background: #e74c3c; color: white; text-decoration: none; border-radius: 5px; }
|
||||||
|
.footer { text-align: center; padding: 20px; color: #666; }
|
||||||
|
.warning { background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 5px; margin: 15px 0; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class='container'>
|
||||||
|
<div class='header'>
|
||||||
|
<h1>" . SITE_NAME . " - 密码重置</h1>
|
||||||
|
</div>
|
||||||
|
<div class='content'>
|
||||||
|
<h2>亲爱的 {$username},</h2>
|
||||||
|
<p>我们收到了您重置密码的请求。</p>
|
||||||
|
<p>请点击下面的按钮重置您的密码:</p>
|
||||||
|
<p style='text-align: center;'>
|
||||||
|
<a href='{$reset_link}' class='button'>重置密码</a>
|
||||||
|
</p>
|
||||||
|
<p>如果按钮无法点击,请复制以下链接到浏览器地址栏:</p>
|
||||||
|
<p><small>{$reset_link}</small></p>
|
||||||
|
<div class='warning'>
|
||||||
|
<strong>注意:</strong>此链接在 <font color='red'>1</font> 小时内有效。如果您没有请求重置密码,请忽略此邮件。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='footer'>
|
||||||
|
<p>© " . date('Y') . " " . SITE_NAME . ". 保留所有权利.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>";
|
||||||
|
|
||||||
|
return sendEmail($email, $subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendEmailNotification($user_id, $subject, $message) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
// 获取用户邮箱和设置
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT u.email, us.email_notifications
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN user_settings us ON u.id = us.user_id
|
||||||
|
WHERE u.id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$user || !$user['email_notifications']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendEmail($user['email'], $subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentUserSettings = isset($_SESSION['user_id']) ? getUserSettings($_SESSION['user_id']) : [
|
||||||
|
'dark_mode' => false,
|
||||||
|
'language' => $lang,
|
||||||
|
'items_per_page' => 20,
|
||||||
|
'email_notifications' => true,
|
||||||
|
'browser_notifications' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
$currentUserSettings['dark_mode'] = isset($_COOKIE['dark_mode']) ? (bool)$_COOKIE['dark_mode'] : false;
|
||||||
|
$currentUserSettings['language'] = $lang;
|
||||||
|
}
|
||||||
|
?>
|
||||||
282
dashboard.php
Normal file
282
dashboard.php
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM images WHERE user_id = ? ORDER BY uploaded_at DESC");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$userImages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
$userImages = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT api_key FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
$user = ['api_key' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
$success = $_SESSION['success'] ?? '';
|
||||||
|
$error = $_SESSION['error'] ?? '';
|
||||||
|
unset($_SESSION['success'], $_SESSION['error']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) as total FROM images WHERE user_id = ?");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$totalImages = $stmt->fetch(PDO::FETCH_ASSOC)['total'];
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) as public FROM images WHERE user_id = ? AND is_public = 1");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$publicImages = $stmt->fetch(PDO::FETCH_ASSOC)['public'];
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT SUM(views) as total_views FROM images WHERE user_id = ?");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$totalViews = $stmt->fetch(PDO::FETCH_ASSOC)['total_views'] ?: 0;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT SUM(file_size) as total_size FROM images WHERE user_id = ?");
|
||||||
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
|
$totalSize = $stmt->fetch(PDO::FETCH_ASSOC)['total_size'] ?: 0;
|
||||||
|
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
$totalImages = $publicImages = $totalViews = $totalSize = 0;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<?php echo $lang; ?>" data-theme="<?php echo $currentUserSettings['dark_mode'] ? 'dark' : 'light'; ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo t('dashboard'); ?> - <?php echo SITE_NAME; ?></title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include 'components/navbar.php'; ?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card card users">
|
||||||
|
<div class="stat-number"><?php echo $totalImages; ?></div>
|
||||||
|
<div class="stat-label"><?php echo t('total_images'); ?></div>
|
||||||
|
<small><?php echo t('public_images'); ?>: <?php echo $publicImages; ?></small>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card card images">
|
||||||
|
<div class="stat-number"><?php echo $publicImages; ?></div>
|
||||||
|
<div class="stat-label"><?php echo t('public_images'); ?></div>
|
||||||
|
<small><?php echo t('private'); ?>: <?php echo $totalImages - $publicImages; ?></small>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card card storage">
|
||||||
|
<div class="stat-number"><?php echo $totalViews; ?></div>
|
||||||
|
<div class="stat-label"><?php echo t('total_views'); ?></div>
|
||||||
|
<small><?php echo t('total_engagement'); ?></small>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card card feedbacks">
|
||||||
|
<div class="stat-number"><?php echo formatFileSize($totalSize); ?></div>
|
||||||
|
<div class="stat-label"><?php echo t('storage_used'); ?></div>
|
||||||
|
<small><?php echo t('total_size'); ?></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dashboard-header">
|
||||||
|
<h1><i class="fas fa-th-large"></i> <?php echo t('my_gallery'); ?></h1>
|
||||||
|
<a href="upload.php" class="btn btn-primary">
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i> <?php echo t('upload_new'); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if($success): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<i class="fas fa-check-circle"></i> <?php echo $success; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if($error): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i> <?php echo $error; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="gallery-actions">
|
||||||
|
<div class="search-box">
|
||||||
|
<input type="text" id="searchInput" placeholder="<?php echo t('search_placeholder'); ?>" onkeyup="searchImages()">
|
||||||
|
<select id="filterSelect" onchange="filterImages()">
|
||||||
|
<option value="all"><?php echo t('all_images'); ?></option>
|
||||||
|
<option value="public"><?php echo t('public'); ?></option>
|
||||||
|
<option value="private"><?php echo t('private'); ?></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span><?php echo t('sort'); ?>:</span>
|
||||||
|
<select id="sortSelect" onchange="sortImages()">
|
||||||
|
<option value="newest"><?php echo t('sort_newest'); ?></option>
|
||||||
|
<option value="oldest"><?php echo t('sort_oldest'); ?></option>
|
||||||
|
<option value="views"><?php echo t('sort_views'); ?></option>
|
||||||
|
<option value="name"><?php echo t('sort_name'); ?></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if(empty($userImages)): ?>
|
||||||
|
<div class="empty-state card">
|
||||||
|
<h3><i class="fas fa-images"></i> <?php echo t('no_images'); ?></h3>
|
||||||
|
<p><?php echo t('upload_first'); ?></p>
|
||||||
|
<a href="upload.php" class="btn btn-primary">
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i> <?php echo t('upload'); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="gallery" id="imageGallery">
|
||||||
|
<?php foreach($userImages as $image): ?>
|
||||||
|
<div class="gallery-item card" data-title="<?php echo htmlspecialchars($image['title'] ?: ''); ?>" data-public="<?php echo $image['is_public']; ?>" data-views="<?php echo $image['views']; ?>">
|
||||||
|
<a href="view-image.php?id=<?php echo $image['id']; ?>">
|
||||||
|
<img src="uploads/<?php echo $image['filename']; ?>"
|
||||||
|
alt="<?php echo htmlspecialchars($image['title'] ?: t('untitled')); ?>"
|
||||||
|
loading="lazy">
|
||||||
|
</a>
|
||||||
|
<div class="image-info">
|
||||||
|
<div class="image-title">
|
||||||
|
<?php echo htmlspecialchars($image['title'] ?: t('untitled')); ?>
|
||||||
|
</div>
|
||||||
|
<div class="image-meta">
|
||||||
|
<small><i class="fas fa-calendar"></i> <?php echo date('m-d H:i', strtotime($image['uploaded_at'])); ?></small>
|
||||||
|
<small><i class="fas fa-eye"></i> <?php echo $image['views']; ?></small>
|
||||||
|
<small><i class="fas fa-weight"></i> <?php echo formatFileSize($image['file_size'] ?? 0); ?></small>
|
||||||
|
</div>
|
||||||
|
<div class="image-actions">
|
||||||
|
<span class="status-badge <?php echo $image['is_public'] ? 'status-public' : 'status-private'; ?>">
|
||||||
|
<?php echo $image['is_public'] ? t('public') : t('private'); ?>
|
||||||
|
</span>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button class="btn btn-sm"
|
||||||
|
onclick="copyToClipboard('<?php echo SITE_URL . '/uploads/' . $image['filename']; ?>')">
|
||||||
|
<i class="fas fa-copy"></i> <?php echo t('copy'); ?>
|
||||||
|
</button>
|
||||||
|
<a href="view-image.php?id=<?php echo $image['id']; ?>" class="btn btn-sm">
|
||||||
|
<i class="fas fa-eye"></i> <?php echo t('view'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="delete-image.php?id=<?php echo $image['id']; ?>"
|
||||||
|
class="btn btn-sm btn-danger"
|
||||||
|
onclick="return confirm('<?php echo t('confirm_delete_image'); ?>')">
|
||||||
|
<i class="fas fa-trash"></i> <?php echo t('delete'); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="api-section mt-3">
|
||||||
|
<h2><i class="fas fa-code"></i> <?php echo t('api_management'); ?></h2>
|
||||||
|
<div class="api-card card">
|
||||||
|
<p><?php echo t('api_migrated_message'); ?></p>
|
||||||
|
<a href="api-docs.php" class="btn btn-primary">
|
||||||
|
<i class="fas fa-book"></i> <?php echo t('view_api_docs'); ?>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<?php if(!empty($user['api_key'])): ?>
|
||||||
|
<div class="mt-2">
|
||||||
|
<label><strong><i class="fas fa-key"></i> <?php echo t('your_api_key'); ?>:</strong></label>
|
||||||
|
<div class="api-key-input">
|
||||||
|
<input type="text" value="<?php echo $user['api_key']; ?>" id="apiKey" readonly>
|
||||||
|
<button onclick="copyApiKey()" class="btn">
|
||||||
|
<i class="fas fa-copy"></i> <?php echo t('copy'); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<small><?php echo t('keep_secret'); ?></small>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyApiKey() {
|
||||||
|
const apiKey = document.getElementById('apiKey');
|
||||||
|
apiKey.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
alert('<?php echo t('api_key_copied'); ?>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard(text) {
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
alert('<?php echo t('copied_to_clipboard'); ?>');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.value = text;
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
alert('<?php echo t('copied_to_clipboard'); ?>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchImages() {
|
||||||
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||||||
|
const items = document.querySelectorAll('.gallery-item');
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
const title = item.getAttribute('data-title').toLowerCase();
|
||||||
|
if (title.includes(searchTerm)) {
|
||||||
|
item.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
item.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterImages() {
|
||||||
|
const filter = document.getElementById('filterSelect').value;
|
||||||
|
const items = document.querySelectorAll('.gallery-item');
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
const isPublic = item.getAttribute('data-public');
|
||||||
|
if (filter === 'all' ||
|
||||||
|
(filter === 'public' && isPublic === '1') ||
|
||||||
|
(filter === 'private' && isPublic === '0')) {
|
||||||
|
item.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
item.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortImages() {
|
||||||
|
const sortBy = document.getElementById('sortSelect').value;
|
||||||
|
const gallery = document.getElementById('imageGallery');
|
||||||
|
const items = Array.from(document.querySelectorAll('.gallery-item'));
|
||||||
|
|
||||||
|
items.sort((a, b) => {
|
||||||
|
switch(sortBy) {
|
||||||
|
case 'newest':
|
||||||
|
return 0;
|
||||||
|
case 'oldest':
|
||||||
|
return 1;
|
||||||
|
case 'views':
|
||||||
|
const viewsA = parseInt(a.getAttribute('data-views'));
|
||||||
|
const viewsB = parseInt(b.getAttribute('data-views'));
|
||||||
|
return viewsB - viewsA;
|
||||||
|
case 'name':
|
||||||
|
const nameA = a.getAttribute('data-title').toLowerCase();
|
||||||
|
const nameB = b.getAttribute('data-title').toLowerCase();
|
||||||
|
return nameA.localeCompare(nameB);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
items.forEach(item => gallery.appendChild(item));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user