diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..d6904d3
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,42 @@
+name: Deploy
+
+on:
+ push:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+env:
+ PROJECT: vcportal
+ S3_DOMAIN: cos.accelerate.myqcloud.com
+ S3_BUCKET: luotianyi-dev-1251131545
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup PDM
+ uses: pdm-project/setup-pdm@v4
+ with:
+ python-version: '3.11'
+ - name: Install Dependencies
+ run: pdm install && pdm add git+https://github.com/luotianyi-dev/deploykit.git
+ - name: Set S3 Prefix
+ run: echo "S3_PREFIX=web/host/$PROJECT/$(date +%Y%m%d-%H%M%S)-${{ github.sha }}" >> $GITHUB_ENV
+ - name: Build
+ run: pdm run portal-build --assets-cdn-url $CDN_URL/assets --css-cdn-url $CDN_URL/css build --clean --with-assets --with-css --with-manifest
+ env:
+ CDN_URL: https://${{ env.S3_BUCKET }}.${{ env.S3_DOMAIN }}/${{ env.S3_PREFIX }}
+ - name: Upload to DeployKit
+ run: pdm run deployctl deploy upload --commit ${{ github.sha }} -f dist public -f server.conf server.conf --switch
+ env:
+ API_KEY: ${{ secrets.API_KEY }}
+ - name: Upload to S3
+ run: pdm run deployctl s3 --bucket ${{ env.S3_BUCKET }} --prefix ${{ env.S3_PREFIX }} -f assets -f css
+ env:
+ S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
+ S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
+ S3_ENDPOINT: https://${{ env.S3_DOMAIN }}
+ working-directory: ./dist
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d59e33a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,191 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm-project.org/#use-with-ide
+.pdm.toml
+.pdm-python
+.pdm-build/
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bc80909
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# VC 导航站
+VC 导航站是一个简单的网站导航系统,收集了中文 VOCALOID 的各种常用网站。
+
+此项目是 Tianyi Network Web 的一部分。
+
+## 构建
+此项目使用 GitHub Actions 自动构建,构建文件请参见 [deploy.yml](https://github.com/luotianyi-dev/web-vcportal/blob/main/.github/workflows/deploy.yml)。
+
+## 版权
+除了 `data/assets` 下的内容,其余内容使用 [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/) 许可证。
+
+`data/assets` 下的内容并非由 Tianyi Network 创作,应遵循合理、非商用的规则使用。
diff --git a/data/articles/about.md b/data/articles/about.md
new file mode 100644
index 0000000..8ac2ee1
--- /dev/null
+++ b/data/articles/about.md
@@ -0,0 +1,42 @@
+---
+title: 关于
+---
+## 我想提交新的网站
+
+如果您想要提交新的网站,请点击页脚的「联系我们」,或者联系QQ:3600424575。
+
+请在提交网站时,包含以下内容:
+
+ - 网站名称
+ - 网站地址
+ - 网站简介
+ - 推荐理由(可选)
+
+如果您推荐的是软件下载相关的网站,我们会对该网站的信誉进行审查,避免有人通过该网站投放病毒。
+
+我们欢迎您提交同人网站、同人组织的相关链接,无论您是否为该组织的负责人或Staff。无论网站是否备案均可。我们原则上不会在此列出以展示个人生活、个人作品为主要内容的网站。如果您的网站为VC相关同人网站,请确保您提交的网站:
+
+ - 在国内外均可正常访问
+ - 启用 HTTPS,包括网站本身和网站的 CSS、JS 及资源文件
+ - WAF 设置不影响正常用户体验
+ - 不包含违法违规内容和 NSFW 内容,不包括对特定或不特定虚拟歌手有攻击性的内容
+ - 有实质性的内容,不是Demo页、空白页、「建设中」页面等
+
+## 我想删除此网站上的内容
+
+如果您不想您拥有的网站出现在我们这里,或者您认为我们的网站上的内容侵害了您的权益,请点击页脚的「联系我们」。
+
+如果我们的网站包含了您的作品,请使用「DMCA Notice」的格式,向我们发出函件。
+
+## 我想在您的网站上进行推广
+
+我们的网站不接受刊登任何商业性质的推广信息。但是,如果您是中文虚拟歌手的创作者、相关软件开发者或社区维护者,您可以通过我们的网站推广您的非商业性质的作品。值得注意的是,我们的网站的流量较低,可能很难得到理想的推广效果。
+
+对于音乐作品,无论是原创还是翻唱,只要您作品符合以下要求我们均会尽力为您进行推广:
+
+ - 使用虚拟歌手创作
+ - 发布时间距今6个月以内
+ - 不是商业曲、梗曲;歌曲质量符合最低要求,不「毁狗耳」
+ - 如果您的作品为其他虚拟歌手相关的内容,如教程、工具、MMD等,也可以联系我们进行推广。
+
+请注意,在此网站上推广作品我们不会收取任何费用,如果任何人以我们的名义向您索要钱财或物品,请不要相信并向相关部门或平台举报。
diff --git a/data/assets/719.svg b/data/assets/719.svg
new file mode 100644
index 0000000..930639d
--- /dev/null
+++ b/data/assets/719.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/acestudio.png b/data/assets/acestudio.png
new file mode 100644
index 0000000..59c5d18
Binary files /dev/null and b/data/assets/acestudio.png differ
diff --git a/data/assets/av2bv.svg b/data/assets/av2bv.svg
new file mode 100644
index 0000000..b3f9f9a
--- /dev/null
+++ b/data/assets/av2bv.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/cleanup_pictures.png b/data/assets/cleanup_pictures.png
new file mode 100644
index 0000000..6657b7c
Binary files /dev/null and b/data/assets/cleanup_pictures.png differ
diff --git a/data/assets/cvseplus.png b/data/assets/cvseplus.png
new file mode 100644
index 0000000..aa1a1fc
Binary files /dev/null and b/data/assets/cvseplus.png differ
diff --git a/data/assets/favicon.ico b/data/assets/favicon.ico
new file mode 100644
index 0000000..73b5d67
Binary files /dev/null and b/data/assets/favicon.ico differ
diff --git a/data/assets/infinity.svg b/data/assets/infinity.svg
new file mode 100644
index 0000000..e63b235
--- /dev/null
+++ b/data/assets/infinity.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/ivocaloid.ico b/data/assets/ivocaloid.ico
new file mode 100644
index 0000000..95a40ae
Binary files /dev/null and b/data/assets/ivocaloid.ico differ
diff --git a/data/assets/lrccon.png b/data/assets/lrccon.png
new file mode 100644
index 0000000..5915a68
Binary files /dev/null and b/data/assets/lrccon.png differ
diff --git a/data/assets/luotianyi.jpg b/data/assets/luotianyi.jpg
new file mode 100644
index 0000000..dafc08b
Binary files /dev/null and b/data/assets/luotianyi.jpg differ
diff --git a/data/assets/luotianyi.love.png b/data/assets/luotianyi.love.png
new file mode 100644
index 0000000..4c482f5
Binary files /dev/null and b/data/assets/luotianyi.love.png differ
diff --git a/data/assets/manual.svg b/data/assets/manual.svg
new file mode 100644
index 0000000..419c76c
--- /dev/null
+++ b/data/assets/manual.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/midishow.png b/data/assets/midishow.png
new file mode 100644
index 0000000..80bc05c
Binary files /dev/null and b/data/assets/midishow.png differ
diff --git a/data/assets/mikustudio.png b/data/assets/mikustudio.png
new file mode 100644
index 0000000..833cc8b
Binary files /dev/null and b/data/assets/mikustudio.png differ
diff --git a/data/assets/mikutap.ico b/data/assets/mikutap.ico
new file mode 100644
index 0000000..7e7b414
Binary files /dev/null and b/data/assets/mikutap.ico differ
diff --git a/data/assets/opensynth.ico b/data/assets/opensynth.ico
new file mode 100644
index 0000000..7e69325
Binary files /dev/null and b/data/assets/opensynth.ico differ
diff --git a/data/assets/synthv.png b/data/assets/synthv.png
new file mode 100644
index 0000000..f82df0d
Binary files /dev/null and b/data/assets/synthv.png differ
diff --git a/data/assets/tdd.png b/data/assets/tdd.png
new file mode 100644
index 0000000..2238552
Binary files /dev/null and b/data/assets/tdd.png differ
diff --git a/data/assets/tianyitap.ico b/data/assets/tianyitap.ico
new file mode 100644
index 0000000..2a7a89f
Binary files /dev/null and b/data/assets/tianyitap.ico differ
diff --git a/data/assets/tybbs.svg b/data/assets/tybbs.svg
new file mode 100644
index 0000000..e90952d
--- /dev/null
+++ b/data/assets/tybbs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/u-solid.svg b/data/assets/u-solid.svg
new file mode 100644
index 0000000..803501d
--- /dev/null
+++ b/data/assets/u-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/vcmusic.org.svg b/data/assets/vcmusic.org.svg
new file mode 100644
index 0000000..7f6f899
--- /dev/null
+++ b/data/assets/vcmusic.org.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/vcmusic.png b/data/assets/vcmusic.png
new file mode 100644
index 0000000..4521f07
Binary files /dev/null and b/data/assets/vcmusic.png differ
diff --git a/data/assets/vocadb.ico b/data/assets/vocadb.ico
new file mode 100644
index 0000000..b7d6a8c
Binary files /dev/null and b/data/assets/vocadb.ico differ
diff --git a/data/assets/vocakey.svg b/data/assets/vocakey.svg
new file mode 100644
index 0000000..72c675d
--- /dev/null
+++ b/data/assets/vocakey.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/assets/vocaloid.png b/data/assets/vocaloid.png
new file mode 100644
index 0000000..ba9ccd9
Binary files /dev/null and b/data/assets/vocaloid.png differ
diff --git a/data/assets/vsqx.top.jpg b/data/assets/vsqx.top.jpg
new file mode 100644
index 0000000..2cc6efe
Binary files /dev/null and b/data/assets/vsqx.top.jpg differ
diff --git a/data/assets/wantwords.ico b/data/assets/wantwords.ico
new file mode 100644
index 0000000..6dba56a
Binary files /dev/null and b/data/assets/wantwords.ico differ
diff --git a/data/assets/xiayuyao.jpg b/data/assets/xiayuyao.jpg
new file mode 100644
index 0000000..dfeb4e7
Binary files /dev/null and b/data/assets/xiayuyao.jpg differ
diff --git a/data/manifest.yml b/data/manifest.yml
new file mode 100644
index 0000000..760450c
--- /dev/null
+++ b/data/manifest.yml
@@ -0,0 +1,313 @@
+site:
+ name: VC 导航站
+ lang: zh-CN
+ description: ""
+ footer: |
+ © 2024 Tianyi Network
+ MPL 2.0 License
+
+ 关于我们 | 联系我们 | 开放源码
+
+topbar:
+ - name: 首页
+ url: /
+ - name: 关于
+ url: /about
+
+tags:
+
+groups:
+ - name: 创作交流
+ sites:
+ - name: 天依心声代理站
+ icon: infinity.svg
+ description: 官方创作交流平台
+ url: https://docs.qq.com/sheet/DSmtYc3RSaHRFT1VH
+ icon_effects: [dark-invert]
+ - name: VSQX分享平台
+ icon: vsqx.top.jpg
+ description: 调音工程分享网站
+ url: https://www.vsqx.top
+ - name: MuseSource
+ icon: https://musescore.org/favicon.ico
+ description: 制谱软件乐谱下载
+ url: https://musescore.org/
+ - name: MidiShow
+ icon: midishow.png
+ description: MIDI交流下载
+ url: https://www.midishow.com/
+ - name: 模之屋
+ icon: https://www.aplaybox.com/favicon.ico
+ description: 模型创作分享社区
+ url: https://www.aplaybox.com/
+ - name: Pixiv
+ icon: https://www.pixiv.net/favicon.ico
+ description: 插画交流网站
+ url: https://www.pixiv.net/
+ - name: 工具网站
+ sites:
+ - name: UtaFormatix
+ icon: u-solid.svg
+ description: 调教工程转换器
+ url: https://sdercolin.github.io/utaformatix3/
+ icon_effects: [dark-invert]
+ - name: VocalRemover
+ icon: https://vocalremover.org/favicon.ico
+ description: AI人声伴奏分离
+ url: https://vocalremover.org/zh/?patrons
+ - name: BPM Estimater
+ icon: 719.svg
+ description: 719的BPM检测器
+ url: https://baka719.top/v_beginner/
+ icon_effects: [dark-invert]
+ - name: MikuMikuAR
+ icon: https://www.mikumikuar.com/favicon.ico
+ description: 手机MMD播放软件
+ url: https://www.mikumikuar.com/
+ - name: 灯里歌词滚动姬
+ icon: https://lrc-maker.github.io/favicons/apple-touch-icon.png
+ description: LRC制作软件
+ url: https://lrc-maker.github.io/
+ - name: SauceNAO
+ icon: https://saucenao.com/favicon.ico
+ description: AI图源查找网站
+ url: https://saucenao.com/
+ - name: AV/BV号转换
+ icon: av2bv.svg
+ description: 转换AV/BV号
+ icon_effects: [dark-invert]
+ url: https://mrhso.github.io/IshisashiWebsite/BVwhodoneit/
+ - name: WantWords
+ icon: wantwords.ico
+ description: 中英文反义词查找
+ url: https://wantwords.net/
+ - name: 邻近词汇检索
+ icon: wantwords.ico
+ description: 中英文同义词查找
+ url: https://tool.mingdawoo.com/lang/nearby_word/
+ - name: 除你背景
+ icon: cleanup_pictures.png
+ description: AI移除图片中物体
+ url: https://cleanup.pictures/
+ icon_effects: [dark-invert]
+ - name: 歌词字幕转换
+ icon: lrccon.png
+ description: 歌词字幕转换
+ url: https://www.lrccon.com/convert.php
+ - name: 软件下载
+ sites:
+ - name: VocaKey
+ icon: vocakey.svg
+ description: 可能炸了
+ url: http://vocakey.info/
+ icon_effects: [dark-invert]
+ - name: VC资源分享
+ icon: https://storage.josephcz.xyz/favicon.ico
+ description: 由 Joseph 提供
+ url: https://storage.josephcz.xyz/TianyiWorkstation/
+ - name: 教程分享
+ sites:
+ - name: 调教教程导航贴
+ description: By 龙猫
+ url: https://t.bilibili.com/608079597169238889
+ - name: 歌声合成教程站
+ description: By Haru.jpg
+ url: https://vocalsynth.harujpg.top
+ - name: 音频技术导论
+ description: By Ltyxh
+ url: https://ltyxh.com/blog/2022/05/12/%E9%9F%B3%E9%A2%91%E6%8A%80%E6%9C%AF1/
+ - name: 数据分析
+ sites:
+ - name: 天钿Daily
+ icon: tdd.png
+ description: VC数据交流分享
+ url: https://tdd.bunnyxt.com/
+ - name: CVSE+
+ icon: cvseplus.png
+ description: 国产引擎排行榜
+ url: https://space.bilibili.com/151439645
+ - name: 中文虚拟歌手周刊
+ url: https://space.bilibili.com/156489
+ - name: 洛天依新曲排行榜
+ url: https://space.bilibili.com/24615264/channel/collectiondetail?sid=741972
+ - name: 言和新曲排行榜
+ url: https://space.bilibili.com/67946083/channel/collectiondetail?sid=737329
+ - name: 同人团体
+ sites:
+ - name: 洛天依相关收集站
+ icon: https://i1.hdslb.com/bfs/face/7f766f409258d4c011bdee4730b8d2217bf3d40a.jpg
+ url: https://space.bilibili.com/34727551
+ icon_effects: [circle]
+ - name: 乐正绫相关收集站
+ icon: https://i2.hdslb.com/bfs/face/baf8a50df947a9e772686952a5adfd87cc1a4ac9.jpg
+ url: https://space.bilibili.com/347392364
+ icon_effects: [circle]
+ - name: 心华相关收集站
+ icon: https://i1.hdslb.com/bfs/face/3b50e431f9db5519c86ca55401de02919a78a212.jpg
+ url: https://space.bilibili.com/285330217
+ icon_effects: [circle]
+ - name: 南北同萌培养姬
+ icon: https://i1.hdslb.com/bfs/face/58c0194438b9bdfb132f28525628f1e64eee30a8.jpg
+ url: https://space.bilibili.com/381680134
+ icon_effects: [circle]
+ - name: 瓜果瓜秧电视台
+ icon: https://i0.hdslb.com/bfs/face/df9d8ae3c63aad607ff5c3dd014cec82e50fcc31.jpg
+ url: https://space.bilibili.com/323657769
+ icon_effects: [circle]
+ - name: 中V宝藏歌曲挖掘站
+ icon: vcmusic.org.svg
+ url: https://space.bilibili.com/526975910
+ icon_effects: [circle, dark-whiteback]
+ - name: 同人网站
+ sites:
+ - name: iVocaloid论坛
+ description: 古早V家交流论坛
+ icon: ivocaloid.ico
+ url: http://bbs.ivocaloid.com/
+ - name: VocaDB
+ description: 虚拟歌手数据库
+ icon: vocadb.ico
+ url: https://vocadb.net/
+ - name: SV用户手册
+ description: SV的非官方教程
+ icon: manual.svg
+ url: https://synthesizer-v-r2-docs.vercel.app/
+ reverse_color: true
+ - name: 小初音工作室
+ description: Q版Flash动画
+ icon: mikustudio.png
+ url: https://www.deviantart.com/johnsu/art/Halfne-Miku-Studio-396850314
+ - name: MikuTap
+ description: 让Miku酱陪你唱歌
+ icon: mikutap.ico
+ url: https://aidn.jp/mikutap/
+ - name: TianyiTap
+ description: 多歌姬版的MikuTap
+ icon: tianyitap.ico
+ url: https://tianyitap.com/
+ - name: OpenSynth
+ description: 自制合成引擎Wiki
+ icon: opensynth.ico
+ url: https://opensynth.miraheze.org/
+ - name: 洛天依Love
+ description: 歌曲推荐网站
+ icon: luotianyi.love.png
+ url: https://www.luotianyi.love/
+ - name: VC音乐推荐网
+ description: 歌曲推荐网站
+ icon: vcmusic.png
+ url: https://www.vcmusic.top/
+ - name: 依の后花园
+ description: V次元-依の后花园
+ icon: tybbs.svg
+ url: https://bbs.66ccff.cc/
+ - name: 官方网站
+ sites:
+ - name: Vocaloid
+ icon: vocaloid.png
+ url: https://www.vocaloid.com/
+ - name: Dreamtonics
+ icon: synthv.png
+ url: https://dreamtonics.com/
+ - name: ACE Studio
+ icon: acestudio.png
+ url: http://ace-studio.timedomain.cn/
+ - name: Vsinger
+ icon: https://vsinger.com/favicon.ico
+ url: https://vsinger.com/
+ white_background: true
+ - name: 初音未来
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i2.hdslb.com/bfs/face/e6c83e92c8c2b5af89c64eea3acbc328c8e4eca1.jpg
+ url: https://weibo.com/mikucfm
+ - name: 洛天依
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/9a629a3a6f941546170ae93b605f3abad7cf5632.jpg
+ url: https://weibo.com/luotianyi0712
+ - name: 言和
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/b04221435adbbb69f1751a9a7e82acf37e346165.jpg
+ url: https://weibo.com/yanheproject
+ - name: 心华
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/7e0f2f125900f5367d1dcc0f19de20b0c3d7007e.jpg
+ url: https://weibo.com/vocaloidxinhua
+ - name: 乐正绫
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/4c621b52d13b28502cc88208898451b952126aa9.jpg
+ url: https://weibo.com/yuezhengling
+ - name: 星尘
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/30999035955895b80c3f101cd31bc979b19ab6e7.jpg
+ url: https://weibo.com/stardustofficial
+ - name: 乐正龙牙
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i0.hdslb.com/bfs/face/b372a61e0d29979153c461fab927c81a54cabe35.jpg
+ url: https://weibo.com/yuezhenglongya0712
+ - name: 徵羽摩柯
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/fc919ecce9d6e9bcbf65e9da6d3dc5c923791999.jpg
+ url: https://weibo.com/zhiyumoke
+ - name: 墨清弦
+ description: 官方微博
+ icon_effects: [circle]
+ icon: https://i0.hdslb.com/bfs/face/8ffda90654e7a9da1b2c77c38afffbc3bf02e47b.jpg
+ url: https://weibo.com/moqingxian0712
+ - name: 初音未来
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i2.hdslb.com/bfs/face/e6c83e92c8c2b5af89c64eea3acbc328c8e4eca1.jpg
+ url: https://space.bilibili.com/399918500/
+ - name: 洛天依
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/9a629a3a6f941546170ae93b605f3abad7cf5632.jpg
+ url: https://space.bilibili.com/36081646/
+ - name: 言和
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/b04221435adbbb69f1751a9a7e82acf37e346165.jpg
+ url: https://space.bilibili.com/406948276/
+ - name: 心华
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/7e0f2f125900f5367d1dcc0f19de20b0c3d7007e.jpg
+ url: https://space.bilibili.com/188839285/
+ - name: 乐正绫
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/4c621b52d13b28502cc88208898451b952126aa9.jpg
+ url: https://space.bilibili.com/406948651/
+ - name: 星尘
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/30999035955895b80c3f101cd31bc979b19ab6e7.jpg
+ url: https://space.bilibili.com/15817819/
+ - name: 乐正龙牙
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i0.hdslb.com/bfs/face/b372a61e0d29979153c461fab927c81a54cabe35.jpg
+ url: https://space.bilibili.com/406948857/
+ - name: 徵羽摩柯
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i1.hdslb.com/bfs/face/fc919ecce9d6e9bcbf65e9da6d3dc5c923791999.jpg
+ url: https://space.bilibili.com/406949083/
+ - name: 墨清弦
+ description: 官方BiliBili账号
+ icon_effects: [circle]
+ icon: https://i0.hdslb.com/bfs/face/8ffda90654e7a9da1b2c77c38afffbc3bf02e47b.jpg
+ url: https://space.bilibili.com/406950978/
+ - name: 夏语遥
+ description: 官方推特
+ icon_effects: [circle]
+ icon: xiayuyao.jpg
+ url: https://twitter.com/yuyaoofficial
diff --git a/pdm.lock b/pdm.lock
new file mode 100644
index 0000000..7984054
--- /dev/null
+++ b/pdm.lock
@@ -0,0 +1,204 @@
+# This file is @generated by PDM.
+# It is not intended for manual editing.
+
+[metadata]
+groups = ["default"]
+strategy = ["cross_platform", "inherit_metadata"]
+lock_version = "4.4.1"
+content_hash = "sha256:d62d1b69b8e89b55d35458b53c0bfea86208d7a83c78c59add7b829aa199d071"
+
+[[package]]
+name = "blinker"
+version = "1.7.0"
+requires_python = ">=3.8"
+summary = "Fast, simple object-to-object and broadcast signaling"
+groups = ["default"]
+files = [
+ {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"},
+ {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"},
+]
+
+[[package]]
+name = "click"
+version = "8.1.7"
+requires_python = ">=3.7"
+summary = "Composable command line interface toolkit"
+groups = ["default"]
+dependencies = [
+ "colorama; platform_system == \"Windows\"",
+]
+files = [
+ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
+ {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+summary = "Cross-platform colored terminal text."
+groups = ["default"]
+marker = "platform_system == \"Windows\""
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "flask"
+version = "3.0.2"
+requires_python = ">=3.8"
+summary = "A simple framework for building complex web applications."
+groups = ["default"]
+dependencies = [
+ "Jinja2>=3.1.2",
+ "Werkzeug>=3.0.0",
+ "blinker>=1.6.2",
+ "click>=8.1.3",
+ "itsdangerous>=2.1.2",
+]
+files = [
+ {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"},
+ {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"},
+]
+
+[[package]]
+name = "htmlmin"
+version = "0.1.12"
+summary = "An HTML Minifier"
+groups = ["default"]
+files = [
+ {file = "htmlmin-0.1.12.tar.gz", hash = "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178"},
+]
+
+[[package]]
+name = "itsdangerous"
+version = "2.1.2"
+requires_python = ">=3.7"
+summary = "Safely pass data to untrusted environments and back."
+groups = ["default"]
+files = [
+ {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
+ {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.3"
+requires_python = ">=3.7"
+summary = "A very fast and expressive template engine."
+groups = ["default"]
+dependencies = [
+ "MarkupSafe>=2.0",
+]
+files = [
+ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
+ {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
+]
+
+[[package]]
+name = "markdown"
+version = "3.6"
+requires_python = ">=3.8"
+summary = "Python implementation of John Gruber's Markdown."
+groups = ["default"]
+files = [
+ {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"},
+ {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"},
+]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.5"
+requires_python = ">=3.7"
+summary = "Safely add untrusted strings to HTML/XML markup."
+groups = ["default"]
+files = [
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
+ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
+]
+
+[[package]]
+name = "portal"
+version = "1.0.5"
+requires_python = ">=3.9"
+git = "https://github.com/baobao1270/portal.git"
+revision = "d449f5ce99f91093d9b208ea369ab2622cb252c6"
+summary = "An unified portal for listing URLs"
+groups = ["default"]
+dependencies = [
+ "flask>=3.0.0",
+ "htmlmin>=0.1.12",
+ "markdown>=3.5.1",
+ "pygments>=2.17.2",
+ "pyyaml>=6.0.1",
+]
+
+[[package]]
+name = "pygments"
+version = "2.17.2"
+requires_python = ">=3.7"
+summary = "Pygments is a syntax highlighting package written in Python."
+groups = ["default"]
+files = [
+ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
+ {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.1"
+requires_python = ">=3.6"
+summary = "YAML parser and emitter for Python"
+groups = ["default"]
+files = [
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
+ {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
+ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+]
+
+[[package]]
+name = "werkzeug"
+version = "3.0.2"
+requires_python = ">=3.8"
+summary = "The comprehensive WSGI web application library."
+groups = ["default"]
+dependencies = [
+ "MarkupSafe>=2.1.1",
+]
+files = [
+ {file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"},
+ {file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..e3f8db2
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,19 @@
+[project]
+name = "web-vcportal"
+version = "0"
+authors = [
+ {name = "Tianyi Network"}
+]
+dependencies = [
+ "portal @ git+https://github.com/baobao1270/portal.git",
+]
+requires-python = ">=3.11"
+readme = "README.md"
+
+[build-system]
+requires = ["pdm-backend"]
+build-backend = "pdm.backend"
+
+
+[tool.pdm]
+distribution = true
diff --git a/server.conf b/server.conf
new file mode 100644
index 0000000..62a4e49
--- /dev/null
+++ b/server.conf
@@ -0,0 +1,9 @@
+error_page 404 /404.html;
+
+if ($host != "vc123.xyz") {
+ return 307 https://vc123.xyz$request_uri;
+}
+
+location / {
+ try_files $uri $uri/ $uri.html =404;
+}