第一个版本
This commit is contained in:
BIN
db.sqlite3
Normal file
BIN
db.sqlite3
Normal file
Binary file not shown.
22
manage.py
Normal file
22
manage.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'statuspage.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Django==4.2.7
|
||||||
0
status/__init__.py
Normal file
0
status/__init__.py
Normal file
BIN
status/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
status/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
status/__pycache__/admin.cpython-38.pyc
Normal file
BIN
status/__pycache__/admin.cpython-38.pyc
Normal file
Binary file not shown.
BIN
status/__pycache__/apps.cpython-38.pyc
Normal file
BIN
status/__pycache__/apps.cpython-38.pyc
Normal file
Binary file not shown.
BIN
status/__pycache__/models.cpython-38.pyc
Normal file
BIN
status/__pycache__/models.cpython-38.pyc
Normal file
Binary file not shown.
BIN
status/__pycache__/urls.cpython-38.pyc
Normal file
BIN
status/__pycache__/urls.cpython-38.pyc
Normal file
Binary file not shown.
BIN
status/__pycache__/views.cpython-38.pyc
Normal file
BIN
status/__pycache__/views.cpython-38.pyc
Normal file
Binary file not shown.
9
status/admin.py
Normal file
9
status/admin.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from .models import Service
|
||||||
|
|
||||||
|
class ServiceAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'status', 'description', 'ip_address', 'port', 'reliability', 'last_updated')
|
||||||
|
list_filter = ('status',)
|
||||||
|
search_fields = ('name', 'ip_address', 'description')
|
||||||
|
|
||||||
|
admin.site.register(Service, ServiceAdmin)
|
||||||
6
status/apps.py
Normal file
6
status/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class StatusConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'status'
|
||||||
32
status/migrations/0001_initial.py
Normal file
32
status/migrations/0001_initial.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2025-06-16 12:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Service',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100, verbose_name='服务名称')),
|
||||||
|
('status', models.CharField(choices=[('operational', '正常运行'), ('degraded', '性能下降'), ('outage', '服务中断')], default='operational', max_length=20, verbose_name='服务状态')),
|
||||||
|
('description', models.TextField(verbose_name='服务描述')),
|
||||||
|
('ip_address', models.GenericIPAddressField(verbose_name='IP地址')),
|
||||||
|
('port', models.PositiveIntegerField(verbose_name='端口号')),
|
||||||
|
('reliability', models.DecimalField(decimal_places=2, default=99.0, max_digits=5, verbose_name='可靠率(%)')),
|
||||||
|
('last_updated', models.DateTimeField(default=django.utils.timezone.now, verbose_name='最后更新时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '服务状态',
|
||||||
|
'verbose_name_plural': '服务状态',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
status/migrations/__init__.py
Normal file
0
status/migrations/__init__.py
Normal file
BIN
status/migrations/__pycache__/0001_initial.cpython-38.pyc
Normal file
BIN
status/migrations/__pycache__/0001_initial.cpython-38.pyc
Normal file
Binary file not shown.
BIN
status/migrations/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
status/migrations/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
24
status/models.py
Normal file
24
status/models.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
class Service(models.Model):
|
||||||
|
STATUS_CHOICES = (
|
||||||
|
('operational', '正常运行'),
|
||||||
|
('degraded', '性能下降'),
|
||||||
|
('outage', '服务中断'),
|
||||||
|
)
|
||||||
|
|
||||||
|
name = models.CharField(max_length=100, verbose_name='服务名称')
|
||||||
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='operational', verbose_name='服务状态')
|
||||||
|
description = models.TextField(verbose_name='服务描述')
|
||||||
|
ip_address = models.GenericIPAddressField(verbose_name='IP地址')
|
||||||
|
port = models.PositiveIntegerField(verbose_name='端口号')
|
||||||
|
reliability = models.DecimalField(max_digits=5, decimal_places=2, default=99.00, verbose_name='可靠率(%)')
|
||||||
|
last_updated = models.DateTimeField(default=timezone.now, verbose_name='最后更新时间')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = '服务状态'
|
||||||
|
verbose_name_plural = '服务状态'
|
||||||
239
status/templates/status/index.html
Normal file
239
status/templates/status/index.html
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>服务状态监控</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: '#165DFF',
|
||||||
|
operational: '#36D399',
|
||||||
|
degraded: '#FBBD23',
|
||||||
|
outage: '#F87272',
|
||||||
|
dark: '#1E293B',
|
||||||
|
light: '#F8FAFC'
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
inter: ['Inter', 'system-ui', 'sans-serif'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style type="text/tailwindcss">
|
||||||
|
@layer utilities {
|
||||||
|
.content-auto {
|
||||||
|
content-visibility: auto;
|
||||||
|
}
|
||||||
|
.card-shadow {
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.card-hover {
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
.card-hover:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
.gradient-bg {
|
||||||
|
background: linear-gradient(135deg, #165DFF 0%, #0A2463 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 font-inter min-h-screen">
|
||||||
|
<!-- 顶部导航 -->
|
||||||
|
<header class="gradient-bg text-white shadow-lg">
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||||
|
<div class="flex items-center mb-4 md:mb-0">
|
||||||
|
<i class="fa fa-server text-3xl mr-3"></i>
|
||||||
|
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold">服务状态监控</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<i class="fa fa-refresh animate-spin mr-2"></i>
|
||||||
|
<span id="last-updated" class="text-sm md:text-base">最后更新: 加载中...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2 text-gray-200">实时监控系统服务运行状态与可靠性</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- 主内容区 -->
|
||||||
|
<main class="container mx-auto px-4 py-8">
|
||||||
|
<!-- 状态概览 -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-10">
|
||||||
|
<div class="bg-white rounded-xl p-6 card-shadow card-hover border-l-4 border-operational">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-gray-500 text-sm">正常运行</p>
|
||||||
|
<h3 id="operational-count" class="text-3xl font-bold mt-1">0</h3>
|
||||||
|
</div>
|
||||||
|
<div class="w-12 h-12 rounded-full bg-operational/10 flex items-center justify-center text-operational">
|
||||||
|
<i class="fa fa-check-circle text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-xl p-6 card-shadow card-hover border-l-4 border-degraded">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-gray-500 text-sm">性能下降</p>
|
||||||
|
<h3 id="degraded-count" class="text-3xl font-bold mt-1">0</h3>
|
||||||
|
</div>
|
||||||
|
<div class="w-12 h-12 rounded-full bg-degraded/10 flex items-center justify-center text-degraded">
|
||||||
|
<i class="fa fa-exclamation-triangle text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-xl p-6 card-shadow card-hover border-l-4 border-outage">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-gray-500 text-sm">服务中断</p>
|
||||||
|
<h3 id="outage-count" class="text-3xl font-bold mt-1">0</h3>
|
||||||
|
</div>
|
||||||
|
<div class="w-12 h-12 rounded-full bg-outage/10 flex items-center justify-center text-outage">
|
||||||
|
<i class="fa fa-times-circle text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 服务列表 -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" id="services-container">
|
||||||
|
<!-- 服务卡片将通过JavaScript动态生成 -->
|
||||||
|
<div class="col-span-full text-center py-12 text-gray-500">
|
||||||
|
<i class="fa fa-circle-o-notch fa-spin text-3xl mb-4"></i>
|
||||||
|
<p>加载服务数据中...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- 页脚 -->
|
||||||
|
<footer class="bg-dark text-white py-6 mt-12">
|
||||||
|
<div class="container mx-auto px-4 text-center">
|
||||||
|
<p>© 2025 服务状态监控系统 | 数据每30秒自动更新</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 状态样式映射
|
||||||
|
const statusStyles = {
|
||||||
|
operational: {
|
||||||
|
class: 'bg-operational text-white',
|
||||||
|
icon: 'fa-check-circle',
|
||||||
|
text: '正常运行'
|
||||||
|
},
|
||||||
|
degraded: {
|
||||||
|
class: 'bg-degraded text-white',
|
||||||
|
icon: 'fa-exclamation-triangle',
|
||||||
|
text: '性能下降'
|
||||||
|
},
|
||||||
|
outage: {
|
||||||
|
class: 'bg-outage text-white',
|
||||||
|
icon: 'fa-times-circle',
|
||||||
|
text: '服务中断'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新服务数据
|
||||||
|
function updateServices() {
|
||||||
|
fetch('/api/services/')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
// 更新最后更新时间
|
||||||
|
const now = new Date();
|
||||||
|
document.getElementById('last-updated').textContent = `最后更新: ${now.toLocaleString()}`;
|
||||||
|
|
||||||
|
// 统计各状态数量
|
||||||
|
const counts = {
|
||||||
|
operational: 0,
|
||||||
|
degraded: 0,
|
||||||
|
outage: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
data.forEach(service => {
|
||||||
|
counts[service.status]++;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新统计数字
|
||||||
|
document.getElementById('operational-count').textContent = counts.operational;
|
||||||
|
document.getElementById('degraded-count').textContent = counts.degraded;
|
||||||
|
document.getElementById('outage-count').textContent = counts.outage;
|
||||||
|
|
||||||
|
// 生成服务卡片
|
||||||
|
const container = document.getElementById('services-container');
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="col-span-full text-center py-12 text-gray-500">
|
||||||
|
<i class="fa fa-info-circle text-3xl mb-4"></i>
|
||||||
|
<p>暂无服务数据</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.forEach(service => {
|
||||||
|
const style = statusStyles[service.status];
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'bg-white rounded-xl overflow-hidden card-shadow card-hover';
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="flex justify-between items-start mb-4">
|
||||||
|
<h3 class="text-xl font-bold text-gray-800">${service.name}</h3>
|
||||||
|
<span class="px-3 py-1 rounded-full text-sm font-medium ${style.class}">
|
||||||
|
<i class="fa ${style.icon} mr-1"></i> ${style.text}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-600 mb-4">${service.description}</p>
|
||||||
|
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg">
|
||||||
|
<p class="text-gray-500">IP地址</p>
|
||||||
|
<p class="font-medium">${service.ip_address}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg">
|
||||||
|
<p class="text-gray-500">端口</p>
|
||||||
|
<p class="font-medium">${service.port}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg col-span-2">
|
||||||
|
<p class="text-gray-500">可靠率</p>
|
||||||
|
<div class="w-full bg-gray-200 rounded-full h-2 mt-1">
|
||||||
|
<div class="${style.class} h-2 rounded-full" style="width: ${service.reliability}%"></div>
|
||||||
|
</div>
|
||||||
|
<p class="font-medium mt-1">${service.reliability}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 text-xs text-gray-500">
|
||||||
|
<p>最后更新: ${new Date(service.last_updated).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container.appendChild(card);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('获取服务数据失败:', error);
|
||||||
|
const container = document.getElementById('services-container');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="col-span-full text-center py-12 text-red-500">
|
||||||
|
<i class="fa fa-exclamation-circle text-3xl mb-4"></i>
|
||||||
|
<p>加载服务数据失败,请刷新页面重试</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始加载
|
||||||
|
updateServices();
|
||||||
|
|
||||||
|
// 定时更新 (30秒)
|
||||||
|
setInterval(updateServices, 30000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
status/tests.py
Normal file
3
status/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
7
status/urls.py
Normal file
7
status/urls.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.home, name='home'),
|
||||||
|
path('api/services/', views.get_services, name='get_services'),
|
||||||
|
]
|
||||||
23
status/views.py
Normal file
23
status/views.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from .models import Service
|
||||||
|
|
||||||
|
# 主页视图
|
||||||
|
def home(request):
|
||||||
|
return render(request, 'status/index.html')
|
||||||
|
|
||||||
|
# API视图 - 获取所有服务状态
|
||||||
|
def get_services(request):
|
||||||
|
services = Service.objects.all()
|
||||||
|
data = []
|
||||||
|
for service in services:
|
||||||
|
data.append({
|
||||||
|
'name': service.name,
|
||||||
|
'status': service.status,
|
||||||
|
'description': service.description,
|
||||||
|
'ip_address': service.ip_address,
|
||||||
|
'port': service.port,
|
||||||
|
'reliability': float(service.reliability),
|
||||||
|
'last_updated': service.last_updated.isoformat()
|
||||||
|
})
|
||||||
|
return JsonResponse(data, safe=False)
|
||||||
0
statuspage/__init__.py
Normal file
0
statuspage/__init__.py
Normal file
BIN
statuspage/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
statuspage/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
statuspage/__pycache__/settings.cpython-38.pyc
Normal file
BIN
statuspage/__pycache__/settings.cpython-38.pyc
Normal file
Binary file not shown.
BIN
statuspage/__pycache__/urls.cpython-38.pyc
Normal file
BIN
statuspage/__pycache__/urls.cpython-38.pyc
Normal file
Binary file not shown.
BIN
statuspage/__pycache__/wsgi.cpython-38.pyc
Normal file
BIN
statuspage/__pycache__/wsgi.cpython-38.pyc
Normal file
Binary file not shown.
16
statuspage/asgi.py
Normal file
16
statuspage/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
ASGI config for statuspage project.
|
||||||
|
|
||||||
|
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'statuspage.settings')
|
||||||
|
|
||||||
|
application = get_asgi_application()
|
||||||
113
statuspage/settings.py
Normal file
113
statuspage/settings.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = 'django-insecure-$w+8+hw%p$2xi_fi+7avahc&03-y@x05e^r02-x3nt5johmk6l'
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'status',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'statuspage.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'statuspage.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'zh-hans'
|
||||||
|
|
||||||
|
TIME_ZONE = 'Asia/Shanghai'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = 'static/'
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
23
statuspage/urls.py
Normal file
23
statuspage/urls.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
URL configuration for statuspage project.
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/4.2/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
path('', include('status.urls')),
|
||||||
|
]
|
||||||
16
statuspage/wsgi.py
Normal file
16
statuspage/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
WSGI config for statuspage project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'statuspage.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
128
部署文件夹/settings.py
Normal file
128
部署文件夹/settings.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', '')
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
allowed_hosts_str = os.environ.get('DJANGO_ALLOWED_HOSTS', '')
|
||||||
|
ALLOWED_HOSTS = [host.strip() for host in allowed_hosts_str.split(',') if host.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'status',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'statuspage.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'statuspage.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
|
||||||
|
'NAME': os.environ.get('DB_NAME', ''),
|
||||||
|
'USER': os.environ.get('DB_USER', ''),
|
||||||
|
'PASSWORD': os.environ.get('DB_PASSWORD', ''),
|
||||||
|
'HOST': os.environ.get('DB_HOST', ''),
|
||||||
|
'PORT': os.environ.get('DB_PORT', ''),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'zh-hans'
|
||||||
|
|
||||||
|
TIME_ZONE = 'Asia/Shanghai'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = 'static/'
|
||||||
|
|
||||||
|
STATIC_ROOT = '/home/djangouser/statuspage/static/'
|
||||||
|
MEDIA_ROOT = '/home/djangouser/statuspage/media/'
|
||||||
|
MEDIA_URL = 'media/'
|
||||||
|
|
||||||
|
# Security settings
|
||||||
|
CSRF_COOKIE_SECURE = True
|
||||||
|
SESSION_COOKIE_SECURE = True
|
||||||
|
SECURE_SSL_REDIRECT = True
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
Reference in New Issue
Block a user