This commit is contained in:
2026-03-29 00:03:04 +09:00
commit d8cce8499f
62 changed files with 27628 additions and 0 deletions

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
# Database files
databases/*.db
# Log files
logs/*.log
logs/**/*.log
logs/**/*.txt
# macOS
.DS_Store
*/.DS_Store
**/.DS_Store
# Python's Cache
__pycache__
*/__pycache__
**/__pycache__

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Nercone
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
> [!IMPORTANT]
> 今後大きいファイルなどを公開する可能性があり、ファイルごとの容量制限を回避するために、このリポジトリは[GitHub](https://github.com/nercone-dev/website/)から移行しました。
# website
[nercone.dev](https://nercone.dev/)のソースコードです
FastAPI + Jinja2 on VPS

0
databases/.gitkeep Normal file
View File

0
logs/.gitkeep Normal file
View File

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Nercone Web Server Updator
[Service]
Type=oneshot
User=root
Group=root
WorkingDirectory=/srv/nercone-webserver
ExecStart=/usr/bin/bash /srv/nercone-webserver/update.sh

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Nercone Web Server Updator
[Timer]
OnCalendar=*-*-* 00:00:00 UTC
Persistent=true
[Install]
WantedBy=timers.target

14
nercone-webserver.service Normal file
View File

@@ -0,0 +1,14 @@
[Unit]
Description=Nercone Web Server
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/srv/nercone-webserver
ExecStart=/usr/bin/bash /srv/nercone-webserver/start.sh
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,7 @@
Contact: mailto:nercone@nercone.dev
Contact: mailto:bugaboo.decent_8y@icloud.com
Expires: 2026-05-31T15:00:00.000Z
Encryption: https://nercone.dev/public-key/
Acknowledgments: https://nercone.dev/vulnerability-reporters/
Preferred-Languages: ja, en
Canonical: https://nercone.dev/.well-known/security.txt

20
public/about.html Normal file
View File

@@ -0,0 +1,20 @@
{% extends "/base.html" %}
{% block title %}About Nercone{% endblock %}
{% block extra_title %}About{% endblock %}
{% block description %}Nerconeについて{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<p>思いついたものを適当に作っているただの学生です。</p>
<p>作ろうと思ったものをとりあえず作ってみていったらいつの間にかこうなってました。</p>
<p>低レイヤに興味を持ちつつ結局いつもPythonを使ってしまう残念な人です。</p>
<p>名前からもわかると思いますが圧倒的猫派です。理由?可愛いからに決まってるじゃないですか。</p>
<h3>今の目標</h3>
<p><!-- てとらさんと結婚することと、-->カーネルレベルからOSを作ることと、Linuxディストリビューションを作ることと、会社建てるかAppleに入ること。</p>
<h3>興味</h3>
<p><!-- てとらさん, -->Apple, macOS, ARM, Linux, 自作OS, OSINT, 機械学習 など</p>
<!-- <p>好きな人: てとらさん</p> -->
<!-- <p>大好きな人: てとらさん</p> -->
<!-- <p>最高な人: てとらさん</p> -->
<!-- <p>この世で一番好きな人: てとらさん</p> -->
<!-- <p>性癖: てとらさん</p> -->
{% endblock %}

View File

@@ -0,0 +1,28 @@
{% extends "/base.html" %}
{% block title %}アクセスカウンタ - Nercone{% endblock %}
{% block extra_title %}Access Counter{% endblock %}
{% block header_desc %}ただのアクセスカウンタ{% endblock %}
{% block extra_head %}
<style>
main {
display: grid;
grid-template-rows: auto 1fr auto;
}
.big-text {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 48pt;
}
</style>
{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<p class="text-highlight big-text">{{ get_access_count() }}</p>
<div class="block">
<p>ただのアクセスカウンターです。</p>
<p>誰かがこのサイトのページへのアクセスに成功した場合に自動的に1増えます。(pngなどのファイルへのアクセスは加算されません)</p>
<p>詳しくは<a href="https://gitea.nercone.dev/nercone-dev/website/">このサーバーのソースコード</a><span class="text-lowlight">※キリ番報告は不要です</span></p>
</div>
{% endblock %}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_レイヤー_1" data-name="レイヤー 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 100">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #fff;
stroke-miterlimit: 10;
stroke-width: 4px;
}
.cls-2 {
fill: #202020;
}
</style>
</defs>
<rect class="cls-2" width="320" height="100"/>
<polyline class="cls-1" points="208 58.1 112 26 112 74 160 74 208 26 208 74 183 74"/>
</svg>

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_レイヤー_1" data-name="レイヤー 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 720">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #fff;
stroke-linecap: square;
stroke-miterlimit: 10;
stroke-width: 16px;
}
.cls-2 {
fill: #202020;
}
</style>
</defs>
<rect class="cls-2" width="720" height="720" rx="128" ry="128"/>
<polyline class="cls-1" points="0 360 360 360 540 180 540 540 180 540 180 180 720 360"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_レイヤー_1" data-name="レイヤー 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 640">
<defs>
<style>
.cls-1 {
fill: #404040;
}
.cls-2 {
fill: none;
stroke: #fff;
stroke-miterlimit: 10;
stroke-width: 16px;
}
.cls-3 {
fill: #303030;
}
.cls-4 {
fill: #fff;
}
.cls-5 {
fill: #202020;
}
</style>
</defs>
<g>
<rect class="cls-5" width="1280" height="640"/>
<polygon class="cls-3" points="0 0 1280 428 1280 640 0 640 0 0"/>
<polygon class="cls-1" points="640 640 1280 0 1280 640 640 640"/>
</g>
<polyline class="cls-2" points="463.71 341.6 207.71 256 207.71 384 335.71 384 463.71 256 463.71 384 397.05 384"/>
<g>
<path class="cls-4" d="M559.71,334.95v-77.91h17.78l24.63,39.42c1.25,2.02,2.53,4.22,3.82,6.59,1.29,2.37,2.61,5,3.95,7.9,1.34,2.89,2.67,6.15,4,9.78h-1.62c-.24-2.89-.46-5.98-.65-9.25-.19-3.28-.36-6.41-.5-9.41-.14-3-.21-5.54-.21-7.63v-37.39h16.31v77.91h-17.83l-22.38-35.76c-1.64-2.68-3.16-5.29-4.55-7.82-1.39-2.53-2.85-5.32-4.37-8.37-1.52-3.05-3.32-6.7-5.41-10.95h2.04c.21,3.76.42,7.37.63,10.82s.37,6.58.5,9.39c.12,2.81.18,5.1.18,6.88v35.82h-16.31Z"/>
<path class="cls-4" d="M641.33,334.95v-77.91h52.13v13.23h-36.18v18.82h33.46v13.02h-33.46v19.61h36.29v13.23h-52.24Z"/>
<path class="cls-4" d="M706.38,334.95v-77.91h30.38c5.93,0,10.96,1.05,15.11,3.14,4.15,2.09,7.31,5.04,9.49,8.84,2.18,3.8,3.27,8.26,3.27,13.39s-1.11,9.54-3.32,13.25c-2.21,3.71-5.42,6.55-9.62,8.52-4.2,1.97-9.3,2.95-15.29,2.95h-20.6v-12.92h18.04c3.28,0,5.96-.44,8.05-1.33s3.66-2.21,4.71-3.97c1.05-1.76,1.57-3.93,1.57-6.51s-.52-4.87-1.57-6.67c-1.05-1.8-2.62-3.16-4.73-4.1-2.11-.94-4.8-1.41-8.08-1.41h-11.45v64.73h-15.95ZM749.73,334.95l-18.98-35.45h17.31l19.35,35.45h-17.67Z"/>
<path class="cls-4" d="M810.75,336c-6.9,0-13.08-1.57-18.54-4.71-5.46-3.14-9.76-7.69-12.92-13.65-3.16-5.96-4.73-13.16-4.73-21.59s1.59-15.69,4.76-21.67c3.17-5.98,7.49-10.54,12.97-13.67,5.47-3.14,11.63-4.71,18.46-4.71,4.43,0,8.55.62,12.37,1.86,3.82,1.24,7.21,3.04,10.17,5.41,2.96,2.37,5.39,5.27,7.27,8.71,1.88,3.43,3.12,7.35,3.71,11.74h-16.16c-.35-2.16-1.02-4.07-2.01-5.73-.99-1.66-2.23-3.07-3.71-4.24-1.48-1.17-3.18-2.06-5.1-2.67-1.92-.61-3.99-.92-6.22-.92-4.04,0-7.59,1.01-10.64,3.03-3.05,2.02-5.42,4.96-7.11,8.81-1.69,3.85-2.54,8.53-2.54,14.04s.85,10.34,2.56,14.17c1.71,3.83,4.08,6.73,7.11,8.68s6.55,2.93,10.56,2.93c2.23,0,4.3-.31,6.2-.92,1.9-.61,3.61-1.5,5.12-2.67s2.77-2.59,3.76-4.26,1.68-3.57,2.07-5.7h16.16c-.42,3.63-1.49,7.09-3.22,10.41s-4.02,6.27-6.88,8.89c-2.86,2.61-6.26,4.67-10.2,6.17-3.94,1.5-8.37,2.25-13.28,2.25Z"/>
<path class="cls-4" d="M890.12,336c-6.87,0-13.05-1.57-18.54-4.71s-9.83-7.69-13.02-13.65c-3.19-5.96-4.78-13.16-4.78-21.59s1.59-15.69,4.78-21.67c3.19-5.98,7.53-10.54,13.02-13.67s11.67-4.71,18.54-4.71,13.09,1.57,18.56,4.71c5.47,3.14,9.8,7.69,12.99,13.67,3.19,5.98,4.78,13.2,4.78,21.67s-1.59,15.63-4.78,21.59c-3.19,5.96-7.52,10.51-12.99,13.65-5.47,3.14-11.66,4.71-18.56,4.71ZM890.12,321.83c4.04,0,7.57-.98,10.59-2.95,3.01-1.97,5.36-4.88,7.03-8.73s2.51-8.55,2.51-14.09-.84-10.34-2.51-14.2c-1.67-3.85-4.02-6.76-7.03-8.73-3.02-1.97-6.54-2.95-10.59-2.95s-7.53.99-10.56,2.98-5.39,4.91-7.06,8.76c-1.67,3.85-2.51,8.57-2.51,14.14s.84,10.23,2.51,14.07,4.03,6.75,7.06,8.73,6.55,2.98,10.56,2.98Z"/>
<path class="cls-4" d="M938.43,334.95v-77.91h17.78l24.63,39.42c1.25,2.02,2.53,4.22,3.82,6.59,1.29,2.37,2.61,5,3.95,7.9,1.34,2.89,2.67,6.15,4,9.78h-1.62c-.24-2.89-.46-5.98-.65-9.25-.19-3.28-.36-6.41-.5-9.41-.14-3-.21-5.54-.21-7.63v-37.39h16.31v77.91h-17.83l-22.38-35.76c-1.64-2.68-3.16-5.29-4.55-7.82-1.39-2.53-2.85-5.32-4.37-8.37-1.52-3.05-3.32-6.7-5.41-10.95h2.04c.21,3.76.42,7.37.63,10.82.21,3.45.37,6.58.5,9.39.12,2.81.18,5.1.18,6.88v35.82h-16.31Z"/>
<path class="cls-4" d="M1020.05,334.95v-77.91h52.13v13.23h-36.18v18.82h33.46v13.02h-33.46v19.61h36.29v13.23h-52.24Z"/>
</g>
<g>
<path class="cls-4" d="M561.94,361.36v17.46h-2.23v-17.46h2.23Z"/>
<path class="cls-4" d="M572.65,383.72v-18h2.04v2.1h.23c.15-.23.36-.53.62-.88s.65-.67,1.15-.96,1.18-.42,2.03-.42c1.09,0,2.06.27,2.89.82s1.48,1.33,1.96,2.34.71,2.21.71,3.59-.23,2.59-.7,3.61-1.12,1.8-1.95,2.36-1.79.83-2.89.83c-.83,0-1.5-.14-2-.43s-.9-.61-1.17-.97-.48-.67-.63-.91h-.16v6.93h-2.11ZM578.42,377.21c.82,0,1.51-.22,2.06-.66s.96-1.04,1.24-1.78.42-1.58.42-2.49-.14-1.72-.41-2.46-.69-1.31-1.24-1.74-1.24-.64-2.07-.64-1.48.2-2.03.61-.96.97-1.24,1.69-.42,1.57-.42,2.54.14,1.82.42,2.57.7,1.32,1.25,1.74,1.22.63,2.01.63Z"/>
<path class="cls-4" d="M587.34,378.82v-13.1h2.04v2.02h.14c.24-.66.67-1.2,1.29-1.6s1.32-.6,2.1-.6c.16,0,.34,0,.56.01s.38.02.51.02v2.12c-.06-.02-.22-.04-.47-.07s-.52-.05-.8-.05c-.62,0-1.19.13-1.68.39s-.88.62-1.17,1.08-.42.98-.42,1.58v8.2h-2.11Z"/>
<path class="cls-4" d="M601.31,379.1c-1.19,0-2.23-.28-3.12-.84s-1.59-1.35-2.09-2.37-.75-2.2-.75-3.54.25-2.56.75-3.58,1.2-1.81,2.09-2.37,1.94-.84,3.12-.84,2.24.28,3.14.84,1.6,1.35,2.1,2.37.75,2.21.75,3.58-.25,2.52-.75,3.54-1.2,1.8-2.1,2.37-1.95.84-3.14.84ZM601.31,377.21c.88,0,1.61-.23,2.18-.68s.99-1.05,1.27-1.79.41-1.54.41-2.4-.14-1.67-.41-2.42-.7-1.35-1.27-1.8-1.3-.69-2.18-.69-1.59.23-2.16.69-.98,1.06-1.25,1.8-.41,1.55-.41,2.43.14,1.66.41,2.4.69,1.33,1.25,1.79,1.28.68,2.16.68Z"/>
<path class="cls-4" d="M615.68,384c-.97,0-1.81-.12-2.51-.37s-1.29-.57-1.76-.98-.84-.85-1.11-1.32l1.71-1.1c.19.25.42.53.7.83s.65.56,1.12.78,1.09.32,1.86.32c1.05,0,1.91-.25,2.58-.75s1.01-1.29,1.01-2.37v-2.62h-.2c-.15.25-.36.55-.63.89s-.66.65-1.16.91-1.18.39-2.02.39c-1.05,0-1.99-.25-2.82-.74s-1.5-1.22-1.98-2.17-.73-2.11-.73-3.48.24-2.53.71-3.52,1.13-1.77,1.97-2.31,1.8-.82,2.89-.82c.84,0,1.52.14,2.02.42s.89.6,1.17.96.48.66.63.89h.2v-2.1h2.05v13.44c0,1.12-.25,2.04-.76,2.76s-1.19,1.24-2.06,1.58-1.83.5-2.89.5ZM615.62,376.73c.8,0,1.47-.18,2.02-.54s.96-.89,1.24-1.57.42-1.5.42-2.46-.14-1.75-.42-2.46-.69-1.26-1.24-1.66-1.22-.6-2.03-.6-1.52.21-2.07.64-.97.99-1.25,1.71-.42,1.5-.42,2.38.14,1.69.42,2.38.7,1.22,1.25,1.61,1.24.58,2.06.58Z"/>
<path class="cls-4" d="M625.09,378.82v-13.1h2.04v2.02h.14c.24-.66.67-1.2,1.29-1.6s1.32-.6,2.1-.6c.16,0,.34,0,.56.01s.38.02.51.02v2.12c-.06-.02-.22-.04-.47-.07s-.52-.05-.8-.05c-.62,0-1.19.13-1.68.39s-.88.62-1.17,1.08-.42.98-.42,1.58v8.2h-2.11Z"/>
<path class="cls-4" d="M637.79,379.12c-.83,0-1.58-.16-2.26-.47s-1.21-.77-1.61-1.37-.6-1.33-.6-2.19c0-.75.15-1.36.45-1.83s.69-.84,1.19-1.11,1.04-.47,1.65-.6,1.21-.24,1.82-.32c.78-.1,1.42-.18,1.91-.23s.86-.15,1.1-.28.36-.36.36-.68v-.07c0-.55-.1-1.01-.3-1.39s-.51-.67-.91-.87-.91-.3-1.52-.3-1.16.1-1.61.29-.81.44-1.08.73-.49.59-.63.9l-2.03-.67c.34-.8.79-1.42,1.36-1.87s1.2-.77,1.89-.96,1.37-.28,2.04-.28c.44,0,.94.05,1.49.16s1.1.31,1.62.63.95.78,1.29,1.39.51,1.43.51,2.46v8.64h-2.07v-1.78h-.14c-.15.3-.39.62-.71.94s-.75.6-1.28.81-1.16.33-1.9.33ZM638.16,377.26c.78,0,1.44-.15,1.98-.46s.95-.7,1.23-1.19.42-1,.42-1.53v-1.82c-.09.1-.27.19-.56.28s-.62.15-.99.22-.73.12-1.08.16-.62.08-.83.1c-.51.06-.98.17-1.42.32s-.79.38-1.06.67-.4.69-.4,1.19c0,.45.12.83.35,1.14s.55.54.96.69.87.23,1.39.23Z"/>
<path class="cls-4" d="M647.6,378.82v-13.1h2.05v2.93s-.15,0-.15,0c.18-.71.46-1.3.85-1.76s.84-.81,1.36-1.05,1.06-.35,1.62-.35c.95,0,1.72.29,2.31.88s.97,1.36,1.14,2.31h-.28c.16-.66.44-1.22.86-1.7s.92-.84,1.51-1.1,1.23-.39,1.93-.39c.76,0,1.45.16,2.07.49s1.12.82,1.49,1.49.56,1.5.56,2.52v8.84h-2.11v-8.78c0-.95-.27-1.62-.8-2.03s-1.15-.61-1.85-.61c-.59,0-1.09.12-1.52.37s-.75.58-.98,1.01-.35.92-.35,1.48v8.55h-2.11v-8.99c0-.73-.24-1.31-.73-1.76s-1.1-.67-1.84-.67c-.51,0-.99.12-1.44.36s-.81.59-1.08,1.06-.41,1.05-.41,1.75v8.25h-2.11Z"/>
<path class="cls-4" d="M668.63,378.82v-13.1h2.05v2.93s-.15,0-.15,0c.18-.71.46-1.3.85-1.76s.84-.81,1.36-1.05,1.06-.35,1.62-.35c.95,0,1.72.29,2.31.88s.97,1.36,1.14,2.31h-.28c.16-.66.44-1.22.86-1.7s.92-.84,1.51-1.1,1.23-.39,1.93-.39c.76,0,1.45.16,2.07.49s1.12.82,1.49,1.49.56,1.5.56,2.52v8.84h-2.11v-8.78c0-.95-.27-1.62-.8-2.03s-1.15-.61-1.85-.61c-.59,0-1.09.12-1.52.37s-.75.58-.98,1.01-.35.92-.35,1.48v8.55h-2.11v-8.99c0-.73-.24-1.31-.73-1.76s-1.1-.67-1.84-.67c-.51,0-.99.12-1.44.36s-.81.59-1.08,1.06-.41,1.05-.41,1.75v8.25h-2.11Z"/>
<path class="cls-4" d="M690.72,363.57c-.4,0-.74-.14-1.03-.41s-.43-.6-.43-.98.14-.72.43-.99.63-.4,1.03-.4.75.13,1.04.4.43.6.43.99-.14.71-.43.98-.64.41-1.04.41ZM689.65,378.82v-13.1h2.11v13.1h-2.11Z"/>
<path class="cls-4" d="M697.57,370.97v7.85h-2.11v-13.1h2.03v3.18s-.28,0-.28,0c.4-1.21.97-2.07,1.71-2.58s1.6-.76,2.6-.76c.88,0,1.66.18,2.32.54s1.18.91,1.55,1.64.56,1.65.56,2.76v8.32h-2.11v-8.14c0-1.01-.26-1.8-.79-2.37s-1.25-.86-2.16-.86c-.62,0-1.19.14-1.69.41s-.89.67-1.18,1.2-.43,1.16-.43,1.91Z"/>
<path class="cls-4" d="M714.95,384c-.97,0-1.81-.12-2.51-.37s-1.29-.57-1.76-.98-.84-.85-1.11-1.32l1.71-1.1c.19.25.42.53.7.83s.65.56,1.12.78,1.09.32,1.86.32c1.05,0,1.91-.25,2.58-.75s1.01-1.29,1.01-2.37v-2.62h-.2c-.15.25-.36.55-.63.89s-.66.65-1.16.91-1.18.39-2.02.39c-1.05,0-1.99-.25-2.82-.74s-1.5-1.22-1.98-2.17-.73-2.11-.73-3.48.24-2.53.71-3.52,1.13-1.77,1.97-2.31,1.8-.82,2.89-.82c.84,0,1.52.14,2.02.42s.89.6,1.17.96.48.66.63.89h.2v-2.1h2.05v13.44c0,1.12-.25,2.04-.76,2.76s-1.19,1.24-2.06,1.58-1.83.5-2.89.5ZM714.89,376.73c.8,0,1.47-.18,2.02-.54s.96-.89,1.24-1.57.42-1.5.42-2.46-.14-1.75-.42-2.46-.69-1.26-1.24-1.66-1.22-.6-2.03-.6-1.52.21-2.07.64-.97.99-1.25,1.71-.42,1.5-.42,2.38.14,1.69.42,2.38.7,1.22,1.25,1.61,1.24.58,2.06.58Z"/>
<path class="cls-4" d="M736.62,379.1c-1.27,0-2.36-.28-3.28-.84s-1.62-1.35-2.12-2.36-.74-2.18-.74-3.53.24-2.53.73-3.55,1.17-1.82,2.06-2.4,1.92-.87,3.11-.87c.7,0,1.38.12,2.06.35s1.29.6,1.84,1.11.99,1.19,1.32,2.02.49,1.86.49,3.07v.88h-10.17v-1.78h9.04l-.98.66c0-.86-.13-1.62-.4-2.29s-.67-1.19-1.2-1.57-1.19-.57-1.98-.57-1.46.19-2.03.58-.99.89-1.29,1.51-.45,1.29-.45,2v1.18c0,.97.17,1.79.5,2.47s.81,1.19,1.41,1.54,1.31.52,2.1.52c.52,0,.98-.07,1.41-.22s.79-.37,1.09-.67.54-.67.7-1.12l2.04.56c-.2.66-.55,1.23-1.03,1.73s-1.08.89-1.8,1.17-1.52.42-2.42.42Z"/>
<path class="cls-4" d="M748.42,378.82l-5-13.1h2.31l2.79,7.77c.3.8.55,1.61.76,2.41s.42,1.58.65,2.33h-.77c.23-.75.44-1.53.65-2.33s.46-1.6.74-2.41l2.79-7.77h2.31l-5,13.1h-2.21Z"/>
<path class="cls-4" d="M763.16,379.1c-1.27,0-2.36-.28-3.28-.84s-1.62-1.35-2.12-2.36-.74-2.18-.74-3.53.24-2.53.73-3.55,1.17-1.82,2.06-2.4,1.92-.87,3.11-.87c.7,0,1.38.12,2.06.35s1.29.6,1.84,1.11.99,1.19,1.32,2.02.49,1.86.49,3.07v.88h-10.17v-1.78h9.04l-.98.66c0-.86-.13-1.62-.4-2.29s-.67-1.19-1.2-1.57-1.19-.57-1.98-.57-1.46.19-2.03.58-.99.89-1.29,1.51-.45,1.29-.45,2v1.18c0,.97.17,1.79.5,2.47s.81,1.19,1.41,1.54,1.31.52,2.1.52c.52,0,.98-.07,1.41-.22s.79-.37,1.09-.67.54-.67.7-1.12l2.04.56c-.2.66-.55,1.23-1.03,1.73s-1.08.89-1.8,1.17-1.52.42-2.42.42Z"/>
<path class="cls-4" d="M771.65,378.82v-13.1h2.04v2.02h.14c.24-.66.67-1.2,1.29-1.6s1.32-.6,2.1-.6c.16,0,.34,0,.56.01s.38.02.51.02v2.12c-.06-.02-.22-.04-.47-.07s-.52-.05-.8-.05c-.62,0-1.19.13-1.68.39s-.88.62-1.17,1.08-.42.98-.42,1.58v8.2h-2.11Z"/>
<path class="cls-4" d="M780.84,383.57l.54-1.83.22.06c.43.11.82.14,1.17.09s.67-.22.95-.52.53-.76.74-1.38l.38-1.09-5-13.17h2.31l2.79,7.77c.3.81.55,1.62.75,2.41s.42,1.57.64,2.32h-.77c.23-.75.44-1.53.65-2.33s.46-1.6.74-2.41l2.81-7.77h2.3l-5.75,15.06c-.27.7-.58,1.27-.96,1.72s-.8.78-1.29,1-1.05.32-1.66.32c-.38,0-.7-.03-.97-.08s-.46-.11-.57-.16Z"/>
<path class="cls-4" d="M798.99,379.1c-1.09,0-2.05-.28-2.88-.83s-1.49-1.34-1.96-2.36-.7-2.22-.7-3.61.24-2.58.71-3.59,1.13-1.79,1.96-2.34,1.8-.82,2.88-.82c.85,0,1.53.14,2.03.42s.88.6,1.15.96.47.65.62.88h.16v-6.46h2.11v17.46h-2.04v-2.03h-.23c-.14.24-.35.54-.63.91s-.67.69-1.17.97-1.17.43-2.01.43ZM799.3,377.21c.8,0,1.47-.21,2.02-.63s.96-1,1.24-1.74.42-1.6.42-2.57-.14-1.81-.42-2.54-.69-1.29-1.24-1.69-1.22-.61-2.03-.61-1.52.21-2.07.64-.96,1.01-1.24,1.74-.41,1.55-.41,2.46.14,1.74.42,2.49.69,1.34,1.25,1.78,1.24.66,2.05.66Z"/>
<path class="cls-4" d="M812.44,379.12c-.83,0-1.58-.16-2.26-.47s-1.21-.77-1.61-1.37-.6-1.33-.6-2.19c0-.75.15-1.36.45-1.83s.69-.84,1.19-1.11,1.04-.47,1.65-.6,1.21-.24,1.82-.32c.78-.1,1.42-.18,1.91-.23s.86-.15,1.1-.28.36-.36.36-.68v-.07c0-.55-.1-1.01-.3-1.39s-.51-.67-.91-.87-.91-.3-1.52-.3-1.16.1-1.61.29-.81.44-1.08.73-.49.59-.63.9l-2.03-.67c.34-.8.79-1.42,1.36-1.87s1.2-.77,1.89-.96,1.37-.28,2.04-.28c.44,0,.94.05,1.49.16s1.1.31,1.62.63.95.78,1.29,1.39.51,1.43.51,2.46v8.64h-2.07v-1.78h-.14c-.15.3-.39.62-.71.94s-.75.6-1.28.81-1.16.33-1.9.33ZM812.8,377.26c.78,0,1.44-.15,1.98-.46s.95-.7,1.23-1.19.42-1,.42-1.53v-1.82c-.09.1-.27.19-.56.28s-.62.15-.99.22-.73.12-1.08.16-.62.08-.83.1c-.51.06-.98.17-1.42.32s-.79.38-1.06.67-.4.69-.4,1.19c0,.45.12.83.35,1.14s.55.54.96.69.87.23,1.39.23Z"/>
<path class="cls-4" d="M821.68,383.57l.54-1.83.22.06c.43.11.82.14,1.17.09s.67-.22.95-.52.53-.76.74-1.38l.38-1.09-5-13.17h2.31l2.79,7.77c.3.81.55,1.62.75,2.41s.42,1.57.64,2.32h-.77c.23-.75.44-1.53.65-2.33s.46-1.6.74-2.41l2.81-7.77h2.3l-5.75,15.06c-.27.7-.58,1.27-.96,1.72s-.8.78-1.29,1-1.05.32-1.66.32c-.38,0-.7-.03-.97-.08s-.46-.11-.57-.16Z"/>
<path class="cls-4" d="M848.23,365.72v1.8h-7.71v-1.8h7.71ZM843.09,378.82v-14.85c0-.76.18-1.39.53-1.9s.82-.89,1.39-1.15,1.17-.39,1.8-.39c.5,0,.91.04,1.24.12s.57.16.73.23l-.59,1.8c-.11-.04-.25-.08-.43-.13s-.41-.07-.71-.07c-.65,0-1.12.17-1.42.5s-.45.81-.45,1.45v14.38h-2.11Z"/>
<path class="cls-4" d="M855.65,379.1c-1.19,0-2.23-.28-3.12-.84s-1.59-1.35-2.09-2.37-.75-2.2-.75-3.54.25-2.56.75-3.58,1.2-1.81,2.09-2.37,1.94-.84,3.12-.84,2.24.28,3.14.84,1.6,1.35,2.1,2.37.75,2.21.75,3.58-.25,2.52-.75,3.54-1.2,1.8-2.1,2.37-1.95.84-3.14.84ZM855.65,377.21c.88,0,1.61-.23,2.18-.68s.99-1.05,1.27-1.79.41-1.54.41-2.4-.14-1.67-.41-2.42-.7-1.35-1.27-1.8-1.3-.69-2.18-.69-1.59.23-2.16.69-.98,1.06-1.25,1.8-.41,1.55-.41,2.43.14,1.66.41,2.4.69,1.33,1.25,1.79,1.28.68,2.16.68Z"/>
<path class="cls-4" d="M864.7,378.82v-13.1h2.04v2.02h.14c.24-.66.67-1.2,1.29-1.6s1.32-.6,2.1-.6c.16,0,.34,0,.56.01s.38.02.51.02v2.12c-.06-.02-.22-.04-.47-.07s-.52-.05-.8-.05c-.62,0-1.19.13-1.68.39s-.88.62-1.17,1.08-.42.98-.42,1.58v8.2h-2.11Z"/>
<path class="cls-4" d="M880.49,378.82v-13.1h2.05v2.93s-.15,0-.15,0c.18-.71.46-1.3.85-1.76s.84-.81,1.36-1.05,1.06-.35,1.62-.35c.95,0,1.72.29,2.31.88s.97,1.36,1.14,2.31h-.28c.16-.66.44-1.22.86-1.7s.92-.84,1.51-1.1,1.23-.39,1.93-.39c.76,0,1.45.16,2.07.49s1.12.82,1.49,1.49.56,1.5.56,2.52v8.84h-2.11v-8.78c0-.95-.27-1.62-.8-2.03s-1.15-.61-1.85-.61c-.59,0-1.09.12-1.52.37s-.75.58-.98,1.01-.35.92-.35,1.48v8.55h-2.11v-8.99c0-.73-.24-1.31-.73-1.76s-1.1-.67-1.84-.67c-.51,0-.99.12-1.44.36s-.81.59-1.08,1.06-.41,1.05-.41,1.75v8.25h-2.11Z"/>
<path class="cls-4" d="M900.95,383.57l.54-1.83.22.06c.43.11.82.14,1.17.09s.67-.22.95-.52.53-.76.74-1.38l.38-1.09-5-13.17h2.31l2.79,7.77c.3.81.55,1.62.75,2.41s.42,1.57.64,2.32h-.77c.23-.75.44-1.53.65-2.33s.46-1.6.74-2.41l2.81-7.77h2.3l-5.75,15.06c-.27.7-.58,1.27-.96,1.72s-.8.78-1.29,1-1.05.32-1.66.32c-.38,0-.7-.03-.97-.08s-.46-.11-.57-.16Z"/>
<path class="cls-4" d="M926.73,379.1c-1.19,0-2.23-.28-3.12-.84s-1.59-1.35-2.09-2.37-.75-2.2-.75-3.54.25-2.56.75-3.58,1.2-1.81,2.09-2.37,1.94-.84,3.12-.84c.63,0,1.23.08,1.79.25s1.08.4,1.54.71.87.69,1.2,1.14.59.96.77,1.53l-2.03.57c-.09-.34-.24-.64-.43-.93s-.43-.53-.71-.73-.6-.37-.96-.48-.75-.17-1.18-.17c-.88,0-1.59.23-2.16.69s-.98,1.06-1.25,1.8-.41,1.55-.41,2.43.14,1.66.41,2.4.69,1.33,1.25,1.79,1.28.68,2.16.68c.44,0,.84-.06,1.21-.18s.69-.28.98-.5.53-.47.72-.77.34-.62.43-.97l2.02.57c-.17.59-.43,1.11-.77,1.56s-.74.85-1.21,1.17-.99.57-1.56.74-1.18.25-1.82.25Z"/>
<path class="cls-4" d="M939.55,378.98c-.88,0-1.66-.18-2.33-.54s-1.19-.91-1.56-1.65-.56-1.65-.56-2.75v-8.32h2.11v8.14c0,1.01.27,1.8.8,2.37s1.25.86,2.17.86c.62,0,1.19-.14,1.68-.41s.89-.67,1.18-1.2.43-1.16.43-1.9v-7.85h2.12v13.1h-2.04v-3.18h.28c-.41,1.23-.98,2.09-1.73,2.59s-1.6.75-2.56.75Z"/>
<path class="cls-4" d="M949.3,378.82v-13.1h2.04v2.02h.14c.24-.66.67-1.2,1.29-1.6s1.32-.6,2.1-.6c.16,0,.34,0,.56.01s.38.02.51.02v2.12c-.06-.02-.22-.04-.47-.07s-.52-.05-.8-.05c-.62,0-1.19.13-1.68.39s-.88.62-1.17,1.08-.42.98-.42,1.58v8.2h-2.11Z"/>
<path class="cls-4" d="M959.64,363.57c-.4,0-.74-.14-1.03-.41s-.43-.6-.43-.98.14-.72.43-.99.63-.4,1.03-.4.75.13,1.04.4.43.6.43.99-.14.71-.43.98-.64.41-1.04.41ZM958.57,378.82v-13.1h2.11v13.1h-2.11Z"/>
<path class="cls-4" d="M969.72,379.1c-1.19,0-2.23-.28-3.12-.84s-1.59-1.35-2.09-2.37-.75-2.2-.75-3.54.25-2.56.75-3.58,1.2-1.81,2.09-2.37,1.94-.84,3.12-.84,2.24.28,3.14.84,1.6,1.35,2.1,2.37.75,2.21.75,3.58-.25,2.52-.75,3.54-1.2,1.8-2.1,2.37-1.95.84-3.14.84ZM969.72,377.21c.88,0,1.61-.23,2.18-.68s.99-1.05,1.27-1.79.41-1.54.41-2.4-.14-1.67-.41-2.42-.7-1.35-1.27-1.8-1.3-.69-2.18-.69-1.59.23-2.16.69-.98,1.06-1.25,1.8-.41,1.55-.41,2.43.14,1.66.41,2.4.69,1.33,1.25,1.79,1.28.68,2.16.68Z"/>
<path class="cls-4" d="M983.23,379.1c-.9,0-1.7-.13-2.4-.39s-1.28-.65-1.73-1.17-.76-1.15-.91-1.9l2-.48c.19.72.54,1.25,1.06,1.58s1.17.5,1.95.5c.91,0,1.64-.2,2.19-.59s.81-.86.81-1.39c0-.45-.16-.83-.47-1.13s-.8-.53-1.45-.67l-2.18-.52c-1.19-.28-2.07-.72-2.65-1.31s-.87-1.35-.87-2.27c0-.75.21-1.41.63-1.99s1-1.02,1.73-1.35,1.56-.49,2.49-.49,1.66.13,2.29.4,1.14.64,1.54,1.11.69,1.02.88,1.63l-1.91.49c-.18-.47-.48-.89-.9-1.28s-1.05-.57-1.89-.57c-.78,0-1.43.18-1.95.54s-.78.81-.78,1.36c0,.48.18.87.53,1.17s.91.53,1.68.71l1.98.47c1.19.28,2.07.72,2.64,1.31s.86,1.33.86,2.23c0,.77-.22,1.45-.65,2.04s-1.04,1.07-1.82,1.41-1.68.52-2.7.52Z"/>
<path class="cls-4" d="M992.51,363.57c-.4,0-.74-.14-1.03-.41s-.43-.6-.43-.98.14-.72.43-.99.63-.4,1.03-.4.75.13,1.04.4.43.6.43.99-.14.71-.43.98-.64.41-1.04.41ZM991.44,378.82v-13.1h2.11v13.1h-2.11Z"/>
<path class="cls-4" d="M1002.41,365.72v1.8h-6.77v-1.8h6.77ZM997.61,362.6h2.11v12.82c0,.59.12,1.01.36,1.28s.63.4,1.19.4c.13,0,.3-.02.51-.05s.4-.06.57-.09l.43,1.78c-.22.08-.47.14-.74.18s-.55.06-.83.06c-1.13,0-2.02-.3-2.65-.9s-.95-1.43-.95-2.5v-12.98Z"/>
<path class="cls-4" d="M1005.36,383.57l.54-1.83.22.06c.43.11.82.14,1.17.09s.67-.22.95-.52.53-.76.74-1.38l.38-1.09-5-13.17h2.31l2.79,7.77c.3.81.55,1.62.75,2.41s.42,1.57.64,2.32h-.77c.23-.75.44-1.53.65-2.33s.46-1.6.74-2.41l2.81-7.77h2.3l-5.75,15.06c-.27.7-.58,1.27-.96,1.72s-.8.78-1.29,1-1.05.32-1.66.32c-.38,0-.7-.03-.97-.08s-.46-.11-.57-.16Z"/>
<path class="cls-4" d="M1019.61,378.97c-.44,0-.81-.15-1.12-.46s-.46-.68-.46-1.12.15-.81.46-1.12.68-.46,1.12-.46.81.15,1.12.46.46.68.46,1.12-.15.81-.46,1.12-.68.46-1.12.46Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

524
public/base.html Normal file
View File

@@ -0,0 +1,524 @@
<!DOCTYPE html>
<html lang="ja-JP">
<head>
<!--
$ curl https://nercone.dev/welcome/
■ ■ ■■■■■ ■■■■ ■■■■ ■■■ ■ ■ ■■■■■
■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■
■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■
■ ■ ■ ■■■■ ■■■■ ■ ■ ■ ■ ■ ■ ■■■■
■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■
■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■
■ ■ ■■■■■ ■ ■ ■■■■ ■■■ ■ ■ ■■■■■
nercone.dev ({{ server_version }})
welcome to nercone.dev!
-->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}nercone's website{% endblock %}</title>
<meta name="description" content="{% block description %}No description.{% endblock %}">
<!-- for Crawlers -->
<link rel="canonical" href="https://nercone.dev/">
<meta name="robots" content="index, follow">
<meta name="googlebot" content="index, follow">
<meta name="author" content="Nercone a.k.a. DiamondGotCat">
<meta name="theme-color" content="#A9A9A9">
<!-- OGP (Open Graph protocol) -->
<meta property="og:site_name" content="nercone's website">
<meta property="og:title" content="{{ self.title() }}">
<meta property="og:description" content="{{ self.description() }}">
<meta property="og:image" content="https://nercone.dev/assets/images/nercone.png">
<meta property="og:url" content="https://nercone.dev/">
<meta property="og:type" content="website">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ self.title() }}">
<meta name="twitter:description" content="{{ self.description() }}">
<meta name="twitter:image" content="https://nercone.dev/assets/images/nercone.png">
<!-- Scripts / Stylesheets etc. -->
<link rel="manifest" href="/site.webmanifest">
<link rel="apple-touch-icon" href="/assets/images/favicon_cat.png" sizes="1200x1200">
<link rel="icon" type="image/png" href="/assets/images/favicon_cat.png" sizes="1200x1200">
<link rel="icon" type="image/svg+xml" href="/assets/images/favicon_cat.svg" sizes="1200x1200">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Noto+Sans+JP:wght@100..900&family=Noto+Sans+TC:wght@100..900&family=Noto+Sans+SC:wght@100..900&family=Noto+Sans+KR:wght@100..900&display=swap');
@font-face {
font-family: "MesloLGS Nerd Font";
font-style: normal;
font-weight: 400;
src:
local("MesloLGS Nerd Font"),
url("https://nercone.dev/assets/fonts/MesloLGSNerdFont-Regular.woff2") format("woff2")
}
@font-face {
font-family: "MesloLGS Nerd Font";
font-style: normal;
font-weight: 700;
src:
local("MesloLGS Nerd Font"),
url("https://nercone.dev/assets/fonts/MesloLGSNerdFont-Bold.woff2") format("woff2")
}
@font-face {
font-family: "MesloLGS Nerd Font";
font-style: italic;
font-weight: 400;
src:
local("MesloLGS Nerd Font"),
url("https://nercone.dev/assets/fonts/MesloLGSNerdFont-Italic.woff2") format("woff2")
}
@font-face {
font-family: "MesloLGS Nerd Font";
font-style: italic;
font-weight: 700;
src:
local("MesloLGS Nerd Font"),
url("https://nercone.dev/assets/fonts/MesloLGSNerdFont-BoldItalic.woff2") format("woff2")
}
html {
color: #A9A9A9;
background-color: #202020;
margin: 0;
padding: 0;
padding-top: 0;
padding-bottom: 0;
font-family: "Inter", "Noto Sans JP", "Noto Sans TC", "Noto Sans SC", sans-serif, monospace;
font-size: 12pt;
font-optical-sizing: auto;
font-weight: 200;
font-style: normal;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
view-transition-name: header-content;
background-color: #202020C0;
backdrop-filter: blur(5px);
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 24px;
display: flex;
align-items: center;
gap: 16px;
position: fixed;
z-index: 10;
}
main {
view-transition-name: main-content;
padding: 24px;
padding-top: 60px;
padding-bottom: 0px;
flex: 1;
}
footer {
view-transition-name: footer-content;
padding: 24px;
padding-top: 0px;
display: flex;
align-items: center;
gap: 16px;
z-index: 10;
}
section {
padding-top: 4px;
padding-bottom: 4px;
}
a {
text-decoration: underline;
color: inherit;
}
h1 {
font-weight: 400;
line-height: 8pt;
}
h2 {
font-weight: 300;
}
h3, h4, h5, h6 {
font-weight: 200;
}
section h2, section h3 {
line-height: 8pt;
}
h1, h2, h3, h4, h5, h6 {
color: #C8C8C8;
}
.block {
background-color: #303030;
border-radius: 6px;
padding-top: 4px;
padding-left: 24px;
padding-right: 24px;
padding-bottom: 4px;
margin-bottom: 16px;
}
.block pre {
margin-bottom: 20px;
}
pre {
border-radius: 4px;
background-color: #262626;
padding: 8px;
padding-left: 16px;
overflow-x: auto;
white-space: pre;
line-height: 24px;
font-size: small;
font-family: "MesloLGS Nerd Font", "Noto Sans JP", "Noto Sans TC", "Noto Sans SC", monospace, sans-serif;
font-optical-sizing: auto;
font-style: normal;
}
.code {
padding: 4px;
font-family: "MesloLGS Nerd Font", "Noto Sans JP", "Noto Sans TC", "Noto Sans SC", monospace, sans-serif;
font-optical-sizing: auto;
font-style: normal;
}
.banner {
height: 50px;
width: auto;
border-radius: 6px;
}
.flex {
display: flex;
align-items: center;
gap: 16px;
}
.flex > * {
margin: 0;
}
.flex-1 {
flex: 1;
}
.flex-vertical {
flex-direction: column;
}
.text-small {
font-size: small;
}
.text-bold {
font-weight: 400;
}
.text-no-decoration {
text-decoration: none;
}
.text-underline {
text-decoration: underline;
}
.text-highlight {
color: #C8C8C8;
}
.text-lowlight {
color: #818181;
}
.text-tc {
font-family: "Noto Sans TC";
}
.text-sc {
font-family: "Noto Sans SC";
}
.text-kr {
font-family: "Noto Sans KR";
}
.narrow-height {
line-height: 0;
}
.hide {
display: none;
}
@media (min-width: 780px) {
.hide.show-on-medium {
display: block;
}
}
@media (min-width: 1200px) {
.hide.show-on-large {
display: block;
}
}
@keyframes vt-fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes vt-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes vt-blur-out {
from { filter: blur(0px); }
to { filter: blur(6px); }
}
@keyframes vt-blur-in {
from { filter: blur(6px); }
to { filter: blur(0px); }
}
@media (prefers-reduced-motion: reduce) {
::view-transition-old(header-content),
::view-transition-new(header-content) {
z-index: 1;
pointer-events: none;
}
}
@media (prefers-reduced-motion: reduce) {
::view-transition-old(main-content),
::view-transition-new(main-content) {
z-index: 1;
pointer-events: none;
}
}
@media (prefers-reduced-motion: reduce) {
::view-transition-old(footer-content),
::view-transition-new(footer-content) {
z-index: 1;
pointer-events: none;
}
}
* { cursor: none; }
#cursor {
view-transition-name: none;
position: fixed;
z-index: 99999;
width: 25px;
height: 25px;
border-radius: 50%;
background: #C8C8C8C0;
pointer-events: none;
transform: translate(-50%, -50%);
opacity: 0;
transition:
left 0.08s ease-out,
top 0.08s ease-out,
width 0.15s cubic-bezier(0.22, 1, 0.36, 1),
height 0.15s cubic-bezier(0.22, 1, 0.36, 1),
border-radius 0.15s cubic-bezier(0.22, 1, 0.36, 1),
background 0.15s ease,
transform 0.08s ease-out,
opacity 0.3s ease;
}
#cursor.visible {
opacity: 1;
}
#cursor.on-text {
width: 3px;
border-radius: 1.5px;
background: #C8C8C8C0;
}
#cursor.on-link {
border-radius: 6px;
background: #C8C8C840;
transform: translate(0, 0);
}
</style>
<script>
(() => {
const textSelectors = 'p, h1, h2, h3, h4, h5, h6, span, li, label, td, th, pre, .code';
const linkSelectors = 'a, button, [role="button"], input[type="submit"], input[type="button"]';
const padding = 6;
let mouseX = 0, mouseY = 0;
let currentLinkEl = null;
let rafId = null;
let cursor = null;
let cursorVisible = false;
let lastTouchTime = 0;
let isMouseDown = false;
const TOUCH_MOUSE_GUARD_MS = 800;
function showCursor() {
if (!cursorVisible && cursor) {
cursorVisible = true;
cursor.classList.add('visible');
}
}
function hideCursor() {
if (cursor) {
cursorVisible = false;
cursor.classList.remove('visible');
currentLinkEl = null;
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
cursor.classList.remove('on-link', 'on-text');
}
}
function isSyntheticMouse() {
return Date.now() - lastTouchTime < TOUCH_MOUSE_GUARD_MS;
}
function updateCursorForLink(el) {
const rect = el.getBoundingClientRect();
cursor.classList.remove('on-text');
cursor.classList.add('on-link');
cursor.style.transform = 'none';
cursor.style.left = (rect.left - padding) + 'px';
cursor.style.top = (rect.top - padding) + 'px';
cursor.style.width = (rect.width + padding * 2) + 'px';
cursor.style.height = (rect.height + padding * 2) + 'px';
}
function trackLink() {
if (currentLinkEl) {
updateCursorForLink(currentLinkEl);
rafId = requestAnimationFrame(trackLink);
}
}
document.addEventListener('DOMContentLoaded', () => {
cursor = document.getElementById('cursor');
if (!cursor) return;
document.addEventListener('touchstart', () => { lastTouchTime = Date.now(); hideCursor(); }, { passive: true });
document.addEventListener('touchmove', () => { lastTouchTime = Date.now(); hideCursor(); }, { passive: true });
document.addEventListener('touchend', () => { lastTouchTime = Date.now(); }, { passive: true });
document.addEventListener('mousemove', (e) => {
if (isSyntheticMouse()) return;
if (e.sourceCapabilities && e.sourceCapabilities.firesTouchEvents) return;
mouseX = e.clientX;
mouseY = e.clientY;
showCursor();
const el = document.elementFromPoint(mouseX, mouseY);
const linkEl = el ? el.closest(linkSelectors) : null;
if (linkEl) {
if (currentLinkEl !== linkEl) {
currentLinkEl = linkEl;
if (rafId) cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(trackLink);
}
} else {
if (currentLinkEl) {
currentLinkEl = null;
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
}
cursor.classList.remove('on-link');
cursor.style.transform = isMouseDown ? 'translate(-50%, -50%) scale(0.9)' : 'translate(-50%, -50%)';
cursor.style.left = mouseX + 'px';
cursor.style.top = mouseY + 'px';
cursor.style.width = '';
cursor.style.height = '';
if (el && el.closest(textSelectors)) {
cursor.classList.add('on-text');
} else {
cursor.classList.remove('on-text');
}
}
});
document.addEventListener('mousedown', () => {
isMouseDown = true;
cursor.style.transform = currentLinkEl ? 'none' : 'translate(-50%, -50%) scale(0.9)';
});
document.addEventListener('mouseup', () => {
isMouseDown = false;
cursor.style.transform = currentLinkEl ? 'none' : 'translate(-50%, -50%) scale(1)';
});
window.addEventListener('scroll', () => {
if (currentLinkEl) updateCursorForLink(currentLinkEl);
}, { passive: true });
});
if (document.startViewTransition) {
document.addEventListener("click", (event) => {
const link = event.target.closest("a");
if (!link || link.hasAttribute('download')) return;
const url = new URL(link.href, location.href);
if (url.origin !== location.origin) return;
if (link.target || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
if (url.hash && url.pathname === location.pathname) {
event.preventDefault();
const target = document.querySelector(url.hash);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
history.pushState(null, '', url.hash);
}
return;
}
event.preventDefault();
document.startViewTransition(async () => {
const response = await fetch(url.href, {
headers: { "X-Requested-With": "view-transition" }
});
const html = await response.text();
const doc = new DOMParser().parseFromString(html, "text/html");
for (const tag of ['head', 'header', 'main', 'footer']) {
const newEl = doc.querySelector(tag);
const curEl = document.querySelector(tag);
if (!newEl || !curEl) { location.href = url.href; return; }
curEl.innerHTML = newEl.innerHTML;
}
history.pushState(null, "", url.href);
currentLinkEl = null;
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
cursor.classList.remove('on-link', 'on-text');
cursor.style.transform = 'translate(-50%, -50%)';
cursor.style.left = mouseX + 'px';
cursor.style.top = mouseY + 'px';
cursor.style.width = '';
cursor.style.height = '';
const el = document.elementFromPoint(mouseX, mouseY);
const newLinkEl = el ? el.closest(linkSelectors) : null;
if (newLinkEl) {
currentLinkEl = newLinkEl;
rafId = requestAnimationFrame(trackLink);
} else if (el && el.closest(textSelectors)) {
cursor.classList.add('on-text');
}
});
});
window.addEventListener("popstate", () => location.reload());
}
})();
</script>
{% block extra_head %}{% endblock %}
</head>
<body>
<div id="cursor"></div>
<header class="flex">
<div class="flex-1 flex">
<a href="/" class="text-no-decoration text-highlight" style="gap: 8px;"><span class="text-bold">Nercone</span> {% block extra_title %}{% endblock %}</a>
<p class="hide show-on-medium">{% block header_desc %}<a href="/daily-quote/" class="text-no-decoration">{{ get_daily_quote() }}</a>{% endblock %}</p>
</div>
<div class="flex">
<a href="/access-counter/" class="text-no-decoration hide show-on-medium">あなたは{{ get_access_count() }}番目の訪問者です。</a>
</div>
</header>
<main>{% block content %}{% endblock %}</main>
<footer class="flex text-lowlight">
<div class="flex-1 flex">
<a href="/" class="text-no-decoration"><span class="text-bold">nercone</span>.dev</a>
<a href="/about/" class="text-no-decoration">about</a>
<a href="/links/" class="text-no-decoration">links</a>
<a href="/projects/" class="text-no-decoration">projects</a>
<a href="mailto:nercone@nercone.dev" class="text-no-decoration hide show-on-medium">email</a>
<a href="{{ onion_site_url }}" class="text-no-decoration hide show-on-medium">onion</a>
<a href="/public-key/" class="text-no-decoration hide show-on-medium">public-key</a>
<a href="/vulnerability-reporters/" class="text-no-decoration hide show-on-medium">vulnerability-reporters</a>
</div>
<div class="flex">
<p class="text-no-decoration hide show-on-medium">当サイトはリンクフリーです</p>
<a href="/server-version/" class="text-no-decoration" id="footer-version-text">{{ server_version }}</a>
</div>
</footer>
</body>
</html>

26
public/daily-quote.html Normal file
View File

@@ -0,0 +1,26 @@
{% extends "/base.html" %}
{% block title %}今日の迷言 - Nercone{% endblock %}
{% block extra_title %}Daily Quote{% endblock %}
{% block header_desc %}日替わり迷言集{% endblock %}
{% block extra_head %}
<style>
main {
display: grid;
grid-template-rows: auto 1fr auto;
}
.big-text {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 32pt;
}
</style>
{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<p class="text-highlight big-text">{{ get_daily_quote() }}</p>
<div class="block">
<p>日替わり迷言集です。毎日0:00 UTCに更新されます。</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends "/base.html" %}
{% block title %}Download / Use Banner - Nercone{% endblock %}
{% block extra_title %}Links{% endblock %}
{% block header_desc %}バナー画像は<a href="https://creativecommons.org/licenses/by/4.0/deed.ja">CC BY 4.0</a>の元で自由に使用できます。{% endblock %}
{% block description %}Nerconeのバナー画像をダウンロード{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/links/" class="text-no-decoration">← Links</a></p>
<h3 class="text-bold">Normal</h3>
<p><a href="/assets/images/banner.ai" class="text-no-decoration" download>adobe-illustrator</a> / <a href="/assets/images/banner.svg" class="text-no-decoration" download>svg</a> / <a href="/assets/images/banner.png" class="text-no-decoration" download>png</a> / <a href="/assets/images/banner.webp" class="text-no-decoration" download>webp</a></p>
<pre>&lt;a href=&quot;https://nercone.dev/&quot;&gt;&lt;img src=&quot;https://assets.nercone.dev/images/banner.svg&quot;&gt;&lt;/a&gt; &lt;!-- SVG --&gt;<br>&lt;a href=&quot;https://nercone.dev/&quot;&gt;&lt;img src=&quot;https://assets.nercone.dev/images/banner.png&quot;&gt;&lt;/a&gt; &lt;!-- PNG --&gt;<br>&lt;a href=&quot;https://nercone.dev/&quot;&gt;&lt;img src=&quot;https://assets.nercone.dev/images/banner.webp&quot;&gt;&lt;/a&gt; &lt;!-- WEBP --&gt;</pre>
<h3 class="text-bold">Cat Version</h3>
<p>猫バージョンです。dotpictを使って描いたやつをIllustratorで加工してます。個人的にはこっちのほうが好き。</p>
<p><a href="/assets/images/banner_cat.ai" class="text-no-decoration" download>adobe-illustrator</a> / <a href="/assets/images/banner_cat.svg" class="text-no-decoration" download>svg</a> / <a href="/assets/images/banner_cat.png" class="text-no-decoration" download>png</a> / <a href="/assets/images/banner_cat.webp" class="text-no-decoration" download>webp</a></p>
<pre>&lt;a href=&quot;https://nercone.dev/&quot;&gt;&lt;img src=&quot;https://assets.nercone.dev/images/banner_cat.svg&quot;&gt;&lt;/a&gt; &lt;!-- SVG --&gt;<br>&lt;a href=&quot;https://nercone.dev/&quot;&gt;&lt;img src=&quot;https://assets.nercone.dev/images/banner_cat.png&quot;&gt;&lt;/a&gt; &lt;!-- PNG --&gt;<br>&lt;a href=&quot;https://nercone.dev/&quot;&gt;&lt;img src=&quot;https://assets.nercone.dev/images/banner_cat.webp&quot;&gt;&lt;/a&gt; &lt;!-- WEBP --&gt;</pre>
{% endblock %}

8
public/error.html Normal file
View File

@@ -0,0 +1,8 @@
{% extends "/base.html" %}
{% block title %}{{ status_code }} {{ status_code_name }} - Nercone{% endblock %}
{% block extra_title %}Error{% endblock %}
{% block header_desc %}{{ status_code }} {{ status_code_name }}{% endblock %}
{% block content %}
<h1 class="text-bold">{{ status_code }} {{ status_code_name }}</h1>
<p>{{ message }}</p>
{% endblock %}

38
public/index.html Normal file
View File

@@ -0,0 +1,38 @@
{% extends "/base.html" %}
{% block title %}nercone's website{% endblock %}
{% block description %}作りたいものを作り、やりたくないことからは逃げる学生{% endblock %}
{% block extra_head %}
<style>
main {
flex: 1;
display: flex;
}
.aaaa {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.big-text {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 64pt;
font-weight: 600;
}
@media (max-width: 460px) {
.big-text {
font-size: 54pt;
}
}
</style>
{% endblock %}
{% block content %}
<div class="aaaa">
<div class="flex-vertical">
<p class="text-highlight big-text" style="padding-bottom: 0px; margin-bottom: 0px; text-box: trim-both cap alphabetic;">NERCONE</p>
<p class="text-bold" style="padding-top: 0px; margin-top: 0px; text-box: trim-both cap alphabetic;">ようこそ・Welcome・<span class="text-sc">欢迎</span><span class="text-tc">歡迎</span><span class="text-kr">환영해요</span></p>
</div>
</div>
{% endblock %}

38
public/links.html Normal file
View File

@@ -0,0 +1,38 @@
{% extends "/base.html" %}
{% block title %}Links - Nercone{% endblock %}
{% block extra_title %}Links{% endblock %}
{% block header_desc %}相互リンク大歓迎{% endblock %}
{% block description %}Nerconeのリンク集{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<div id="nercone" class="block">
<h3>nercone</h3>
<img src="/assets/images/banner_cat.png" alt="banner-nercone" class="banner">
<p>バナーは<a href="/download-banner/">こ↑こ↓</a>からダウンロードできます</p>
<p><a href="https://nercone.dev/" class="text-no-decoration">website</a> / <a href="https://github.com/nercone-dev/" class="text-no-decoration">github</a> / <a href="https://orcid.org/0009-0001-3615-0962/" class="text-no-decoration">orcid</a> / <a href="https://twitter.com/nercone_x/" class="text-no-decoration">twitter</a> / <a href="https://bsky.app/profile/nercone.dev/" class="text-no-decoration">bluesky</a> / <a href="https://qiita.com/DiamondGotCat/" class="text-no-decoration">qiita</a> / <a href="https://zenn.dev/techcat56/" class="text-no-decoration">zenn</a></p>
<!--
<pre>
class Nercone(Identity):
@property
def loves(self):
return t3tra
</pre>
-->
</div>
<div id="t3tra" class="block">
<h3>t3tra<!----></h3>
<a href="https://t3tra.dev/" target="_blank" rel="noopener noreferrer"><img src="https://t3tra.dev/images/banner.png" alt="banner-t3tra" class="banner"></a>
<p>技術とか才能が凄すぎる人<!--で僕が好きな人--> <!-- 好きな人にリンク貼ってもらえたうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしいうれしい好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き好き --></p>
<p><a href="https://t3tra.dev/" class="text-no-decoration">website</a> / <a href="https://github.com/t3tra-dev/" class="text-no-decoration">github</a> / <a href="https://orcid.org/0009-0006-9423-1572/" class="text-no-decoration">orcid</a> / <a href="https://twitter.com/t3tra_x/" class="text-no-decoration">twitter</a> / <a href="https://bsky.app/profile/t3tra.dev/" class="text-no-decoration">bluesky</a> / <a href="https://qiita.com/t3tra/" class="text-no-decoration">qiita</a> / <a href="https://zenn.dev/t3tra/" class="text-no-decoration">zenn</a></p>
<!-- tip: Nerconeはてとらさんのことが好きすぎるためライバルは積極的に潰そうとします。 -->
<!-- tip of tip: ただし想像以上に強いライバルが現れると勝てないとわかり悲しくなって1日ぐらい現実世界からログアウトします。 -->
<!-- tip of tip of tip: なのでNerconeには優しくしてください -->
<!-- tip of tip of tip of tip: 言い忘れてたけどてとらさんは僕のものなので手を出すな -->
<!-- tip of tip of tip of tip of tip: そういえばてとらさんってヤンデレだったりするのかな (妄想) -->
<!-- tip of tip of tip of tip of tip of tip: 前に確かメンヘラだとかなんとか言ってた気がするからもしかしたら... -->
<!-- tip of tip of tip of tip of tip of tip of tip: 流石にないか... -->
<!-- tip of tip of tip of tip of tip of tip of tip of tip: そういえば最近てとらさんがウェブサイトのコード書き直してFastAPI + Jinja2にしてた。まさか... -->
<!-- tip of tip of tip of tip of tip of tip of tip of tip of tip: いや待て落ち着けまだそうと決まったわけじゃ...でもそうだったらいいな -->
</div>
<p class="text-lowlight">追加は<a href="#nercone">SNSまたはメール</a>でご連絡ください。</p>
{% endblock %}

38
public/projects.html Normal file
View File

@@ -0,0 +1,38 @@
{% extends "/base.html" %}
{% block title %}Projects - Nercone{% endblock %}
{% block extra_title %}Projects{% endblock %}
{% block header_desc %}主なプロジェクトたち{% endblock %}
{% block description %}Nerconeの主なプロジェクトたち{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<div id="os" class="block">
<h3>NerconeOS</h3>
<p>自作OS (とりあえず計画段階)</p>
<p><a href="https://github.com/nercone-dev/nerconeos/" target="_blank" rel="noopener noreferrer" class="text-highlight">GitHub</a></p>
</div>
<div id="fastget" class="block">
<h3>FastGet</h3>
<p>高速ファイルダウンロードツール</p>
<p><a href="https://github.com/nercone-dev/nercone-fastget/" target="_blank" rel="noopener noreferrer" class="text-highlight">GitHub</a> <a href="https://pypi.org/project/nercone-fastget/" class="text-highlight">PyPI</a></p>
</div>
<div id="nersh" class="block">
<h3>SpeedClone</h3>
<p>ちょっと速い気がしなくもないgitのクローンツール</p>
<p><a href="https://github.com/nercone-dev/SpeedClone/" target="_blank" rel="noopener noreferrer" class="text-highlight">GitHub</a></p>
</div>
<div id="gatepass" class="block">
<h3>GatePass</h3>
<p>macOS向け実行ファイル隔離解除ツール</p>
<p><a href="https://github.com/nercone-dev/GatePass/" target="_blank" rel="noopener noreferrer" class="text-highlight">GitHub</a></p>
</div>
<div id="ypsh" class="block">
<h3>YPSH Language</h3>
<p>自作プログラミング言語</p>
<p><a href="https://github.com/nercone-dev/ypsh/" target="_blank" rel="noopener noreferrer" class="text-highlight">GitHub</a></p>
</div>
<div id="zetallm" class="block">
<h3>Zeta LLM</h3>
<p>自作LLM</p>
<p><a href="https://github.com/nercone-dev/zeta-llm/" target="_blank" rel="noopener noreferrer" class="text-highlight">GitHub</a></p>
</div>
{% endblock %}

14
public/public-key.html Normal file
View File

@@ -0,0 +1,14 @@
{% extends "/base.html" %}
{% block title %}PGP公開鍵 - Nercone{% endblock %}
{% block extra_title %}Public Key{% endblock %}
{% block header_desc %}PGP公開鍵{% endblock %}
{% block description %}NerconeのPGP公開鍵とそのフィンガープリント{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<h3><a href="mailto:nercone@nercone.dev" class="text-no-decoration">Nercone &lt;nercone&#64;nercone.dev&gt;</a></h3>
<pre>-----BEGIN PGP PUBLIC KEY BLOCK-----<br><br>mDMEaZNZjBYJKwYBBAHaRw8BAQdAUSCDOhESmGQBqA9BXpmmB6tYAvzwPt+5pEjJ<br>UDl9rEO0Nk5lcmNvbmUgKGZvcm1lcmx5IERpYW1vbmRHb3RDYXQpIDxuZXJjb25l<br>QG5lcmNvbmUuZGV2PoiTBBMWCgA7FiEEc5Q3UzpWjrjbmTU3UFzPN/mj6OkFAmmT<br>WYwCGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQUFzPN/mj6OkjVgD/<br>ZSTmQCQ0R4JShXU74Onftlm0wYqj6+8E9C13OVC/Rx8A/iQIvmXZe9zfEslmFS7V<br>oWFegRuDoqLACL7BZUx/2r0MuDgEaZNZjBIKKwYBBAGXVQEFAQEHQPAgLyhydOum<br>8vtTBa/46VH5B29v0DnNEUCmHYnHn3EMAwEIB4h4BBgWCgAgFiEEc5Q3UzpWjrjb<br>mTU3UFzPN/mj6OkFAmmTWYwCGwwACgkQUFzPN/mj6OmgXQD/Umyya1slJclVhI/+<br>IHmvak+E6LMRcnr6r6ybY57iEK4BAOA/CMuOE718KYm0yqVSV8lK5ym1G/1BDwml<br>ZjQEF4sK<br>=qeqQ<br>-----END PGP PUBLIC KEY BLOCK-----</pre>
<pre>7394 3753 3A56 8EB8 DB99 3537 505C CF37 F9A3 E8E9</pre>
<h3><a href="mailto:nenaicone@nercone.dev" class="text-no-decoration">Nenaicone &lt;nenaicone&#64;nercone.dev&gt;</a></h3>
<pre>-----BEGIN PGP PUBLIC KEY BLOCK-----<br><br>mDMEaZQeWxYJKwYBBAHaRw8BAQdALmr++RYnSeciGId1NrX7p98szIfBEbv/nRCl<br>lb9+UhK0O05lbmFpY29uZSAoZm9ybWVybHkgRGlhbW9uZEdvdENhdDIpIDxuZW5h<br>aWNvbmVAbmVyY29uZS5kZXY+iJMEExYKADsWIQQS6Kz4FKR/rWdwzS0/G6dMBRxh<br>NwUCaZQeWwIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA/G6dMBRxh<br>N1/PAP0T/zNoMug90X6wJn3UDsEAndWFsLYn0RRHdE2IFujL6QD+Iq2qnTF4ljjd<br>3eWv9x5T/UEoGo+0texh1DUI4VJriQm4OARplB5bEgorBgEEAZdVAQUBAQdAKH7y<br>MF16gPhz61eQfWkLELKWeDO9uzYoFqFEDw+5ux4DAQgHiHgEGBYKACAWIQQS6Kz4<br>FKR/rWdwzS0/G6dMBRxhNwUCaZQeWwIbDAAKCRA/G6dMBRxhNxaNAQDoZQw1S7nd<br>hjBtgQtmAoGwGjD3NBha/h1c2Pw9E5APsAD/WeFwkawAsBfW8nYIIx2HIDhTgpBY<br>5mu22bFyMHzKNQo=<br>=0sUi<br>-----END PGP PUBLIC KEY BLOCK-----</pre>
<pre>12E8 ACF8 14A4 7FAD 6770 CD2D 3F1B A74C 051C 6137</pre>
{% endblock %}

16
public/quotes.txt Normal file
View File

@@ -0,0 +1,16 @@
MarkItDownのネーミングセンス良いよね
MicrosoftはGitHubだけやってればいい
Windowsはexeファイルを実行するためのOS
LinuxディストリビューションはFedoraが最強
Lythonのコード全部理解できるようになりたい
Cloudflare神
Tailscale最高
#zshをすこれ
FiRSTSTEP (NeXTSTEP)
nerCone (arXiv)
cURLじいさんの空飛ぶ$HOME
pwd && whoami (ここはどこ私は誰)
TeXのバージョン番号の付け方面白い
Pithon 3.1415926535897932384626433
192.168の168(彩葉)の部分
猫みたいに自由気ままに生きてみたい...

34
public/robots.txt Normal file
View File

@@ -0,0 +1,34 @@
# ╭──────────────────────────────────────────────╮
# │ ╭──────╮ │
# │ │ │ I'm a robot │
# │ │ │ antiCAPTCHA │
# │ ╰──────╯ Privacy | Terms │
# ╰──────────────────────────────────────────────╯
# Note: "I'm a robot" Box Inspired by https://fxtwitter.com/robots.txt
# If want to delete this, Please send deletion request to: nercone@diamondgotcat.net
# I delete this text if on received deletion request from FxEmbed.
# I don't check my emails often so my response may be delayed. Sorry.
User-Agent: *
Content-Signal: search=yes, ai-input=yes, ai-train=no
Allow: /
User-Agent: BingBot
Disallow: /
User-Agent: AdIdxBot
Disallow: /
User-Agent: BingPreview
Disallow: /
User-Agent: MicrosoftPreview
Disallow: /
User-Agent: BingVideoPreview
Disallow: /
# ╭──────────────────────────────────────────────╮
# │ Thanks for reading and following robots.txt. │
# │ Have a nice day! │
# ╰──────────────────────────────────────────────╯

View File

@@ -0,0 +1,28 @@
{% extends "/base.html" %}
{% block title %}サーバーのバージョン - Nercone{% endblock %}
{% block extra_title %}Server Version{% endblock %}
{% block header_desc %}サーバーのバージョン{% endblock %}
{% block extra_head %}
<style>
main {
display: grid;
grid-template-rows: auto 1fr auto;
}
.big-text {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 48pt;
}
</style>
{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<p class="text-highlight big-text">{{ server_version }}</p>
<div class="block">
<p>このサーバーで現在使用されているソースコードのコミットID(ハッシュ)の先頭7文字です。</p>
<p>サーバーが起動されるたびに<span class="code">git rev-parse --short HEAD</span>を使用して取得しています。</p>
<p>詳しくは<a href="https://gitea.nercone.dev/nercone-dev/website/">このサーバーのソースコード</a></p>
</div>
{% endblock %}

10
public/shorturls.json Normal file
View File

@@ -0,0 +1,10 @@
{
"email": {"type": "redirect", "content": "mailto:nercone@nercone.dev"},
"github": {"type": "redirect", "content": "https://github.com/nercone-dev/"},
"twitter": {"type": "redirect", "content": "https://twitter.com/nercone_x/"},
"bluesky": {"type": "redirect", "content": "https://bsky.app/profile/nercone.dev/"},
"youtube": {"type": "redirect", "content": "https://youtube.com/@nercone-ch/"},
"gitea": {"type": "redirect", "content": "https://gitea.nercone.dev/nercone-dev/"},
"orcid": {"type": "redirect", "content": "https://orcid.org/0009-0001-3615-0962/"},
"justuuid": {"type": "redirect", "content": "https://justuuid.pages.dev/u/7b70a75e-d72f-4bba-8e55-1c3c9e032678/"}
}

47
public/site.webmanifest Normal file
View File

@@ -0,0 +1,47 @@
{
"name": "nercone.dev",
"description": "作りたいものを作り、やりたくないことからは逃げる学生",
"start_url": "https://nercone.dev/",
"display": "standalone",
"theme_color": "#A9A9A9",
"background_color": "#202020",
"icons": [
{
"src": "/assets/images/favicon_cat.png",
"type": "image/png",
"sizes": "1200x1200"
},
{
"src": "/assets/images/favicon_cat.svg",
"type": "image/svg+xml",
"sizes": "1200x1200"
}
],
"shortcuts": [
{
"name": "About Nercone",
"short_name": "About",
"url": "/about"
},
{
"name": "Links",
"url": "/links"
},
{
"name": "Projects",
"url": "/projects"
},
{
"name": "Public Keys",
"url": "/public-key"
},
{
"name": "Daily Quote",
"url": "/daily-quote"
},
{
"name": "Access Counter",
"url": "/access-counter"
}
]
}

23
public/sitemap.xml Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://nercone.dev/</loc>
<priority>1.0</priority>
</url>
<url>
<loc>https://nercone.dev/about/</loc>
<priority>0.9</priority>
</url>
<url>
<loc>https://nercone.dev/links/</loc>
<priority>0.9</priority>
</url>
<url>
<loc>https://nercone.dev/projects/</loc>
<priority>0.9</priority>
</url>
<url>
<loc>https://nercone.dev/public-key/</loc>
<priority>0.9</priority>
</url>
</urlset>

10
public/to/404.html Normal file
View File

@@ -0,0 +1,10 @@
{% extends "/base.html" %}
{% block title %}404 Not Found - Nercone Redirect{% endblock %}
{% block extra_title %}Redirect{% endblock %}
{% block header_desc %}404 Not Found{% endblock %}
{% block description %}見つからないよ{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← ホーム</a></p>
<h1 class="text-bold">404 Not Found</h1>
<p>見つからないよ</p>
{% endblock %}

View File

@@ -0,0 +1,22 @@
{% extends "/base.html" %}
{% block title %}Thank you for Helping!{% endblock %}
{% block extra_title %}Vulnerability Reporters{% endblock %}
{% block header_desc %}These people found and pointed out security vulnerabilities in my system/service.{% endblock %}
{% block description %}People who found and pointed out security vulnerabilities in my system/service{% endblock %}
{% block content %}
<p class="text-small text-bold"><a href="/" class="text-no-decoration">← Home</a></p>
<h3>Thank you for Helping!</h3>
<p>These people found and pointed out security vulnerabilities in my system/service.</p>
<p>As I'm just a student, I occasionally overlook vulnerabilities while writing code.</p>
<p>These people pointed out the vulnerabilities, prompted me to fix them, and helped me avoid danger.</p>
<!--
<div id="1" class="block">
<h3>Arbitrary Code Execution (UNRESOLVED)</h3>
<p>If Nercone is asked by a specific individual to carry out an arbitrary command in exchange for a reward, and Nercone is in a state where it can execute that command, it will comply with the request.</p>
<p>This vulnerability is unfixable and therefore remains unpatched.</p>
<p class="text-bold">Scope of impact</p>
<p>Nercone's Life</p>
<p class="text-small text-bold">Reported by <a href="https://t3tra.dev/" class="text-no-decoration">t3tra</a>. Thank you very much! (but I cannot fix this issue... Sorry.)</p>
</div>
-->
{% endblock %}

19
pyproject.toml Normal file
View File

@@ -0,0 +1,19 @@
[build-system]
requires = ["uv_build>=0.9.5,<0.10.0"]
build-backend = "uv_build"
[project]
name = "nercone-webserver"
version = "1.0.0"
requires-python = ">=3.8"
dependencies = [
"psutil",
"httpx",
"websockets",
"fastapi",
"jinja2",
"uvicorn[standard]"
]
[project.scripts]
nercone-webserver = "nercone_webserver.__main__:main"

View File

View File

@@ -0,0 +1,35 @@
import uvicorn
from .server import app
def main():
log_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"format": "%(asctime)s %(levelname)s %(name)s: %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
}
},
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": "logs/uvicorn.log",
"formatter": "default"
},
"console": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
"formatter": "default"
}
},
"loggers": {
"uvicorn": {"handlers": ["file", "console"], "level": "INFO", "propagate": False},
"uvicorn.error": {"handlers": ["file", "console"], "level": "INFO", "propagate": False},
"uvicorn.access": {"handlers": ["file", "console"], "level": "INFO", "propagate": False}
}
}
uvicorn.run("nercone_webserver.server:app", host="0.0.0.0", port=8080, workers=1, server_header=False, log_config=log_config)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,63 @@
import sqlite3
from pathlib import Path
class AccessCounter:
def __init__(self, filepath: str = str(Path.cwd().joinpath("databases", "access_counter.db"))):
self.filepath = filepath
def get(self) -> int:
if Path(self.filepath).is_file():
conn = sqlite3.connect(self.filepath)
try:
cur = conn.cursor()
cur.execute("SELECT value FROM access_counter WHERE rowid = 1")
row = cur.fetchone()
if row is None:
conn.execute("""
CREATE TABLE IF NOT EXISTS access_counter (
value INTEGER NOT NULL
)
""")
conn.execute("INSERT OR IGNORE INTO access_counter (rowid, value) VALUES (1, 0)")
conn.commit()
return 0
return row[0]
finally:
conn.close()
else:
conn = sqlite3.connect(self.filepath)
conn.execute("""
CREATE TABLE IF NOT EXISTS access_counter (
value INTEGER NOT NULL
)
""")
conn.execute("INSERT OR IGNORE INTO access_counter (rowid, value) VALUES (1, 0)")
conn.commit()
conn.close()
return 0
def increase(self):
if Path(self.filepath).is_file():
conn = sqlite3.connect(self.filepath)
try:
cur = conn.cursor()
conn.execute("BEGIN IMMEDIATE")
cur.execute(
"UPDATE access_counter SET value = value + 1 WHERE rowid = 1"
)
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
else:
conn = sqlite3.connect(self.filepath)
conn.execute("""
CREATE TABLE IF NOT EXISTS access_counter (
value INTEGER NOT NULL
)
""")
conn.execute("INSERT OR IGNORE INTO access_counter (rowid, value) VALUES (1, 1)")
conn.commit()
conn.close()

View File

@@ -0,0 +1,29 @@
from http import HTTPStatus
from fastapi import Request, Response
from fastapi.templating import Jinja2Templates
default_messages = {
400: "日本語でおk",
401: "見たいのならログインすることね",
402: "夢が欲しけりゃ金払え!",
403: "あんたなんかに見せるもんですか!",
404: "そんなページ知らないっ!",
405: "そのMethodはNot Allowedだよ",
406: "すまんがその条件ではお渡しできない。",
407: "うちのプロキシ使うんだったらまずログインしな。",
408: "もう用がないならさっさと帰りなさい。",
409: "ちょっと待ったそんな話聞いてないぞ",
410: "もう無いで。",
411: "サイズを教えろ。話はそれからだ。",
412: "なにその条件美味しいの",
413: "そ、そそ、そんなの入りきらないよっ!",
414: "もちつけ",
415: "そんな形式知らない!",
416: "そんな大きく...ない...んだ...",
417: "期待させて悪かったわね!",
418: "ティーポット「私はコーヒーを注ぐためのものではありません!やだっ!」"
}
def error_page(templates: Jinja2Templates, request: Request, status_code: int, message: str | None = None) -> Response:
status_code_name = HTTPStatus(status_code).phrase
return templates.TemplateResponse(status_code=status_code, request=request, name="error.html", context={"status_code": status_code, "status_code_name": status_code_name, "message": message or default_messages.get(status_code, "あんのーん")})

View File

@@ -0,0 +1,34 @@
import uuid
import json
from pathlib import Path
from starlette.types import Scope
from datetime import datetime, timezone
access_log_path = Path.cwd().joinpath("logs", "access.log")
access_log_path.parent.mkdir(parents=True, exist_ok=True)
def log_access(scope: Scope, write: bool = True):
client = scope.get("client") or ("", 0)
server = scope.get("server") or ("", 0)
headers = dict(scope.get("headers", []))
hostname = headers.get(b"host", b"").decode().split(":")[0].strip()
log = {
"id": str(uuid.uuid4()),
"timestamp": datetime.now(timezone.utc).isoformat(),
"from": {
"address": client[0],
"port": client[1]
},
"to": {
"scheme": scope.get("scheme", "https"),
"host": hostname,
"port": server[1]
},
"method": scope.get("method", "GET"),
"path": scope.get("path", "/"),
"headers": {k.decode(): v.decode() for k, v in headers.items()}
}
if write:
with access_log_path.open("a", encoding="utf-8") as f:
f.write(json.dumps(log, ensure_ascii=False) + "\n")
return log

View File

@@ -0,0 +1,107 @@
import subprocess
from datetime import datetime, timezone
from fastapi import Response
from fastapi.responses import PlainTextResponse
from starlette.types import Scope, ASGIApp, Receive, Send
from .logger import log_access
server_version = subprocess.run(["/usr/bin/git", "rev-parse", "--short", "HEAD"], text=True, capture_output=True).stdout.strip()
onion_hostname = "4sbb7xhdn4meuesnqvcreewk6sjnvchrsx4lpnxmnjhz2soat74finid.onion"
hostnames = ["localhost", "nercone.dev", "d-g-c.net", "diamondgotcat.net", onion_hostname]
class Middleware:
def __init__(self, app: ASGIApp):
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send):
if scope["type"] not in ("http", "websocket"):
await self.app(scope, receive, send)
return
if scope["type"] == "websocket":
await self.app(scope, receive, send)
return
headers = dict(scope.get("headers", []))
hostname = headers.get(b"host", b"").decode().split(":")[0].strip()
scope["log"] = log_access(scope)
if not any([hostname.endswith(candidate) for candidate in hostnames]):
response = PlainTextResponse("許可されていないホスト名でのアクセスです。", status_code=400)
await self._send_with_headers(response, scope, receive, send)
return
hostname_parts = hostname.split(".")
if hostname_parts[1:] == ["localhost"]:
subdomain = ".".join(hostname_parts[:-1])
else:
subdomain = ".".join(hostname_parts[:-2])
body = await self._read_body(receive)
async def cached_receive():
return {"type": "http.request", "body": body, "more_body": False}
if subdomain not in ["", "www"]:
original_path = scope["path"] if scope["path"].strip() else "/"
subdomain_path = f"/{'/'.join(subdomain.split('.')[::-1])}{original_path}"
response = await self._get_response(scope, cached_receive, subdomain_path)
if response.status_code < 400:
await self._send_with_headers(response, scope, cached_receive, send)
return
response = await self._get_response(scope, cached_receive, original_path)
await self._send_with_headers(response, scope, cached_receive, send)
else:
response = await self._get_response(scope, cached_receive, scope["path"])
await self._send_with_headers(response, scope, cached_receive, send)
async def _get_response(self, scope: Scope, receive: Receive, path: str) -> Response:
new_scope = dict(scope, path=path)
status_code = 200
resp_headers = []
body_parts = []
async def capture_send(message):
nonlocal status_code, resp_headers
if message["type"] == "http.response.start":
status_code = message["status"]
resp_headers = message.get("headers", [])
elif message["type"] == "http.response.body":
body_parts.append(message.get("body", b""))
body = await self._read_body(receive)
async def cached_receive():
return {"type": "http.request", "body": body, "more_body": False}
await self.app(new_scope, cached_receive, capture_send)
response = Response(
content=b"".join(body_parts),
status_code=status_code,
)
if response.status_code == 404 and path != "/" and path.endswith("/"):
return await self._get_response(scope, cached_receive, path.rstrip("/"))
for k, v in resp_headers:
response.headers.raw.append((k, v))
return response
async def _read_body(self, receive: Receive) -> bytes:
body = b""
while True:
message = await receive()
body += message.get("body", b"")
if not message.get("more_body", False):
break
return body
async def _send_with_headers(self, response: Response, scope, receive, send):
response.headers["Server"] = f"nercone.dev ({server_version})"
response.headers["Onion-Location"] = f"http://{onion_hostname}/"
if "access-control-allow-origin" not in response.headers:
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "*"
response.headers["Access-Control-Allow-Headers"] = "*"
await response(scope, receive, send)

View File

@@ -0,0 +1,48 @@
import re
import httpx
import asyncio
from websockets.client import connect
from fastapi import Request, Response, WebSocket
hop_by_hop_headers = ["transfer-encoding", "connection", "keep-alive", "upgrade", "proxy-authenticate", "proxy-authorization", "te", "trailers", "content-encoding", "content-length"]
def to_raw_headers(str_headers: dict[str, str]) -> list[tuple[bytes, bytes]]:
return [(k.encode("latin-1"), v.encode("latin-1")) for k, v in str_headers.items()]
def make_http_proxy(base_url_http: str, headers: dict = {}, remove_prefix_path: bool = False):
async def http_proxy(request: Request, path: str = "") -> Response:
url = f"{base_url_http}/{path}" if remove_prefix_path else f"{base_url_http}{request.url.path}"
merged_headers = dict(request.headers)
merged_headers.pop("accept-encoding", None)
merged_headers |= {k.lower(): v for k, v in headers.items()}
async with httpx.AsyncClient() as client:
resp = await client.request(
method=request.method,
url=url,
headers=to_raw_headers(merged_headers),
content=await request.body(),
params=request.query_params
)
response = Response(content=resp.content, status_code=resp.status_code)
for k, v in resp.headers.multi_items():
if k.lower() not in hop_by_hop_headers:
response.headers.append(k, v)
return response
return http_proxy
def make_websocket_proxy(base_url_websocket: str, remove_prefix_path: bool = False):
async def websocket_proxy(client_ws: WebSocket, path: str = ""):
url = f"{base_url_websocket}/{path}" if remove_prefix_path else f"{base_url_websocket}{client_ws.url.path}"
await client_ws.accept()
async with connect(url) as server_ws:
async def client_to_server():
async for message in client_ws.iter_bytes():
await server_ws.send(message)
async def server_to_client():
async for message in server_ws:
await client_ws.send_bytes(message)
await asyncio.gather(
asyncio.create_task(client_to_server()),
asyncio.create_task(server_to_client())
)
return websocket_proxy

View File

@@ -0,0 +1,140 @@
import json
import random
from pathlib import Path
from zoneinfo import ZoneInfo
from datetime import datetime, timezone
from fastapi import FastAPI, Request, Response
from fastapi.templating import Jinja2Templates
from fastapi.responses import PlainTextResponse, JSONResponse, FileResponse, RedirectResponse
from jinja2.exceptions import TemplateNotFound
from .error import error_page
from .logger import log_access
from .database import AccessCounter
from .proxy import make_http_proxy, make_websocket_proxy
from .middleware import Middleware, server_version, onion_hostname
app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
app.add_middleware(Middleware)
templates = Jinja2Templates(directory=Path.cwd().joinpath("public"))
accesscounter = AccessCounter()
templates.env.globals["get_access_count"] = accesscounter.get
templates.env.globals["server_version"] = server_version
templates.env.globals["onion_site_url"] = f"http://{onion_hostname}/"
poste_http_proxy = make_http_proxy("http://localhost:8081", remove_prefix_path=True)
poste_websocket_proxy = make_websocket_proxy("ws://localhost:8081", remove_prefix_path=True)
app.add_api_route("/mail/{path:path}", poste_http_proxy, methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
app.add_api_websocket_route("/mail/{path:path}", poste_websocket_proxy)
bluesky_http_proxy = make_http_proxy("http://localhost:3000")
bluesky_websocket_proxy = make_websocket_proxy("ws://localhost:3000")
bluesky_routes = ["/xrpc/{path:path}", "/oauth/{path:path}", "/.well-known/atproto-did", "/.well-known/oauth-authorization-server", "/.well-known/oauth-protected-resource"]
for route in bluesky_routes:
app.add_api_route(route, bluesky_http_proxy, methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
app.add_api_websocket_route(route, bluesky_websocket_proxy)
def get_current_year() -> str:
return str(datetime.now(ZoneInfo("Asia/Tokyo")).year)
templates.env.globals["get_current_year"] = get_current_year
def get_daily_quote() -> str:
seed = str(datetime.now(timezone.utc).date())
with Path.cwd().joinpath("public", "quotes.txt").open("r") as f:
quotes = f.read().strip().split("\n")
return random.Random(seed).choice(quotes)
templates.env.globals["get_daily_quote"] = get_daily_quote
@app.api_route("/ping", methods=["GET"])
async def ping(request: Request):
return PlainTextResponse("pong!", status_code=200)
@app.api_route("/echo", methods=["GET"])
async def echo(request: Request):
return JSONResponse(request.scope["log"], status_code=200)
@app.api_route("/status", methods=["GET"])
async def status(request: Request):
return JSONResponse(
{
"status": "ok",
"version": server_version,
"daily_quote": get_daily_quote(),
"access_count": accesscounter.get()
},
status_code=200
)
@app.api_route("/welcome", methods=["GET"])
async def ping(request: Request):
return PlainTextResponse(
f"""
■ ■ ■■■■■ ■■■■ ■■■■ ■■■ ■ ■ ■■■■■
■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■
■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■
■ ■ ■ ■■■■ ■■■■ ■ ■ ■ ■ ■ ■ ■■■■
■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■
■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■
■ ■ ■■■■■ ■ ■ ■■■■ ■■■ ■ ■ ■■■■■
nercone.dev ({server_version})
welcome to nercone.dev!
""".strip() + "\n",
status_code=200
)
@app.api_route("/error/{code}", methods=["GET", "POST", "HEAD"])
async def fake_error_page(request: Request, code: str):
return error_page(templates=templates, request=request, status_code=int(code))
@app.api_route("/{full_path:path}", methods=["GET", "POST", "HEAD"])
async def default_response(request: Request, full_path: str) -> Response:
if not full_path.endswith(".html"):
base_dir = Path.cwd().joinpath("public")
safe_full_path = full_path.lstrip('/')
target_path = (base_dir / safe_full_path).resolve()
if not str(target_path).startswith(str(base_dir.resolve())):
return error_page(templates=templates, request=request, status_code=403, message="ディレクトリトラバーサルね、知ってる。公開してないところ覗きたいのえっt")
if target_path.exists() and target_path.is_file():
return FileResponse(target_path)
templates_to_try = []
if full_path == "" or full_path == "/":
templates_to_try.append("index.html")
elif full_path.endswith(".html"):
templates_to_try.append(full_path.lstrip('/'))
else:
clean_path = full_path.strip('/')
templates_to_try.append(f"{clean_path}.html")
templates_to_try.append(f"{clean_path}/index.html")
for template_name in templates_to_try:
try:
response = templates.TemplateResponse(status_code=200, request=request, name=template_name)
accesscounter.increase()
return response
except TemplateNotFound:
continue
shorturls_json = Path.cwd().joinpath("public", "shorturls.json")
if not shorturls_json.exists():
return error_page(templates=templates, request=request, status_code=500, message="設定ファイルぐらい用意しておけよ!")
try:
with shorturls_json.open("r", encoding="utf-8") as f:
shorturls = json.load(f)
except Exception:
return error_page(templates=templates, request=request, status_code=500, message="なにこの設定ファイル読めないじゃない!")
current_id = full_path.strip().rstrip("/")
visited = set()
for _ in range(10):
if current_id in visited:
return error_page(templates=templates, request=request, status_code=500, message="循環依存ってなんかちょっとえっt")
visited.add(current_id)
if current_id not in shorturls:
break
entry = shorturls[current_id]
entry_type = entry.get("type")
content = entry.get("content")
if entry_type == "redirect":
return RedirectResponse(url=content)
elif entry_type == "alias":
current_id = content
else:
break
return error_page(templates=templates, request=request, status_code=404, message="そんなページ知らないっ!")

1
start.sh Normal file
View File

@@ -0,0 +1 @@
/root/.local/bin/nercone-webserver

7
update.sh Normal file
View File

@@ -0,0 +1,7 @@
sudo /usr/bin/systemctl disable nercone-webserver
sudo /usr/bin/systemctl kill nercone-webserver
/usr/bin/git pull
/root/.local/bin/uv tool uninstall nercone-webserver --no-cache || true
/root/.local/bin/uv tool install . --upgrade --no-cache
sudo /usr/bin/systemctl enable nercone-webserver
sudo /usr/bin/systemctl start nercone-webserver