--
17
.gitignore
vendored
Normal 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
@@ -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
@@ -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
0
logs/.gitkeep
Normal file
9
nercone-webserver-autoupdator.service
Normal 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
|
||||||
9
nercone-webserver-autoupdator.timer
Normal 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
@@ -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
|
||||||
7
public/.well-known/security.txt
Normal 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
@@ -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 %}
|
||||||
28
public/access-counter.html
Normal 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 %}
|
||||||
BIN
public/assets/fonts/MesloLGSNerdFont-Bold.woff2
Normal file
BIN
public/assets/fonts/MesloLGSNerdFont-BoldItalic.woff2
Normal file
BIN
public/assets/fonts/MesloLGSNerdFont-Italic.woff2
Normal file
BIN
public/assets/fonts/MesloLGSNerdFont-Regular.woff2
Normal file
5113
public/assets/images/banner.ai
Normal file
BIN
public/assets/images/banner.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
19
public/assets/images/banner.svg
Normal 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 |
BIN
public/assets/images/banner.webp
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
5252
public/assets/images/banner_cat.ai
Normal file
BIN
public/assets/images/banner_cat.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
38
public/assets/images/banner_cat.svg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
public/assets/images/banner_cat.webp
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
5023
public/assets/images/favicon.ai
Normal file
BIN
public/assets/images/favicon.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
20
public/assets/images/favicon.svg
Normal 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 |
BIN
public/assets/images/favicon.webp
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
5275
public/assets/images/favicon_cat.ai
Normal file
BIN
public/assets/images/favicon_cat.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
20
public/assets/images/favicon_cat.svg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/assets/images/favicon_cat.webp
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
5281
public/assets/images/nercone.ai
Normal file
BIN
public/assets/images/nercone.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
81
public/assets/images/nercone.svg
Normal 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 |
BIN
public/assets/images/nercone.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
524
public/base.html
Normal 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
@@ -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 %}
|
||||||
15
public/download-banner.html
Normal 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><a href="https://nercone.dev/"><img src="https://assets.nercone.dev/images/banner.svg"></a> <!-- SVG --><br><a href="https://nercone.dev/"><img src="https://assets.nercone.dev/images/banner.png"></a> <!-- PNG --><br><a href="https://nercone.dev/"><img src="https://assets.nercone.dev/images/banner.webp"></a> <!-- WEBP --></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><a href="https://nercone.dev/"><img src="https://assets.nercone.dev/images/banner_cat.svg"></a> <!-- SVG --><br><a href="https://nercone.dev/"><img src="https://assets.nercone.dev/images/banner_cat.png"></a> <!-- PNG --><br><a href="https://nercone.dev/"><img src="https://assets.nercone.dev/images/banner_cat.webp"></a> <!-- WEBP --></pre>
|
||||||
|
{% endblock %}
|
||||||
8
public/error.html
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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 <nercone@nercone.dev></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 <nenaicone@nercone.dev></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
@@ -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
@@ -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! │
|
||||||
|
# ╰──────────────────────────────────────────────╯
|
||||||
28
public/server-version.html
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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 %}
|
||||||
22
public/vulnerability-reporters.html
Normal 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
@@ -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"
|
||||||
0
src/nercone_webserver/__init__.py
Normal file
35
src/nercone_webserver/__main__.py
Normal 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()
|
||||||
63
src/nercone_webserver/database.py
Normal 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()
|
||||||
29
src/nercone_webserver/error.py
Normal 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, "あんのーん")})
|
||||||
34
src/nercone_webserver/logger.py
Normal 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
|
||||||
107
src/nercone_webserver/middleware.py
Normal 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)
|
||||||
48
src/nercone_webserver/proxy.py
Normal 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
|
||||||
140
src/nercone_webserver/server.py
Normal 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="そんなページ知らないっ!")
|
||||||
7
update.sh
Normal 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
|
||||||