From 7792734fb0ebbede45955aae2ca2bda8609b4d39 Mon Sep 17 00:00:00 2001 From: xiaji Date: Thu, 31 Jul 2025 21:21:45 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db.sqlite3 | Bin 0 -> 135168 bytes manage.py | 22 ++ requirements.txt | 1 + status/__init__.py | 0 status/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 173 bytes status/__pycache__/admin.cpython-38.pyc | Bin 0 -> 620 bytes status/__pycache__/apps.cpython-38.pyc | Bin 0 -> 450 bytes status/__pycache__/models.cpython-38.pyc | Bin 0 -> 1400 bytes status/__pycache__/urls.cpython-38.pyc | Bin 0 -> 353 bytes status/__pycache__/views.cpython-38.pyc | Bin 0 -> 841 bytes status/admin.py | 9 + status/apps.py | 6 + status/migrations/0001_initial.py | 32 +++ status/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-38.pyc | Bin 0 -> 1402 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 184 bytes status/models.py | 24 ++ status/templates/status/index.html | 239 ++++++++++++++++++ status/tests.py | 3 + status/urls.py | 7 + status/views.py | 23 ++ statuspage/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 177 bytes .../__pycache__/settings.cpython-38.pyc | Bin 0 -> 2443 bytes statuspage/__pycache__/urls.cpython-38.pyc | Bin 0 -> 1018 bytes statuspage/__pycache__/wsgi.cpython-38.pyc | Bin 0 -> 586 bytes statuspage/asgi.py | 16 ++ statuspage/settings.py | 113 +++++++++ statuspage/urls.py | 23 ++ statuspage/wsgi.py | 16 ++ 部署文件夹/settings.py | 128 ++++++++++ 31 files changed, 662 insertions(+) create mode 100644 db.sqlite3 create mode 100644 manage.py create mode 100644 requirements.txt create mode 100644 status/__init__.py create mode 100644 status/__pycache__/__init__.cpython-38.pyc create mode 100644 status/__pycache__/admin.cpython-38.pyc create mode 100644 status/__pycache__/apps.cpython-38.pyc create mode 100644 status/__pycache__/models.cpython-38.pyc create mode 100644 status/__pycache__/urls.cpython-38.pyc create mode 100644 status/__pycache__/views.cpython-38.pyc create mode 100644 status/admin.py create mode 100644 status/apps.py create mode 100644 status/migrations/0001_initial.py create mode 100644 status/migrations/__init__.py create mode 100644 status/migrations/__pycache__/0001_initial.cpython-38.pyc create mode 100644 status/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 status/models.py create mode 100644 status/templates/status/index.html create mode 100644 status/tests.py create mode 100644 status/urls.py create mode 100644 status/views.py create mode 100644 statuspage/__init__.py create mode 100644 statuspage/__pycache__/__init__.cpython-38.pyc create mode 100644 statuspage/__pycache__/settings.cpython-38.pyc create mode 100644 statuspage/__pycache__/urls.cpython-38.pyc create mode 100644 statuspage/__pycache__/wsgi.cpython-38.pyc create mode 100644 statuspage/asgi.py create mode 100644 statuspage/settings.py create mode 100644 statuspage/urls.py create mode 100644 statuspage/wsgi.py create mode 100644 部署文件夹/settings.py diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..8828eb8cf42a69fbda04c4242a9e57192013a9d8 GIT binary patch literal 135168 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 literal 0 HcmV?d00001 diff --git a/status/__pycache__/admin.cpython-38.pyc b/status/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5334ac8e3e3a747dbe17e7a1059dc1490d2d6c3 GIT binary patch literal 620 zcmYjOy>1jS5VrT9yFCh^-~}kQ$pfGW1%X5Z5mJ<9S;=yCoWv*Z54I0+9S9^O8ag^8 zf#fDELdF6Pk4c0e`n zr8`2ukhQ3N^s4#z7EW^;L6DLHDFdKo2tgSEYCwdnvCA(B?t9iEby3S3;SyuHT^Kh7 zVQMXVE4$WBWY0K&7RI=!@3eJOEtKSYQc1gXx#GsMMGxExD5y?RxCnKEi@xR|L)b17 zmoer=7;_WGnhqAq?{mhUFSxplm&yw5W=3#bAF_QZ6qs*B-}K}j z{ys){aJSkuLYwNP}qWBM!(cC68b+hzKV=SIL5Q>A}g=MoVN$vM%}em%RL?76_;Q=LK*Fo s+~kRO#?FpeE;7;zmuYc;)d~IoY5Z%4KNX$nQYz5nE?gxlSVnhoe2qDdJu&mijyl`LM-ho;v3WAMYItwKh zrZ;FT{TJ7oMD7RJJ9`%}12a1_@9kq|I@M|kfqm7EPlxb+%VO13DC*#13j#+RJIKWb z#^?}ng_B#viG^>7p>Va0v_c9jq~T<}5d=-&>)HLT?5rAwq7E)LAXv!29KmmF5Uv<1 zC!nGxm3G9USmX+qn|#2$zOVBW-SxTU2z}4)H{!@Yuq}rtYa%1Jv_ev()MKtiX;PvT zxUnPi3Z-W;bCyVrTRj##5oMr2dzpkPu~!tzTV&z)2(sq3*%4MK%yXL!Y;)i5#;)Z> z!koR2XVd9ocsKi;%ts&QeDsixU(BET$>QodeR@kDpUr$IlLdj$gNu-Wp1h@yp@>zS iby^xp%FFE}GgJW0kpEHgtPDU>+{t&^l{XZUTOof0tbK_9 literal 0 HcmV?d00001 diff --git a/status/__pycache__/models.cpython-38.pyc b/status/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48a79f43c9b96dcc1ab058ad3a765ad27e51d8a5 GIT binary patch literal 1400 zcmZux-D?|15Wg>dpJZF|N80y7@5O&WDZ#;RQ$N@>$cAzVjKp#RyPHjeF0o_W!&Xej9(+M`P9x1Ja_Y>8Kw)IJp>}jt);_Ku2TR=%9c3>`ek9ad!FOe6;tCWw0FTMXtH!BWf;kK<|*y z3t0Z$D5JsP>h+nWrg~{yUW<;-qT{`=fSb!-UPOZ@(crgOhKV&~EhI(x>x+d1YczNf zAOCRq=>CTvSeB7u1-In}TvUKI-45}bo<|S@lNi%5Rg^|Yd(pw8(f2P$hi9Y5=kep0 zmiG2YQ&)i&w8khxaf3IFFwG-iV$K;^#j#(!#|jzsAQuHh4gSkT=qyo$uWh zImh7va~$cO%Q7hH?!@&>%wOC7Gtd(B0uq;v8XssJY6CP(LEkn1b+SSO!sJU_wK5{_ zBpr4fQFI(c5qcD5({a8Gi6=95oTQYZHa-wo>5vgGslgbN(S;%X&q^@zJ?B+JW$!1! zW|`EoImvDyvn%!WTk9KjXYsSomlr>&GnuU@EN&6@sY^XzQ*xh8D?F7B z5~H3=HeJsR?uzmf3Ftb0LsB%lM5A~8uWZvJJaEDupdHu@>XQt|3L%@vl1C$JASvk# z79(3xtE!p&s_LYc*O|1gT3JzmHtDwfdDu(~VP5aU!1XvwqGwj*w=KExf5AlldCwfkT%3nRxzH7 zC&X$|Jv`+PNhBdh6cGkYYT*Yowo mWixqc?jMM#f~poo)%VIX+3sfrty8wGekD}5gB`t#yWc?K}2h=>RY9z=o|a~VcLXS3CjFNz@?{`HTmxBOW}U_a|7Hi=`dZ~u z=?=H8`%vVM6qH2{YQpgtox!rwTS-@SX@5S*b`c8*Ahm`KQOK@?ft{sib6|uS!1V+J zZ_oEkmTD4B(X`qeC@=?1hv@nF0BPY?yaGx?yq$=C5-%3rL8fv8@yVCNljGyFcdt&q z9i8rfjZgRAP7XiCKVBc5J%2G7f1Z5!7*B7Cw-WUf=F1)H^@>dS7!~M7#x<$?PrHYX zCZf{*7!AYve9sxP5#L}VXUG@XlN%#<=>~iGhUTch5zYoS( z{LRazJ#?fa-8WPPZ>bYlFBh4z{#2uFN(Eh=lqM;1+epe)A*EIT)+-BbY*VW=5o<}B zm>t_n1(?-ADTPt;9v036v93BZno_etvDQ~z1J)I3YJE|b*g{Q7=@wC&Xolz#lC7<$ zg)n-S9CPHTCs3>xIzuN_qvPoSmng(8P^(c9-YcTu@4jl)l literal 0 HcmV?d00001 diff --git a/status/admin.py b/status/admin.py new file mode 100644 index 0000000..715c2ff --- /dev/null +++ b/status/admin.py @@ -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) diff --git a/status/apps.py b/status/apps.py new file mode 100644 index 0000000..1cb87e4 --- /dev/null +++ b/status/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class StatusConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'status' diff --git a/status/migrations/0001_initial.py b/status/migrations/0001_initial.py new file mode 100644 index 0000000..5f01ade --- /dev/null +++ b/status/migrations/0001_initial.py @@ -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': '服务状态', + }, + ), + ] diff --git a/status/migrations/__init__.py b/status/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/status/migrations/__pycache__/0001_initial.cpython-38.pyc b/status/migrations/__pycache__/0001_initial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7dc611335e686271c4e1d9399c18bf79dbea156 GIT binary patch literal 1402 zcmY*Z&2QX96t~CT?~i}-a zO@-*AREf;Dn8i(xxsH(OmJ8Gt7aNgoc#R;K!7YBpgDN!@lo|oGrddi&r>SNd2xv6Z z%z}!K4k<`?^ko7Wl;t1`ImmYi;zEpzP=d0h!3d1P7>vi36R62U1tvQx+MPgDfM*8i zSwuy6Zh%fAD#7#c0=x(>!OOAtDU_AzNOQC~)*Odd;MFLamBqJ@!E4(DPIoj*txdog zto(JEyYQssMVYxo`6_ErNf?}yWPYxy%cALfuF>Gs^eD(eo3ob5*Nso;y37g6nQ5~t zR2Ek$UvdRC9J58G9-Sc|J$@07{&pw0z8h?Ij_&NoB%5o7O&!Zy;RRI7wD3&N7pN+@ zZJO|J+KKDlt>EzR`0?iP?pLVK0>%CSg+#e==lJR`y~8iVuREwryS`^ybSPx+aIe3$ z$44X_B36_C2kaOE;+a~78C(#ZE6*&ZEF;65}6m3&@hTn#$ zP5wCA^tU&H%^Ur1?)SI$`!^55oA;|Ec8(qJ9&O*V%o=fv4MXM)1HTa8rg$$J#;3k%M{P%xVGi?fFk}&En>s+92E)Xoa%EvQo}>%W zx5c$cQv85fXK}{PMZsilcE#j_>mptAlI!Go>fo$3<`>Q;Oef;Xf-5l6DxG&cYSDpT zaV{Y&u_?_VBa5s>6PSjk=~%8z`5v={%zCJ~;yP4nj{BL+|2Kc)2Zg_l>BYyNALDKI z-TGx5cu`+tW|P(D+=kzxjwkB9!@IqQ5Bpo!dXEl*+dtQX+uiW)_w{7L!rceq&Uf`> z!s}0eFZJo^>9>plQB&=89{s;ExW#EavP37kuB(Jp)KT@MT8^KJdg3oqAS(G=|3m*L z_Nb<1E~#h=mSTnzQYQz(xXP&~Me;ZC-Lc=(+LGHlsF2zBl7_w?^+iMpjjCDgU$_s- AC;$Ke literal 0 HcmV?d00001 diff --git a/status/migrations/__pycache__/__init__.cpython-38.pyc b/status/migrations/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..612af4ba969b6d20d5fbd3cf70fa6b94653665cc GIT binary patch literal 184 zcmWIL<>g`kg2O@q86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o1Tr$aS`g2`x@7 zDvqhhOw7uRami0E%}vcKDUNxvW7U&gyPi#Jf3kbq(6vqL|{6#FEnDnB2_tqQsKS{Ji3r`1s7c%#!$cy@JYH95%W6DWy57b|7m%12F>t Du9`cq literal 0 HcmV?d00001 diff --git a/status/models.py b/status/models.py new file mode 100644 index 0000000..bb91ffe --- /dev/null +++ b/status/models.py @@ -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 = '服务状态' diff --git a/status/templates/status/index.html b/status/templates/status/index.html new file mode 100644 index 0000000..fe7163a --- /dev/null +++ b/status/templates/status/index.html @@ -0,0 +1,239 @@ + + + + + + 服务状态监控 + + + + + + + +
+
+
+
+ +

服务状态监控

+
+
+ + 最后更新: 加载中... +
+
+

实时监控系统服务运行状态与可靠性

+
+
+ + +
+ +
+
+
+
+

正常运行

+

0

+
+
+ +
+
+
+
+
+
+

性能下降

+

0

+
+
+ +
+
+
+
+
+
+

服务中断

+

0

+
+
+ +
+
+
+
+ + +
+ +
+ +

加载服务数据中...

+
+
+
+ + +
+
+

© 2025 服务状态监控系统 | 数据每30秒自动更新

+
+
+ + + + \ No newline at end of file diff --git a/status/tests.py b/status/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/status/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/status/urls.py b/status/urls.py new file mode 100644 index 0000000..9bbb9fa --- /dev/null +++ b/status/urls.py @@ -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'), +] \ No newline at end of file diff --git a/status/views.py b/status/views.py new file mode 100644 index 0000000..554bbcc --- /dev/null +++ b/status/views.py @@ -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) diff --git a/statuspage/__init__.py b/statuspage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/statuspage/__pycache__/__init__.cpython-38.pyc b/statuspage/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d72ece67a7992251fea0197592c9c5f25c59798f GIT binary patch literal 177 zcmWIL<>g`kf(=3e86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o1T($aS`g2`x@7 zDvqhhOw7uRami0E%}vcKDUNxvW7U&gyPi#Jf3kbq(6vqL|{6#FEnDg2eRHnE3e2yv&mLc)fzkTO2mI`6;D2sdgayJ_9iW0PIURnE(I) literal 0 HcmV?d00001 diff --git a/statuspage/__pycache__/settings.cpython-38.pyc b/statuspage/__pycache__/settings.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..989515d76be774c29a3f29dcd620e2975d3144d3 GIT binary patch literal 2443 zcmb7GTW=dh6ka>zf$mU13vT@SP-XjD2C!z%#N$^7>DC{==0dO`~$8v+!|IBXh=;W zUQL;tI)a8#0wqxjji59dHIr%@jiC(MhxVg!bO24DNi(I6qJ!!fI%IN)M~Bf7bQHaT zj+s1(p{e`48B;UpO?4kSj^4rv^!5~og!^$4Q}?42=pC>*f!>YQlhJyL#7tftN2k>T z=!`l6c}&9RAUdlaLeuJDbWY)>XY_20fIPHlgy|bS&VjI&1p#Jr;;VvC;AH?8&v6 zt252DQ|*%%)`_-CX0GV>^rmt8{M_uujrH@F@F(8+3$yEU4!hKHoA#Z{R?R=y1rZI` zVIEl#BTD_rL1_o#O$`V`7wj;$(ZB)4PLMuAy64EJk1dZVHWI zyN;NPzW&3MdaGg%mfHlc^_gB7NLvTC`%{+S8HgrE)VF<(gXi7iw%@tb<-5=8^6v8* z7BTL$bmI=pj`-1nr`x#F4u^#H`R(5sOtIrx)X(;S7`EFM%%->k2-rbevm@rCF|GB~ zG?<85W}Rs;RR-kcdelEV2(sHoM0llR`semw>EXLDVa%WD*>1OWNa<^kL#q9`0po2N zU*oW)o92V$yp+2om-7LiFDxlRN~%`1XqT*1YlTW#@u&J2Tcais^>!PWj>;G$5nk0h z7TX>Kw6a4Wy=xJM=lnDXd;95lkdVuZg|Zy*WvL{4qi`AcHwSW$dZ(>Zy5@RFgA0eC zD4bZQ*ppZ|;~j<-ViQXbr{{Lp!u9;?dqpY{hu9svh#iwPcllBCw?-l12MAx@m>gE)pilKs5nnd7%z8$U<;L} zj|53ggWF%zf@B-+Sc|N9aiBR!X>gxfIFx&sy4F1$B-(HtIZRE>ONty8K1eHaZb`0b zx8#q5;k-OwUkox*u~=D_^V-ddQd5F`g|bou5eT4LlptLy?TpN{X^vS;}j-rD6e; z73jxIQ7SLirA1lGRq`?b7D}?FR-k=Sbx64I{?&4j2zNCVGOq!0E{q;b#HT;OIIm{Mgvg zc#>wEvjkM=!x(xx{f{9(i-!+9;-(h&!4aH4a^Ku9G^RJ9G}+f!m$O zjVBm%QQn;6Uo&! zOsUdPid+)~4UmFbz-N>^S0UUqC`?EKDK~&@0-dD;T?=2;*hdTNZp38 zF9m~auZOa%3QlO2`KwjFRbU0oC^T1%*x_=kwNmLC3Px5SM~tks?70>A>nT#|+Kv&1 zXHfsfLg*opmPlAKp3=Ezu>OaQ%@2|4S&-%64@O%&Npnp3z?e|hv(jvnr~Yh}#y~^G zw8!R>603<9U9Bg`ld z+`_Yt7mX@=f3A#21=w#+hO)f*Ah=XCdOb5YyG`{XH^x=nNEKMvT2j-_T!=1*4uGM5vk%`ibgcv5J}Dm*D5~ye0+9#dhzDv+1Hcv zqc8FK(d+T?hxq%elZzJz<9DCNZ{NqipHFNnh1p&)f0GHEr{tDnz?FUI-qM1zR9kQs Ge*OY$6iCqk literal 0 HcmV?d00001 diff --git a/statuspage/__pycache__/wsgi.cpython-38.pyc b/statuspage/__pycache__/wsgi.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4000c16939b491eaff5b544c69b3d353ce05a0f GIT binary patch literal 586 zcmYjP&1(}u6rbIsO;ra20*~ypi&->Gh*KcM=-)D!P!%L!J!NJe=P4&TI4qJ9tkg8f$zEX9sx!2u) DjF-e~ literal 0 HcmV?d00001 diff --git a/statuspage/asgi.py b/statuspage/asgi.py new file mode 100644 index 0000000..1f943e8 --- /dev/null +++ b/statuspage/asgi.py @@ -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() diff --git a/statuspage/settings.py b/statuspage/settings.py new file mode 100644 index 0000000..bf307ee --- /dev/null +++ b/statuspage/settings.py @@ -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' diff --git a/statuspage/urls.py b/statuspage/urls.py new file mode 100644 index 0000000..123b6d1 --- /dev/null +++ b/statuspage/urls.py @@ -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')), +] diff --git a/statuspage/wsgi.py b/statuspage/wsgi.py new file mode 100644 index 0000000..47bd39b --- /dev/null +++ b/statuspage/wsgi.py @@ -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() diff --git a/部署文件夹/settings.py b/部署文件夹/settings.py new file mode 100644 index 0000000..17b1888 --- /dev/null +++ b/部署文件夹/settings.py @@ -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'