commit
a61d2e1a21
241 changed files with 27663 additions and 0 deletions
@ -0,0 +1,7 @@ |
||||
version: 2 |
||||
updates: |
||||
- package-ecosystem: npm |
||||
directory: "/" |
||||
schedule: |
||||
interval: daily |
||||
open-pull-requests-limit: 20 |
@ -0,0 +1,8 @@ |
||||
.DS_Store |
||||
Thumbs.db |
||||
db.json |
||||
*.log |
||||
node_modules/ |
||||
public/ |
||||
.deploy*/ |
||||
_multiconfig.yml |
@ -0,0 +1,5 @@ |
||||
# Default ignored files |
||||
/shelf/ |
||||
/workspace.xml |
||||
# Editor-based HTTP Client requests |
||||
/httpRequests/ |
@ -0,0 +1,8 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="ProjectModuleManager"> |
||||
<modules> |
||||
<module fileurl="file://$PROJECT_DIR$/.idea/个人博客.iml" filepath="$PROJECT_DIR$/.idea/个人博客.iml" /> |
||||
</modules> |
||||
</component> |
||||
</project> |
@ -0,0 +1,12 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module type="WEB_MODULE" version="4"> |
||||
<component name="NewModuleRootManager"> |
||||
<content url="file://$MODULE_DIR$"> |
||||
<excludeFolder url="file://$MODULE_DIR$/temp" /> |
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" /> |
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" /> |
||||
</content> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
</component> |
||||
</module> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,136 @@ |
||||
# Hexo Configuration |
||||
## Docs: https://hexo.io/docs/configuration.html |
||||
## Source: https://github.com/hexojs/hexo/ |
||||
|
||||
# Site |
||||
title: 牧尘的NAS小站 |
||||
subtitle: '最好用的NAS使用技巧' |
||||
description: 'NAS折腾不完全指南,定期分享一些好玩的东西,不定期记录生活。' |
||||
keywords: |
||||
author: 牧尘 |
||||
language: zh-CN |
||||
timezone: '' |
||||
|
||||
# URL |
||||
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project' |
||||
url: https://www.dreamlyn.cn |
||||
permalink: :abbrlink.html |
||||
permalink_defaults: |
||||
abbrlink: |
||||
alg: crc32 #support crc16(default) and crc32 |
||||
rep: dec #support dec(default) and hex |
||||
pretty_urls: |
||||
trailing_index: true # Set to false to remove trailing 'index.html' from permalinks |
||||
trailing_html: true # Set to false to remove trailing '.html' from permalinks |
||||
|
||||
# Directory |
||||
source_dir: source |
||||
public_dir: public |
||||
tag_dir: tags |
||||
archive_dir: archives |
||||
category_dir: categories |
||||
code_dir: downloads/code |
||||
i18n_dir: :lang |
||||
skip_render: |
||||
# Writing |
||||
new_post_name: :title.md # File name of new posts |
||||
default_layout: post |
||||
titlecase: false # Transform title into titlecase |
||||
external_link: |
||||
enable: true # Open external links in new tab |
||||
field: site # Apply to the whole site |
||||
exclude: '' |
||||
filename_case: 0 |
||||
render_drafts: false |
||||
post_asset_folder: true |
||||
relative_link: false |
||||
future: true |
||||
syntax_highlighter: highlight.js |
||||
highlight: |
||||
line_number: true |
||||
auto_detect: true |
||||
tab_replace: '' |
||||
wrap: true |
||||
hljs: false |
||||
prismjs: |
||||
preprocess: true |
||||
line_number: true |
||||
tab_replace: '' |
||||
|
||||
# Home page setting |
||||
# path: Root path for your blogs index page. (default = '') |
||||
# per_page: Posts displayed per page. (0 = disable pagination) |
||||
# order_by: Posts order. (Order by date descending by default) |
||||
index_generator: |
||||
path: '' |
||||
per_page: 10 |
||||
order_by: -date |
||||
|
||||
# Category & Tag |
||||
default_category: uncategorized |
||||
category_map: |
||||
网络基础: network |
||||
NAS基础: nasbase |
||||
NAS服务: nasservice |
||||
tag_map: |
||||
网络: network |
||||
NAS技术: nas |
||||
反向代理: rproxy |
||||
|
||||
# Metadata elements |
||||
## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta |
||||
meta_generator: true |
||||
|
||||
# Date / Time format |
||||
## Hexo uses Moment.js to parse and display date |
||||
## You can customize the date format as defined in |
||||
## http://momentjs.com/docs/#/displaying/format/ |
||||
date_format: YYYY-MM-DD |
||||
time_format: HH:mm:ss |
||||
## updated_option supports 'mtime', 'date', 'empty' |
||||
updated_option: 'mtime' |
||||
|
||||
# Pagination |
||||
## Set per_page to 0 to disable pagination |
||||
per_page: 10 |
||||
pagination_dir: page |
||||
|
||||
# Include / Exclude file(s) |
||||
## include:/exclude: options only apply to the 'source/' folder |
||||
include: |
||||
exclude: |
||||
ignore: |
||||
|
||||
# Extensions |
||||
## Plugins: https://hexo.io/plugins/ |
||||
## Themes: https://hexo.io/themes/ |
||||
theme: butterfly |
||||
|
||||
# Deployment |
||||
## Docs: https://hexo.io/docs/one-command-deployment |
||||
deploy: |
||||
type: ali-oss |
||||
region: oss-cn-beijing |
||||
accessKeyId: LTAI4Fswd2FDvYJybC6MbpJH |
||||
accessKeySecret: XcX2iMXRhKBIgc5Ev7jMKOPWvf9VcE |
||||
bucket: template-blog |
||||
# 站点地图 |
||||
sitemap: |
||||
path: sitemap.xml |
||||
baidusitemap: |
||||
path: baidusitemap.xml |
||||
# 订阅 |
||||
feed: |
||||
enable: true |
||||
type: atom |
||||
path: atom.xml |
||||
limit: 20 |
||||
baidu_url_submit: |
||||
count: 3 ## 比如3,代表提交最新的三个链接 |
||||
host: www.dreamlyn.cn ## 在百度站长平台中注册的域名 |
||||
token: 8qrbejM1ER8SkGIH ## 请注意这是您的秘钥, 请不要发布在公众仓库里! |
||||
path: baidu_urls.txt ## 文本文档的地址, 新链接会保存在此文本文档里 |
||||
#重定向 |
||||
alias: |
||||
bsrt/: html/bsrt.html |
||||
nass/: https://outline.dreamlyn.cn:8443/share/19b1d3c1-9926-46da-9bbe-511d5caf88f3 |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@ |
||||
{ |
||||
"name": "hexo-site", |
||||
"version": "0.0.0", |
||||
"private": true, |
||||
"scripts": { |
||||
"build": "hexo generate", |
||||
"clean": "hexo clean", |
||||
"deploy": "hexo deploy", |
||||
"server": "hexo server" |
||||
}, |
||||
"hexo": { |
||||
"version": "7.1.1" |
||||
}, |
||||
"dependencies": { |
||||
"hexo": "^7.0.0", |
||||
"hexo-abbrlink": "^2.2.1", |
||||
"hexo-baidu-url-submit": "^0.0.6", |
||||
"hexo-deployer-ali-oss": "^1.0.0", |
||||
"hexo-generator-alias": "^1.0.0", |
||||
"hexo-generator-archive": "^2.0.0", |
||||
"hexo-generator-baidu-sitemap": "^0.1.9", |
||||
"hexo-generator-category": "^2.0.0", |
||||
"hexo-generator-feed": "^3.0.0", |
||||
"hexo-generator-index": "^3.0.0", |
||||
"hexo-generator-search": "^2.4.3", |
||||
"hexo-generator-sitemap": "^3.0.1", |
||||
"hexo-generator-tag": "^2.0.0", |
||||
"hexo-renderer-ejs": "^2.0.0", |
||||
"hexo-renderer-marked": "^6.0.0", |
||||
"hexo-renderer-pug": "^3.0.0", |
||||
"hexo-renderer-stylus": "^3.0.0", |
||||
"hexo-server": "^3.0.0", |
||||
"hexo-theme-landscape": "^1.0.0", |
||||
"hexo-wordcount": "^6.0.1" |
||||
} |
||||
} |
@ -0,0 +1,5 @@ |
||||
hexo new page --path about/me "About me" |
||||
hexo new --path about/me "About me" |
||||
|
||||
# 部署命令 |
||||
hexo g && hexo d |
@ -0,0 +1,4 @@ |
||||
--- |
||||
title: {{ title }} |
||||
tags: |
||||
--- |
@ -0,0 +1,4 @@ |
||||
--- |
||||
title: {{ title }} |
||||
date: {{ date }} |
||||
--- |
@ -0,0 +1,9 @@ |
||||
--- |
||||
title: {{ title }} |
||||
date: {{ date }} |
||||
tags: |
||||
categories: |
||||
keywords: |
||||
description: |
||||
cover: |
||||
--- |
@ -0,0 +1,7 @@ |
||||
- class_name: 友情鏈接 |
||||
class_desc: |
||||
link_list: |
||||
- name: 我不是咕咕鸽 |
||||
link: https://blog.laoda.de |
||||
avatar: https://blog.laoda.de/upload/guguge.webp |
||||
descr: 服务器折腾不完全指南,定期分享一些好玩的东西,不定期记录生活。 |
@ -0,0 +1,7 @@ |
||||
right: |
||||
- class_name: |
||||
id_name: |
||||
name: dsdf |
||||
icon: fas fa-heartbeat |
||||
order: |
||||
html: '<script type="text/javascript" id="clstr_globe" src="//clustrmaps.com/globe.js?d=5V2tOKp8qAdRM-i8eu7ETTO9ugt5uKbbG-U7Yj8uMl8"></script>' |
@ -0,0 +1,65 @@ |
||||
--- |
||||
title: NAS使用DDNS |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
categories: NAS基础 |
||||
abbrlink: 28082 |
||||
date: 2023-08-22 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
>群晖自带的DDNS无法使用泛域名,我在NAS使用过程中需要把域名的所有子域名都通过DDNS指向本机,所有在这里采用装第三方DDNS服务的方法来使用DDNS。 |
||||
|
||||
DDNS其实就是动态的调整DNS服务器中的A记录,实现的前提就是域名服务商提供API来修改域名的A记录,我们在需要使用DDNS的地方通过脚本来获取可能动态会变动的公网IP,然后通过API告诉域名服务商修改A记录。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/103721.webp"> |
||||
|
||||
因此,实现DDNS需要做两件事:第一,获取DNSPod的API Token;第二,在本地用程序获取公网IP地址并使用DNSPod API更新A记录。 |
||||
|
||||
# 获取DNSPod Token |
||||
在DNSPOD的控制台中,如下图所示点击API密钥中。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/104153.webp"> |
||||
|
||||
点击DNSPod Token并创建密钥,记录下ID和Token。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/104220.webp"> |
||||
|
||||
# 动态更新DNS |
||||
我在威联通上通过docker-compose来实现DDNS。 |
||||
|
||||
在nas上合适的位置创建目录,并创建如下文件。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/104303.webp"> |
||||
|
||||
其中docker-compose文件如下: |
||||
``` |
||||
--- |
||||
version: "3" |
||||
services: |
||||
ddns-go: |
||||
image: jeessy/ddns-go |
||||
container_name: ddns-go |
||||
environment: |
||||
- PUID=1000 |
||||
- PGID=1000 |
||||
- TZ=Asia/Shanghai |
||||
network_mode: "host" |
||||
restart: unless-stopped |
||||
``` |
||||
在ddns-go目录下执行命令docker-compose up -d命令来启动ddns-go,使用docker-compose logs -f来查看启动日志,如果启动没有问题,那么就可以使用浏览器访问http://192.168.31.206:9876来访问。ddns-go的配置界面如下:DNS服务商选择Dnspod;ID和Token设置为上面记录的值;Domains设置为*.dreamlyn.cn。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/104421.webp"> |
||||
|
||||
啰嗦一句,最开始的时候,我是将DDNS的域名设置为home.dreamlyn.cn,并将*.dreamlyn.cn的cname设置为home.dreamlyn.cn。但这样设置的话,后面在使用traefik进行DNS Challenge时会存在问题,不得已只好将DDNS直接设置为*.dreamlyn.cn。 |
||||
|
||||
在其他配置中,设置登录用户名、密码并保存。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/104524.webp"> |
||||
|
||||
查看右侧的日志并登录DNSPod的后台看*.dreamlyn.cn的A记录是否变更,如变更,则说明DDNS安装成功。 |
||||
|
||||
至此,我们便打通了从 外网到NAS的线路,也就是说访问*.dreamlyn.cn:880时会映射到NAS的80端口,访问*.dreamlyn.cn:8443则会映射到NAS的443端口。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/104611.webp"> |
@ -0,0 +1,67 @@ |
||||
--- |
||||
title: NAS双网口改桥接模式 |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
categories: NAS基础 |
||||
abbrlink: 3026110258 |
||||
date: 2024-02-22 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
>当我们有两台设备需要上网(其中一台是群晖NAS),但是旁边只有一个网口时,可以采用将NAS双网口改成桥接模式来实现两台设备的网络连接。 |
||||
|
||||
我们将群晖的两个网络接口分别命名为接口1和接口2,具体的连接方式为,原有的网口接NAS的网络接口1,NAS的网络接口2连接另外一台设备。 |
||||
|
||||
# 打开Open vSwitch功能 |
||||
|
||||
进入群晖系统–>控制面板–>网络–>选中“局域网”–>点击“管理”,在下拉菜单中选择“Open vSwitch 设置” |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/205037.webp"> |
||||
|
||||
在弹出界面中选中“启用” |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/205222.webp"> |
||||
|
||||
# 删除ovs_eth绑定网口 |
||||
|
||||
开启群晖的SSH并登录,使用sudo -i获取root权限。通过ifconfig可以看到ovs_eth0和ovs_eth1,这是群晖两个默认的网桥,对应连接的接口是eth0和eth1,要确定一下哪个是连接路由器的,哪个是连接电脑的。 (以下以ovs_eth0连接路由器,ovs_eth1连接电脑为例) |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/26/091604.webp" > |
||||
|
||||
随后输入命令删除ovs_eth1 |
||||
``` |
||||
ovs-vsctl del-br ovs_eth1 |
||||
``` |
||||
|
||||
# 将eth1加入ovs_eth0网桥 |
||||
使用下面的命令添加ETH1 |
||||
``` |
||||
ovs-vsctl add-port ovs_eth0 eth1 |
||||
``` |
||||
随后输入命令查看网络状态 |
||||
``` |
||||
ovs-vsctl show |
||||
``` |
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/26/092650.webp"> |
||||
|
||||
|
||||
# 设置开机自动桥接 |
||||
上面的的步骤配置完后,群晖重启之后还是会恢复默认网桥设置的,所以我们还得要加一个开机的计划任务让它可以每次开机自动绑定网桥。 |
||||
按下图所示添加用户自定义计划脚本 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/26/093157.webp" > |
||||
|
||||
账号选root,名称随意。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/26/093452.webp" > |
||||
|
||||
在任务设置里面输入刚才的命令并点击确定 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/26/093556.webp" > |
||||
|
||||
命令如下 |
||||
``` |
||||
ovs-vsctl del-br ovs_eth1 |
||||
ovs-vsctl add-port ovs_eth0 eth1 |
||||
``` |
@ -0,0 +1,46 @@ |
||||
--- |
||||
title: Traefik将acme.json 分割成证书 |
||||
tags: |
||||
- 网络 |
||||
categories: NAS基础 |
||||
abbrlink: 60950 |
||||
date: 2023-06-22 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
我们在使用Let’s Encrypt进行自动证书获取时,是将TLS信息存放到acme.json中的。但是总有那么些原因要用到证书,比如我在同一个域名下,有另外一个服务没有使用traefik代理,这时候就需要将acme.json分割成证书。 |
||||
|
||||
在此,使用docker-compose安装certdump来对acme.json进行分割。 |
||||
|
||||
在traefik目录下,创建certdump.yml文件。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/101307.webp"> |
||||
|
||||
文件内容如下: |
||||
``` |
||||
--- |
||||
version: "3" |
||||
services: |
||||
certdumper: |
||||
image: humenius/traefik-certs-dumper:latest |
||||
container_name: certdumper |
||||
environment: |
||||
- PUID=1000 |
||||
- PGID=1000 |
||||
- TZ=Asia/Shanghai |
||||
volumes: |
||||
- ./configs:/traefik:ro |
||||
- ./output:/output:rw |
||||
networks: |
||||
default: |
||||
external: |
||||
name: docker_default |
||||
``` |
||||
此时在traefik目录下执行如下命令: |
||||
``` |
||||
# 创建docker网络 |
||||
docker network create -d bridge --attachable=true docker_default |
||||
# 启动容器,分割acme.json |
||||
docker-compose -f certdump.yml up -d |
||||
``` |
||||
容器启动后,将会在output文件夹下导出证书。 |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,11 @@ |
||||
--- |
||||
title: 外网访问NAS |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
categories: NAS基础 |
||||
abbrlink: 4218199689 |
||||
date: 2023-08-22 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
@ -0,0 +1,26 @@ |
||||
--- |
||||
title: 将花生壳域名的服务商改为DNSPod |
||||
tags: |
||||
- 网络 |
||||
categories: NAS基础 |
||||
abbrlink: 40925 |
||||
date: 2022-03-21 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
>在服务搭建过程中,需要使用域名服务商的API,并通过API来管理域名记录,而花生壳并不提供相应的功能,因而对域名服务商进行调整,下面给出调整过程。 |
||||
|
||||
# 在花生壳管理端修改域名的DNS |
||||
将NS管理中的DNS修改为ninety.dnspod.net和baron.dnspod.net |
||||
注意:修改完之后,花生壳中设定的域名解析将完全失效(包括DDNS) |
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/115913.webp"> |
||||
# 在DNSPod中管理域名 |
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/115914.webp"> |
||||
如图,在面板中点击添加域名,输入域名后一路下一步即可。 |
||||
|
||||
# 为什么要修改域名服务商 |
||||
因为我在搭建服务的过程中,需要使用traefik,traefik有个很好用的东西叫letsencrypt,他可以采用tlsChallenge、httpChallenge和dnsChallenge三种方式来自动申请HTTPS证书。 |
||||
|
||||
但是因为我的很多服务是在家中搭建,运营商并不开放80和443端口,导致tlsChallenge和httpChallenge两种方式无法使用 ,只能使用dnsChallenge。 |
||||
|
||||
我们知道dnsChallenge的验证方式,需要给域名添加一条txt记录来验证域名所有权,如果想完全自动得去申请tls证书,那么一定要使用域名服务商的API,而花生壳并不提供相应的api,导致我不得不更换服务商。 |
@ -0,0 +1,239 @@ |
||||
--- |
||||
title: NAS中安装Traefik |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
- 反向代理 |
||||
categories: NAS服务 |
||||
description: NAS中安装Traefik作反向代理 |
||||
abbrlink: 2636364671 |
||||
date: 2023-02-30 11:02:22 |
||||
keywords: |
||||
--- |
||||
>我在家里的NAS上使用docker搭建了许多服务,这些服务都使用traefik进行代理,当这些服务需要使用HTTPS时,我们可以使用Traefik的Let’s Encrypt来自动获取证书,本篇文章主要介绍Traefik的安装以及如何使用traefik来自动获取证书。 |
||||
|
||||
# 安装Traefik |
||||
我在NAS上的服务大部分都是采用docker-compose的方式进行安装,而traefik的反向代理对docker原生支持,所以我采用traefik作为所有服务的入口。当我们使用不同的域名,比如:gitea.dreamlyn.cn或者给blog.dreamlyn.cn来访问网站时,traefik会根据不同的域名,将访问代理到不同的docker容器。有兴趣深入了解traefik的可以参考[中文文档](https://www.traefik.tech/)或者[官方文档](https://doc.traefik.io/traefik/v2.9/)。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/150652.webp"> |
||||
|
||||
traefik反向代理 |
||||
对于traefik本身的安装,我也是采用了docker-compose的方式。 |
||||
|
||||
在nas上合适的位置创建目录,并创建如下文件。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/150751.webp"> |
||||
|
||||
其中docker-compose文件如下: |
||||
|
||||
``` |
||||
--- |
||||
version: "3" |
||||
services: |
||||
traefik: |
||||
image: traefik:latest |
||||
container_name: traefik |
||||
environment: |
||||
- PUID=1000 |
||||
- PGID=1000 |
||||
- TZ=Asia/Shanghai |
||||
volumes: |
||||
- /var/run/docker.sock:/var/run/docker.sock |
||||
- ./configs/traefik.yml:/etc/traefik/traefik.yml |
||||
- ./configs/conf.d:/configs/conf.d:rw |
||||
ports: |
||||
- "80:80" |
||||
- "443:443" |
||||
labels: |
||||
- "traefik.enable=true" |
||||
#设置http |
||||
- "traefik.http.routers.traefik.entrypoints=web" |
||||
- "traefik.http.routers.traefik.service=traefik" |
||||
- "traefik.http.routers.traefik.rule=Host(`traefik.dreamlyn.cn`)" |
||||
#设定端口号,traefik本身有个dashboard,端口为8080 |
||||
- "traefik.http.services.traefik.loadbalancer.server.port=8080" |
||||
restart: always |
||||
networks: |
||||
default: |
||||
external: |
||||
name: docker_default |
||||
``` |
||||
traefik配置文件如下: |
||||
|
||||
``` |
||||
entryPoints: |
||||
web: |
||||
address: :80 |
||||
websecure: |
||||
address: :443 |
||||
# 两种Traefik的配置方式, |
||||
providers: |
||||
#docker配置方式 |
||||
#可以在使用docker-compose安装wordpress的时候直接配置好wordpress的相关配置,而不需要修改traefik的任何东西。 |
||||
docker: |
||||
exposedByDefault: false |
||||
defaultRule: "Host(`{{trimPrefix `/` .Name}}.dreamlyn.cn`)" |
||||
#file配置方式,反向代理非docker时使用,配置文件统一放在/configs/conf.d目录里 |
||||
file: |
||||
watch: true |
||||
directory: /configs/conf.d |
||||
api: |
||||
debug: true |
||||
dashboard: true |
||||
insecure: true |
||||
#log: |
||||
# level: DEBUG |
||||
``` |
||||
此时在traefik目录下执行如下命令: |
||||
``` |
||||
# 创建docker网络,如果已经有docker_default网络,则不需要。 |
||||
docker network create -d bridge --attachable=true docker_default |
||||
# 启动traefik |
||||
docker-compose up -d |
||||
# 查看启动日志 |
||||
docker-compose logs -f |
||||
``` |
||||
如果启动没有问题,那么就可以使用http://traefik.dreamlyn.cn:880来访问traefik的dashboard了。 |
||||
|
||||
# 使用DNS Challenge |
||||
当我们的服务需要使用HTTPS时,我们可以使用traefik的Let’s Encrypt来自动获取证书。Traefik有三种方式来使用Let’s Encrypt:TLS Challenge、HTTP Challenge和DNS Challenge。 |
||||
|
||||
其中TLS Challenge和HTTP Challenge两种方式较为简单,但是需要服务的访问端口为80或者443,而我们在家中的网络一般都是屏蔽了80和443端口的,所以需要使用较为复杂的DNS Challenge。 |
||||
``` |
||||
Let's Encrypt是一种自动获取HTTPS证书的方式,也就是我们设置好之后,不需要再去手动申请证书,然后各种配置了。 |
||||
``` |
||||
使用DNS Challenge有几个前置条件: |
||||
|
||||
1. 确保域名的服务商在Traefik支持的域名服务商列表中(不在列表中时,请参考这篇文章对域名服务商进行修改)。 |
||||
2. 确保使用DNS Challenge申请的域名使用的是A记录,并且正确解析到traefik服务上。(在NAS折腾记录:打通网络篇,我将DDNS的主机域名直接设置为了*.dreamlyn.cn就是这个原因) |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/151024.webp"> |
||||
|
||||
那么接下来就根据上面的原理一步步实现DNS Challenge。 |
||||
|
||||
## DNS设置 |
||||
DNS Challenge自动获取HTTPS证书的一个过程是验证域名的归属权,这个过程是Traefik通过调用域名服务商的API自动进行的(Traefik支持的域名服务商参考[这里](https://doc.traefik.io/traefik/https/acme/#providers)),而Traefik调用API的时候,是需要SecretId和SecretKey的,接下来具体操作。 |
||||
|
||||
到 https://doc.traefik.io/traefik/https/acme/#providers 查看Traefik是否支持自己所购买的域名服务商,如果不支持也没关系,参考[这篇文章](https://www.dreamlyn.cn/40925.html),将域名服务商修改为DNSPOD(被腾讯收购后改为了Tencent Cloud DNS)。 |
||||
|
||||
在DNSPOD的控制台中,如下图所示点击API密钥中。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/151239.webp"> |
||||
|
||||
新建密钥。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/151305.webp"> |
||||
|
||||
## 修改docker-compose.yml |
||||
如图,在相应位置创建acme.json。需要使用命令`chmod 600 acme.json`将acme.json文件权限调整为600 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/151412.webp"> |
||||
|
||||
修改docker-compose.yml,确保下图红框部分正确配置。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/151443.webp"> |
||||
|
||||
最终代码如下: |
||||
|
||||
``` |
||||
--- |
||||
version: "2.1" |
||||
services: |
||||
traefik: |
||||
image: traefik |
||||
container_name: traefik |
||||
environment: |
||||
- PUID=1000 |
||||
- PGID=1000 |
||||
- TZ=Asia/Shanghai |
||||
# 因为家中的服务端口不是443,在使用Let’s Encrypt时只能用DNS Challenge的方式 |
||||
# 前期如果不需要HTTPS,可以忽略 |
||||
- "TENCENTCLOUD_SECRET_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" |
||||
- "TENCENTCLOUD_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
||||
|
||||
volumes: |
||||
- /var/run/docker.sock:/var/run/docker.sock |
||||
- ./configs/traefik.yml:/etc/traefik/traefik.yml |
||||
# 如果不用HTTPS,可以忽略acem.json |
||||
# 用的话,需要使用 chmod 600 acme.json 命令修改文件的权限 |
||||
- ./configs/acme.json:/configs/acme.json:rw |
||||
- ./configs/conf.d:/configs/conf.d:rw |
||||
- ./configs/certs:/configs/certs |
||||
ports: |
||||
- "80:80" |
||||
- "443:443" |
||||
labels: |
||||
- "traefik.enable=true" |
||||
#设置http |
||||
- "traefik.http.routers.traefik.entrypoints=web" |
||||
- "traefik.http.routers.traefik.service=traefik" |
||||
- "traefik.http.routers.traefik.rule=Host(`traefik.dreamlyn.cn`)" |
||||
#设定端口号,traefik本身有个dashboard,端口为8080 |
||||
- "traefik.http.services.traefik.loadbalancer.server.port=8080" |
||||
restart: always |
||||
networks: |
||||
default: |
||||
external: |
||||
name: docker_default |
||||
``` |
||||
## 修改traefik.yml |
||||
修改traefik.yml文件,确保红框部分的正确配置。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/151535.webp"> |
||||
|
||||
最终代码如下: |
||||
|
||||
``` |
||||
entryPoints: |
||||
web: |
||||
address: :80 |
||||
websecure: |
||||
address: :443 |
||||
# 不使用HTTPS的话,下面可以忽略 |
||||
http: |
||||
tls: |
||||
certResolver: letsencrypt |
||||
domains: |
||||
- main: dreamlyn.cn |
||||
sans: |
||||
- "*.dreamlyn.cn" |
||||
# 两种配置方式, |
||||
providers: |
||||
#docker配置方式 |
||||
#比如:可以在使用docker-compose安装wordpress的时候直接配置好wordpress的相关配置,而不需要修改traefik的任何东西。 |
||||
docker: |
||||
exposedByDefault: false |
||||
defaultRule: "Host(`{{trimPrefix `/` .Name}}.dreamlyn.cn`)" |
||||
#file配置方式,反向代理非docker时使用,比如群晖上的Plex,配置文件统一放在/configs/conf.d目录里 |
||||
file: |
||||
watch: true |
||||
directory: /configs/conf.d |
||||
#注意,letsencrypt在服务器无法开放80和443端口的情况下无法使用tls和http,暂时不用。 |
||||
#后来发现DNS的服务商可以修改,故将花生壳的DNS服务商修改为DNSPod,可以使用DNS-01进行自动tls申请 |
||||
certificatesResolvers: |
||||
letsencrypt: |
||||
acme: |
||||
email: lyn@dreamlyn.cn |
||||
storage: /configs/acme.json |
||||
dnschallenge: |
||||
provider: tencentcloud |
||||
api: |
||||
debug: true |
||||
dashboard: true |
||||
insecure: true |
||||
#log: |
||||
# level: DEBUG |
||||
``` |
||||
## 运行Traefik |
||||
在traefik目录下执行如下命令: |
||||
``` |
||||
# 创建docker网络,如果已经有docker_default网络,则不需要。 |
||||
docker network create -d bridge --attachable=true docker_default |
||||
# 启动traefik |
||||
docker-compose up -d |
||||
# 查看启动日志 |
||||
docker-compose logs -f |
||||
``` |
||||
如果日志没有问题的话,我们就可以通过http://traefik.dreamlyn.cn:880来访问了。 |
||||
|
||||
至此,Traefik的安装已经完成了。 |
@ -0,0 +1,225 @@ |
||||
--- |
||||
title: 使用frp进行内网穿透 |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
categories: NAS服务 |
||||
abbrlink: 25460 |
||||
date: 2023-02-25 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
>当我们所搭建的服务并不具备公网IP,无法从外放访问服务时,可以利用frp来进行内网穿透。 |
||||
|
||||
# 什么是frp |
||||
frp服务是内网穿透服务中的一种,可以理解为花生壳内网穿透的替代品,但是要比花生壳内网穿透快很多。它的大致原理如下,用户访问安装有frps服务的设备,frps能根据与frpc建立的联系,自动打通隧道,使用户的访问映射到内网的客户端。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/230354.webp"> |
||||
|
||||
# 开始使用frp |
||||
接下来我们分两种情况介绍具体如何使用: |
||||
|
||||
1. 当我们有一台暴漏公网IP的Centos服务器时。 |
||||
2. 当我们没有任何可以暴漏公网IP的服务器时。 |
||||
|
||||
# 在Centos上搭建frp服务 |
||||
当我们有一台暴漏公网IP的Centos服务器时,可以在服务器端安装frps服务。 |
||||
|
||||
## 下载frp并解压 |
||||
|
||||
到GitHub查看最新版本:https://github.com/fatedier/frp/releases |
||||
|
||||
如果访问不了github,我在这儿也给出0.45.0版下载地址。 |
||||
|
||||
操作命令如下: |
||||
``` |
||||
# 下载frp可执行包 |
||||
wget https://github.com/fatedier/frp/releases/download/v0.45.0/frp_0.45.0_linux_amd64.tar.gz |
||||
# git无法访问的话,用下面的地址 |
||||
wget https://minio.dreamlyn.cn:9000/blog/attachment/frp_0.45.0_linux_amd64.tar.gz |
||||
# 解压 |
||||
tar zxf frp_0.45.0_linux_amd64.tar.gz |
||||
``` |
||||
## 配置frps |
||||
``` |
||||
# 进入解压缩后的目录 |
||||
cd frp_0.45.0_linux_amd64/ |
||||
# 修改配置 |
||||
vim frps.ini |
||||
``` |
||||
配置文件如下: |
||||
``` |
||||
[common] |
||||
# fpr客户端和frp服务器通信的地址 |
||||
bind_port = 7000 |
||||
# 默认http和https监听端口 |
||||
vhost_http_port = 80 |
||||
vhost_https_port = 443 |
||||
# 针对不同的服务使用[]隔开,服务名自己可以随便定,跟客户端能对应上就行。 |
||||
[ssh] |
||||
# 默认使用tcp,监听服务端的2200端口 |
||||
# 如果服务端接收到2200端口,则跟据[ssh]这个服务名到客户端寻找相应配置。 |
||||
listen_port = 2200 |
||||
auth_token = 123456 |
||||
# [http]服务需要指定type |
||||
[http] |
||||
type = http |
||||
custom_domains = nas.example.com |
||||
auth_token = 123456 |
||||
# remote这个服务时当时我为了远程访问windows桌面设定的,很实用。 |
||||
[remote] |
||||
listen_port = 3389 |
||||
auth_token = 123456 |
||||
``` |
||||
## 启动 |
||||
``` |
||||
# 启动frps |
||||
./frps -c frps.ini |
||||
``` |
||||
## 设置开机自启 |
||||
使用vim创建并编辑 /lib/systemd/system/frps.service,设置如下 |
||||
``` |
||||
[Unit] |
||||
Description=Frp Server Service |
||||
After=network.target |
||||
|
||||
[Service] |
||||
Type=simple |
||||
# 假设fpr存放地址为/usr/local/ |
||||
ExecStart=/usr/local/frp_0.45.0_linux_amd64/frps -c /usr/local/frp_0.45.0_linux_amd64/frps.ini |
||||
KillSignal=SIGQUIT |
||||
TimeoutStopSec=5 |
||||
KillMode=process |
||||
PrivateTmp=true |
||||
StandardOutput=syslog |
||||
StandardError=inherit |
||||
|
||||
[Install] |
||||
WantedBy=multi-user.target |
||||
``` |
||||
之后就可以使用如下命令控制frp服务 |
||||
``` |
||||
启动服务 systemctl start frps |
||||
开机自启动 systemctl enable frps |
||||
重启服务 systemctl restart frps |
||||
停止服务 systemctl stop frps |
||||
查看日志与状态 systemctl status frps |
||||
``` |
||||
# 使用第三方的免费frp服务 |
||||
当我们没有任何可以暴漏公网IP的服务器时,就需要使用第三方的免费frp服务,如果已经使用centos搭建了frp服务,可以跳过这段直接看客户端的搭建。 |
||||
|
||||
先来说下什么是第三方免费frp服务吧,它的存在使我们可以在没有公网IP服务器、并且运营商早已封杀80和443端口的情况下,爆露出我们的服务,笔者这个博客的搭建也是采用的这种形式。 |
||||
|
||||
在此推荐使用[SAKURA FRP](https://www.natfrp.com/),隧道限速10M足够使用,每月有5G的免费流量,关键是签到还送流量。 |
||||
|
||||
第一步先注册,然后到管理界面点击 服务->隧道列表。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/230859.webp"> |
||||
|
||||
然后点击创建隧道然后选择节点,这里的隧道可以理解为从公网IP到自己本地服务的一条线路。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/230938.webp"> |
||||
|
||||
隧道类型,一般建站的话直接选HTTP/HTTPS,其他的选TCP隧道。 |
||||
|
||||
如果是建站使用的话,还需要设置域名cname记录,如下图所示cn-cd-dx-1.natfrp.cloud是我需要设置的cname值(cname值需要到域名管理后台设置)。 |
||||
|
||||
注:如果选择国内节点,建站需要先进行备案。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/231015.webp"> |
||||
|
||||
记录配置文件,供后面客户端配置使用 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/231236.webp"> |
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/231109.webp"> |
||||
|
||||
配置文件有两种形式,其中第一种是采用SAKURA FRP官网提供的客户端来连接。第二种采用标准frp客户端进行访问,笔者一直采用第二种形式。 |
||||
|
||||
# 使用NAS创建frp客户端 |
||||
终于轮到NAS登场了,我采用docker-compose的方式安装frp。 |
||||
|
||||
在nas上合适的位置创建目录,并创建如下文件。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/231331.webp"> |
||||
|
||||
docker-compose.yml文件如下 |
||||
``` |
||||
--- |
||||
version: "3" |
||||
services: |
||||
natfrp: |
||||
image: oldiy/frpc |
||||
container_name: natfrp |
||||
environment: |
||||
- PUID=1000 |
||||
- PGID=1000 |
||||
- TZ=Asia/Shanghai |
||||
volumes: |
||||
- ./configs:/frp |
||||
restart: unless-stopped |
||||
networks: |
||||
default: |
||||
external: |
||||
name: docker_default |
||||
``` |
||||
frpc.ini文件如下 |
||||
``` |
||||
# 对应Centos搭建的frp服务 |
||||
[common] |
||||
server_addr = 服务器IP地址 |
||||
server_port = 7000 |
||||
auth_token = 123456 |
||||
[ssh] |
||||
local_ip = 192.168.31.206 |
||||
local_port = 22 |
||||
remote_port = 2200 |
||||
[http] |
||||
type = http |
||||
local_ip = 192.168.31.206 |
||||
local_port = 5000 |
||||
custom_domains = nas.example.com |
||||
[remote] |
||||
local_ip = 192.168.31.10 |
||||
local_port = 3389 |
||||
remote_port = 3389 |
||||
``` |
||||
``` |
||||
# 对应使用SAKURA FRP搭建的frp服务,也就是上文提示的记录配置文件 |
||||
[common] |
||||
user = xxxxxxxxxxxx |
||||
sakura_mode = true |
||||
use_recover = true |
||||
login_fail_exit = false |
||||
|
||||
protocol = tcp |
||||
tcp_mux = true |
||||
pool_count = 1 |
||||
|
||||
token = xxxxxxxxxxxx |
||||
server_addr = cn-cd-dx-1.natfrp.cloud |
||||
server_port = 7000 |
||||
|
||||
[blog_http] |
||||
# id = |
||||
type = http |
||||
local_ip = 192.168.31.206 |
||||
local_port = 80 |
||||
custom_domains = www.example.com |
||||
|
||||
[blog_https] |
||||
# id = |
||||
type = https |
||||
local_ip = 192.168.31.206 |
||||
local_port = 443 |
||||
custom_domains = www.example.com |
||||
``` |
||||
|
||||
启动docker |
||||
``` |
||||
cd natfrp |
||||
docker-compose up -d |
||||
docker-compose logs -f |
||||
``` |
||||
OK,可以了,使用www.example.com访问你的网站吧!!! |
||||
|
||||
文章有不清楚的可以在评论区留言,我会尽快回复. |
@ -0,0 +1,153 @@ |
||||
--- |
||||
title: 威联通NAS安装旁路由 |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
categories: NAS服务 |
||||
abbrlink: 34692 |
||||
date: 2022-02-26 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
>软路由的安装最好使用虚拟机,使用Docker安装的一个弊端就是无法使用旁路由来做代理。 在此也介绍虚拟机安装的方式 |
||||
|
||||
# 下载套件 |
||||
|
||||
首先要下载Virtualization Station虚拟化工作站 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/215518.webp"> |
||||
|
||||
# 下载OpenWRT |
||||
|
||||
2023-01-06精简版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleach-mini-20230106-openwrt-x86-64-generic-squashfs-combined-efi.vmdk) |
||||
2024-01-01精简版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleachwrt-mini-20240101-x86-64-generic-squashfs-combined-efi.vmdk) |
||||
|
||||
2023-01-06大而全版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleach-plus-20230106-openwrt-x86-64-generic-squashfs-combined-efi.vmdk) |
||||
2024-01-01大而全版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleachwrt-plus-20240101-x86-64-generic-squashfs-combined-efi.vmdk) |
||||
|
||||
|
||||
# 安装OpenWRT |
||||
|
||||
打开Virtualization Station虚拟化工作站后点击右上角导入→映像转换器。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/215807.webp"> |
||||
|
||||
上方选择直接解压出来的 img 映像文件,下方转换后的保存为止,直接导出到目录即可。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/215837.webp"> |
||||
|
||||
创建虚拟机,名称输入**【openwrt】,此时下方的操作系统会自动转换为【Linux】** |
||||
|
||||
描述这里可填可不填,我个人习惯将使用的 openwrt 简介填在这里,方便下面查看。 |
||||
|
||||
磁盘位置选择**【使用现有映像】**,选择之前转换好的的openwrt固件,点击确认。 |
||||
|
||||
其他没什么需要修改的,openwrt 对系统要求不高,1核1G足够了。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/215914.webp"> |
||||
|
||||
先不开机,在总览这里点击下方的设置按钮。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/215945.webp"> |
||||
|
||||
【设定】-【磁盘空间】,将磁盘界面改成 SATA,点击下方的**【套用】** |
||||
|
||||
后续如果还想调整,比如加大核心内存等都可以在这里更改。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/220026.webp"> |
||||
|
||||
设置完成,点击开机 |
||||
|
||||
等个十几秒,看到小窗口的代码不动了,点击小窗口进入代码页面 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/220057.webp"> |
||||
|
||||
# 设置网络 |
||||
|
||||
输入命令修改ip地址 |
||||
``` |
||||
vi etc/config/network |
||||
``` |
||||
VI编辑器简单实用方式:按 i 键进行修改,修改完后按Esc退出,接着输入命令 :wq ,保存刚才的修改 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212539.webp"> |
||||
|
||||
之后输入service network restart命令重启网络就可以了。 |
||||
|
||||
待重启之后,在浏览器输入刚才修改过的IP,就可以进入Openwrt的登陆界面了,OpenWRT的默认账号密码是root:password,进入系统后可以修改密码。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212805.webp"> |
||||
|
||||
# 配置OpenWRT |
||||
安装完成后,要想正常使用还需要再配置一下,找到左侧菜单 网络–>接口,选择LAN的接口进行修改 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212917.webp"> |
||||
|
||||
这里主要设置IPv4地址、IPv4网关和自定义DNS服务器。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212953.webp"> |
||||
|
||||
同时在下方勾选忽略此接口,来禁用OpenWRT的DHCP功能,保存并应用就可以了。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213025.webp"> |
||||
|
||||
# OpenWRT常用功能 |
||||
## 拨号上网 |
||||
默认情况下,运营商提供的光猫是不支持DDNS和端口转发等功能的,那么我们就需要使用OpenWRT来进行拨号上网,并在里面进行DDNS和端口转发等功能。 |
||||
|
||||
首先来看下网络组成情况。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213121.webp"> |
||||
|
||||
我们需要将光猫改成桥接模式(可以找维修师傅远程处理即可),然后在OpenWRT里进行如下操作: |
||||
|
||||
点击网络–>接口–>LAN–>修改–>物理设置,取消桥接接口选项。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213151.webp"> |
||||
|
||||
在网络–>接口中添加新接口,如图所示进行设置,点击提交。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213304.webp"> |
||||
|
||||
修改WAN口,在基础设置中输入拨号账号和密码即可。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213741.webp"> |
||||
|
||||
PC上网时,将默认网关设置成旁路由的IP地址(我这里是192.168.31.2)。 |
||||
|
||||
## 端口转发 |
||||
点击网络–>防火墙–>端口转发来进行端口转发的设置。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213816.webp"> |
||||
|
||||
## 设置DDNS |
||||
首先我们要了解DDNS的原理。 |
||||
|
||||
如图为PC正常上网的流程,PC根据域名到DNS服务器请求IP地址,DNS服务器根据A 记录查找IP地址并返回给PC,PC使用IP地址请求web服务。(当然这些流程对用户来说都是透明的) |
||||
|
||||
了解了这个流程我们再来说DDNS,DDNS其实就是动态的调整DNS服务器中的A记录,实现的前提就是域名服务商提供API来修改域名的A记录,我们在需要使用DDNS的地方通过脚本来获取可能动态会变动的公网IP,然后通过API告诉域名服务商修改A记录。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214355.webp"> |
||||
|
||||
我以DNSPOD为例进行演示,在DNSPOD的控制台找到域名解析,并添加一条A记录,这里我设置主机记录为home,IP地址先随便填写. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214525.webp"> |
||||
|
||||
在DNSPOD的控制台点击我的账号并选择我的密钥,查看对应的SecretId和SecretKey . |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214601.webp"> |
||||
|
||||
点击服务–>动态DDNS–>端口转发来进行端口转发的设置,在下方输入框输入DNSPOD并点击添加. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214645.webp"> |
||||
|
||||
勾选启用,查询主机名填写刚才添加记录的域名,DDNS服务提供商,选择dnspod.cn,域名填写刚才添加记录的域名,用户名和密码对应SecretId和SecretKey,然后点击保存并应用. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214726.webp"> |
||||
|
||||
当下图红线处出现域名和IP对应关系时,即为配置成功. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214756.webp"> |
||||
|
||||
## 其他功能 |
||||
OpenWRT是搭载在Linux系统之上的,里面的服务还有很多,比如frp内网穿透、DNS过滤器、广告过滤大师、Docker等,在此就不再一一列举了,感兴趣的可以在评论区留言共同交流. |
@ -0,0 +1,195 @@ |
||||
--- |
||||
title: 群晖NAS安装旁路由 |
||||
tags: |
||||
- 网络 |
||||
- NAS技术 |
||||
categories: NAS服务 |
||||
abbrlink: 43311 |
||||
date: 2022-02-25 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
>试想以下这样的场景,当我们了解了DDNS、端口转发等功能,却发现家里的路由器并不支持;或者当家里的路由器对于拦截广告等功能已经不堪重负时,我们应该怎么办? |
||||
|
||||
>最好的方式当然是换一台路由器,但是当我们手边刚好有一台NAS的时候,不妨使用NAS搭建旁路由(软路由)的方式来解决这些问题。 |
||||
|
||||
# NAS安装旁路由的两种方式 |
||||
网上流传比较多的有两种方式: |
||||
|
||||
>通过NAS自带的虚拟机安装 |
||||
|
||||
>通过Docker安装。 |
||||
|
||||
笔者在当时安装的时候,刚开始是使用Docker的方式,中间也对网络也进行了各种设置,但是让我崩溃的是,安装完后对笔者的某些应用有限制。不得已只好放弃,转而采用虚拟机进行安装。建议在有NAS的时候采用虚拟机的方式安装,而在无法提供虚拟机的时候再采用Docker的方式。 |
||||
|
||||
# 安装虚拟机VMM |
||||
在这里使用群晖NAS进行安装,其他NAS流程是类似的。 |
||||
|
||||
VMM是虚拟机程序Virtual Machine Manager的简称,群晖套件中心就有,打开套件中心,搜索Virtual Machine Manager,第一个就是,点击安装 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/204800.webp"> |
||||
|
||||
# 主板开启虚拟化 |
||||
进入群晖系统–>控制面板–>网络–>选中“局域网”–>点击“管理”,在下拉菜单中选择“Open vSwitch 设置” |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/205037.webp"> |
||||
|
||||
在弹出界面中选中“启用” |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/205222.webp"> |
||||
|
||||
# 下载OpenWRT |
||||
|
||||
2023-01-06精简版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleach-mini-20230106-openwrt-x86-64-generic-squashfs-combined-efi.img.gz) |
||||
2024-01-01精简版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleachwrt-mini-20240101-x86-64-generic-squashfs-combined-efi.img.gz) |
||||
|
||||
2023-01-06大而全版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleach-plus-20230106-openwrt-x86-64-generic-squashfs-combined-efi.img.gz) |
||||
2024-01-01大而全版[下载](https://template-mine.oss-cn-beijing.aliyuncs.com/NAS%E4%B8%8B%E8%BD%BD/%E6%97%81%E8%B7%AF%E7%94%B1/bleachwrt-plus-20240101-x86-64-generic-squashfs-combined-efi.img.gz) |
||||
|
||||
# 安装OpenWRT |
||||
解压刚才下载的文件得到.img后缀的文件,然后打开群晖内的Virtual Machine Manager,选择映像–>新增–>从PC上传文件并上传刚才解压出来的img文件,之后点击下一步。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211106.webp"> |
||||
|
||||
勾选存储空间并点击应用 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211251.webp"> |
||||
|
||||
状态处显示良好,表明此次上传没有问题。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211532.webp"> |
||||
|
||||
上传完映像之后,我们点击网络–>新增,为网络命名为ETH1,勾选连接网线的接口(一般局域网1就是),随后应用。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211615.webp"> |
||||
|
||||
之后我们点击虚拟机,导入刚才的镜像。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211706.webp"> |
||||
|
||||
从硬盘映像导入,选择刚才上传的映像,然后点击下一步 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211748.webp"> |
||||
|
||||
选择存储空间后继续下一步 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211824.webp"> |
||||
|
||||
为虚拟机命名,选择CPU和内存容量,继续下一步。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/211921.webp"> |
||||
|
||||
设置虚拟盘大小,其他默认,下一步。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212006.webp"> |
||||
|
||||
网络选择ETH1并设置,在里面的型号中选择e1000。(不选也可以使用,但网络为半双工的,改为e1000后,网络变成全双工)。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212057.webp"> |
||||
|
||||
自动启动选择是,其他默认,下一步 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212134.webp"> |
||||
|
||||
设置管理员权限,只勾选admin,下一步 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212236.webp"> |
||||
|
||||
勾选创建后启动虚拟机,随后应用。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212347.webp"> |
||||
|
||||
至此,虚拟机已经安装成功了。但是openwrt的默认ip地址是192.168.1.1,而我家里的路由器IP地址是192.168.31.1,需要将openwrt的IP地址设置成192.168.31.X,在此我设置成192.168.31.2(需要和主路由一个网段)。 |
||||
|
||||
如下图,选择旁路由后点击连接,在弹出的标签页进入openwrt的终端。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212432.webp"> |
||||
|
||||
# 设置网络 |
||||
输入命令修改ip地址 |
||||
``` |
||||
vi etc/config/network |
||||
``` |
||||
VI编辑器简单实用方式:按 i 键进行修改,修改完后按Esc退出,接着输入命令 :wq ,保存刚才的修改 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212539.webp"> |
||||
|
||||
之后输入service network restart命令重启网络就可以了。 |
||||
|
||||
待重启之后,在浏览器输入刚才修改过的IP,就可以进入Openwrt的登陆界面了,OpenWRT的默认账号密码是root:password,进入系统后可以修改密码。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212805.webp"> |
||||
|
||||
# 配置OpenWRT |
||||
安装完成后,要想正常使用还需要再配置一下,找到左侧菜单 网络–>接口,选择LAN的接口进行修改 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212917.webp"> |
||||
|
||||
这里主要设置IPv4地址、IPv4网关和自定义DNS服务器。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/212953.webp"> |
||||
|
||||
同时在下方勾选忽略此接口,来禁用OpenWRT的DHCP功能,保存并应用就可以了。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213025.webp"> |
||||
|
||||
# OpenWRT常用功能 |
||||
## 拨号上网 |
||||
默认情况下,运营商提供的光猫是不支持DDNS和端口转发等功能的,那么我们就需要使用OpenWRT来进行拨号上网,并在里面进行DDNS和端口转发等功能。 |
||||
|
||||
首先来看下网络组成情况。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213121.webp"> |
||||
|
||||
我们需要将光猫改成桥接模式(可以找维修师傅远程处理即可),然后在OpenWRT里进行如下操作: |
||||
|
||||
点击网络–>接口–>LAN–>修改–>物理设置,取消桥接接口选项。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213151.webp"> |
||||
|
||||
在网络–>接口中添加新接口,如图所示进行设置,点击提交。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213304.webp"> |
||||
|
||||
修改WAN口,在基础设置中输入拨号账号和密码即可。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213741.webp"> |
||||
|
||||
PC上网时,将默认网关设置成旁路由的IP地址(我这里是192.168.31.2)。 |
||||
|
||||
## 端口转发 |
||||
点击网络–>防火墙–>端口转发来进行端口转发的设置。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/213816.webp"> |
||||
|
||||
## 设置DDNS |
||||
首先我们要了解DDNS的原理。 |
||||
|
||||
如图为PC正常上网的流程,PC根据域名到DNS服务器请求IP地址,DNS服务器根据A 记录查找IP地址并返回给PC,PC使用IP地址请求web服务。(当然这些流程对用户来说都是透明的) |
||||
|
||||
了解了这个流程我们再来说DDNS,DDNS其实就是动态的调整DNS服务器中的A记录,实现的前提就是域名服务商提供API来修改域名的A记录,我们在需要使用DDNS的地方通过脚本来获取可能动态会变动的公网IP,然后通过API告诉域名服务商修改A记录。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214355.webp"> |
||||
|
||||
我以DNSPOD为例进行演示,在DNSPOD的控制台找到域名解析,并添加一条A记录,这里我设置主机记录为home,IP地址先随便填写. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214525.webp"> |
||||
|
||||
在DNSPOD的控制台点击我的账号并选择我的密钥,查看对应的SecretId和SecretKey . |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214601.webp"> |
||||
|
||||
点击服务–>动态DDNS–>端口转发来进行端口转发的设置,在下方输入框输入DNSPOD并点击添加. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214645.webp"> |
||||
|
||||
勾选启用,查询主机名填写刚才添加记录的域名,DDNS服务提供商,选择dnspod.cn,域名填写刚才添加记录的域名,用户名和密码对应SecretId和SecretKey,然后点击保存并应用. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214726.webp"> |
||||
|
||||
当下图红线处出现域名和IP对应关系时,即为配置成功. |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/22/214756.webp"> |
||||
|
||||
## 其他功能 |
||||
OpenWRT是搭载在Linux系统之上的,里面的服务还有很多,比如frp内网穿透、DNS过滤器、广告过滤大师、Docker等,在此就不再一一列举了,感兴趣的可以在评论区留言共同交流. |
@ -0,0 +1,35 @@ |
||||
--- |
||||
title: 什么是DDNS |
||||
tags: |
||||
- 网络 |
||||
categories: 网络基础 |
||||
abbrlink: 42499 |
||||
date: 2022-02-22 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
# 什么是DDNS |
||||
动态 DNS(DDNS)是一项在 IP 地址发生变化时可以自动更新 DNS 记录的服务。域名将网络 IP 地址转换为人类可读的名称,便于识别和使用。将名称映射到 IP 地址的信息以表格形式记录在 DNS 服务器上。但是,网络管理员会动态分配 IP 地址并经常更改。每当 IP 地址发生变化时,DDNS 服务都会更新 DNS 服务器记录。借助 DDNS,域名管理变得更容易、更高效。 |
||||
|
||||
# 为什么要使用动态DNS |
||||
过去,IP 地址是静态的,很少更改。但是,由于互联网规模不断扩大以及服务器、智能传感器和最终用户设备数量大幅增加,IP 地址出现短缺。 |
||||
|
||||
# 动态DNS运作机制 |
||||
组织通常会订阅由 DDNS 提供商提供的动态 DNS(DDNS)服务。提供商还维护用于处理关联域名的 DNS 记录的 DNS 服务器。以下是一般步骤: |
||||
|
||||
1. 向动态 DNS 服务提供商注册域名并配置 DNS 设置 |
||||
2. 向提供商提供域名的初始 IP 地址 |
||||
3. 使用更改的 IP 地址在设备或服务器实例上安装动态 DNS 客户端 |
||||
|
||||
DDNS 客户端持续监控 IP 地址并检测任何更改。客户端向动态 DNS 提供商发送 DNS 记录更新通知,通知其新的 IP 地址。动态 DNS 提供商修改记录以指向新 IP 地址。 |
||||
|
||||
动态 DNS 客户端会继续监控 IP 地址以了解进一步的更改。每当发生新的更改时,该过程都会重复。 |
||||
|
||||
# DNS和动态DNS的差异 |
||||
DNS 服务是一种全球分布式服务,可将人类可读的名称转换为数字 IP 地址。DNS 服务器将域名请求转换为 IP 地址。这些地址控制最终用户在 Web 浏览器中键入域名时将访问哪个服务器。 |
||||
|
||||
动态 DNS(DDNS)是 DNS 的扩展,可自动实时更新与域名关联的 IP 地址。它扩展 DNS 的功能。借助 DDNS,即使在动态 IP 地址环境中,组织和个人也可以保持连接和可访问性。 |
||||
|
||||
DNS 得到所有 DNS 服务器的普遍支持,在全球范围内用于将域名解析为 IP 地址。 |
||||
|
||||
另一方面,DDNS 需要特定 DDNS 提供商的支持。组织必须订阅 DDNS 服务并将其设备或路由器配置为与所选 DDNS 提供商合作。 |
@ -0,0 +1,27 @@ |
||||
--- |
||||
title: 什么是DNS |
||||
tags: |
||||
- 网络 |
||||
categories: 网络基础 |
||||
abbrlink: 27420 |
||||
date: 2022-01-22 11:02:22 |
||||
keywords: |
||||
description: |
||||
--- |
||||
# DNS 基础知识 |
||||
Internet 上的所有计算机,从智能手机或笔记本电脑到可提供大量零售网站内容的服务器,均通过使用编号寻找另一方并相互通信。这些编号称为 IP 地址。当我们打开 Web 浏览器并前往一个网站时,不必记住和输入长编号。而是输入域名(如 example.com),然后在正确的位置结束。 |
||||
|
||||
# DNS 将流量路由到 Web 应用程序 |
||||
下图概述了 DNS 服务如何协同工作以将终端用户路由到您的网站或应用程序。 |
||||
|
||||
<img src="https://img.dreamlyn.cn:8443/i/2024/02/23/112256.webp"> |
||||
|
||||
1. 用户打开 Web 浏览器,在地址栏中输入 www.example.com,然后按 Enter 键。 |
||||
2. www.example.com 的请求被路由到 DNS 解析程序,这一般由用户的互联网服务提供商 (ISP) 进行管理,例如有线 Internet 服务提供商、DSL 宽带提供商或公司网络。 |
||||
3. ISP 的 DNS 解析程序将 www.example.com 的请求转发到 DNS 根名称服务器。 |
||||
4. ISP 的 DNS 解析程序再次转发 www.example.com 的请求,这次转发到 .com 域的一个 TLD 名称服务器。.com 域的名称服务器使用与 example.com 域相关的四个 Amazon Route 53 名称服务器的名称来响应该请求。 |
||||
5. ISP 的 DNS 解析程序选择一个 Amazon Route 53 名称服务器,并将 www.example.com 的请求转发到该名称服务器。 |
||||
6. Amazon Route 53 名称服务器在 example.com 托管区域中查找 www.example.com 记录,获得相关值,例如,Web 服务器的 IP 地址 (192.0.2.44),并将 IP 地址返回至 DNS 解析程序。 |
||||
7. ISP 的 DNS 解析程序最终获得用户需要的 IP 地址。解析程序将此值返回至 Web 浏览器。DNS 解析程序还会将 example.com 的 IP 地址缓存 (存储) 您指定的时长,以便它能够在下次有人浏览 example.com 时更快地作出响应。有关更多信息,请参阅存活期 (TTL)。 |
||||
8. Web 浏览器将 www.example.com 的请求发送到从 DNS 解析程序中获得的 IP 地址。这是您的内容所处位置,例如,在 Amazon EC2 实例中或配置为网站端点的 Amazon S3 存储桶中运行的 Web 服务器。 |
||||
9. 192.0.2.44 上的 Web 服务器或其他资源将 www.example.com 的 Web 页面返回到 Web 浏览器,且 Web 浏览器会显示该页面。 |
@ -0,0 +1,4 @@ |
||||
--- |
||||
title: 关于作者 |
||||
date: 2024-02-22 10:54:11 |
||||
--- |
@ -0,0 +1,4 @@ |
||||
--- |
||||
title: 留言板 |
||||
date: 2024-02-23 15:30:52 |
||||
--- |
@ -0,0 +1,207 @@ |
||||
--- |
||||
title: B站字幕转换 |
||||
date: 2022-02-22 10:34:50 |
||||
toc: false |
||||
--- |
||||
{% raw %} |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta http-equiv="Pragma" content="no-cache"> |
||||
<meta http-equiv="Cache-Control" content="no-cache"> |
||||
<meta http-equiv="Expires" content="0"> |
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> |
||||
<!-- import Vue before Element --> |
||||
<script src="https://unpkg.com/vue@2/dist/vue.js"></script> |
||||
<!-- import JavaScript --> |
||||
<script src="https://unpkg.com/element-ui/lib/index.js"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"> |
||||
<div class="tools-upload"> |
||||
<el-upload action="#" :http-request="requestUpload" :show-file-list="false"> |
||||
<el-button size="mini" type="primary">上传</el-button> |
||||
</el-upload> |
||||
<div class="tools-right"> |
||||
<div class="tool-item"> |
||||
文件编码: |
||||
<el-select size="mini" v-model="fileEncoder" placeholder="文件编码"> |
||||
<el-option label="GBK" value="GBK"></el-option> |
||||
<el-option label="UTF-8" value="UTF-8"></el-option> |
||||
<el-option label="GB2312" value="GB2312""></el-option> |
||||
</el-select> |
||||
</div> |
||||
<div class="tool-item"> |
||||
换行符: |
||||
<el-select size="mini" v-model="newline" placeholder="换行符"> |
||||
<el-option label="\r\n (Windows)" value="windows"></el-option> |
||||
<el-option label="\n (Linux)" value="linux"></el-option> |
||||
</el-select> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<el-input type="textarea" placeholder="请上传或输入字幕文件" v-model="originText" rows="15" show-word-limit=""> |
||||
</el-input> |
||||
<div class="tools-btn"> |
||||
<el-button size="mini" @click="generateSrt">生成SRT文件</el-button> |
||||
<el-button size="mini" @click="generateTXT">生成TXT文件</el-button> |
||||
<el-button size="mini" @click="clear">清空</el-button> |
||||
</div> |
||||
<el-input type="textarea" placeholder="等待生成SRT文件" v-model="srtText" rows="15" show-word-limit=""> |
||||
</el-input> |
||||
<el-button type="text" @click="saveSrt">保存到本地</el-button> |
||||
|
||||
<h3>字幕提取方法参考下面的视频</h3> |
||||
|
||||
<div style="position: relative; padding: 30% 50%;"> |
||||
<iframe src="//player.bilibili.com/player.html?aid=863543343&bvid=BV16G4y1S7Hp&cid=985575449&page=1&as_wide=1&high_quality=1&danmaku=1" |
||||
scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" |
||||
style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"></iframe> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
<script> |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
data: function () { |
||||
return { |
||||
fileEncoder: "UTF-8", |
||||
newline: "windows", |
||||
originText: "", |
||||
srtText: "", |
||||
fileName: "" |
||||
} |
||||
}, |
||||
methods: { |
||||
// 覆盖默认的上传行为 |
||||
requestUpload(content) { |
||||
let file = content.file |
||||
let index = file.name.lastIndexOf('.') |
||||
this.fileName = file.name.substring(0, index) |
||||
// 新建一个FileReader |
||||
const reader = new FileReader() |
||||
// 读取文件 |
||||
reader.readAsText(file, this.fileEncoder) |
||||
// 读取完文件之后会回来这里 |
||||
reader.onload = (e) => { |
||||
// 读取文件内容 |
||||
const fileString = e.target.result |
||||
// 接下来可对文件内容进行处理 |
||||
this.originText = fileString |
||||
} |
||||
}, |
||||
generateSrt() { |
||||
this.srtText = "" |
||||
let originObj = JSON.parse(this.originText) |
||||
let body = originObj.body |
||||
let lastTo = null |
||||
for (let index = 0; index < body.length; index++) { |
||||
let line = body[index] |
||||
this.srtText += (index + (this.newline == 'windows' ? '\r\n' : '\n')) |
||||
let from = line.from |
||||
if (lastTo) { |
||||
let timeA = (from + '').split("."); |
||||
let timeB = (lastTo + '').split("."); //上次的时间 |
||||
if (parseInt(timeB[0]) >= parseInt(timeA[0])) { |
||||
timeA[0] = timeB[0]; |
||||
if (timeB[1]) { |
||||
if (parseInt(timeB[1]) >= parseInt(timeA[1])) { |
||||
timeA[1] = timeB[1]; |
||||
} |
||||
} |
||||
from = (timeA[1] ? (timeA[0] + '.' + timeA[1]) : timeA[0]) |
||||
} |
||||
} |
||||
this.srtText += (this.deelTime(from) + " --> ") |
||||
let to = line.to |
||||
lastTo = to |
||||
this.srtText += (this.deelTime(to) + (this.newline == 'windows' ? '\r\n' : '\n')) |
||||
this.srtText += line.content |
||||
this.srtText += (this.newline == 'windows' ? '\r\n' : '\n') |
||||
this.srtText += (this.newline == 'windows' ? '\r\n' : '\n') |
||||
} |
||||
}, |
||||
generateTXT() { |
||||
this.srtText = "" |
||||
let originObj = JSON.parse(this.originText) |
||||
let body = originObj.body |
||||
let lastTo = null |
||||
for (let index = 0; index < body.length; index++) { |
||||
let line = body[index] |
||||
let content = line.content |
||||
this.srtText += content |
||||
this.srtText += (this.newline == 'windows' ? '\r\n' : '\n') |
||||
} |
||||
}, |
||||
deelTime(origin) { |
||||
let timeA = (origin + '').split("."); |
||||
let tranTime = this.sumSecondToTime(timeA[0]); |
||||
return tranTime + "," + (timeA.length < 2 ? '000' : timeA[1].padStart(3, '0')); |
||||
}, |
||||
|
||||
sumSecondToTime(sumSecond) { |
||||
if (sumSecond <= 0) { |
||||
return "00:00:00"; |
||||
} |
||||
let h = parseInt(sumSecond / 3600); |
||||
let m = parseInt((sumSecond - h * 3600) / 60); |
||||
let s = sumSecond - h * 3600 - m * 60; |
||||
let time = (h + '').padStart(2, '0') + ':' + (m + '').padStart(2, '0') + ':' + (s + '').padStart(2, |
||||
'0'); |
||||
return time; |
||||
}, |
||||
clear() { |
||||
this.originText = "" |
||||
}, |
||||
saveSrt() { |
||||
// const file = new Blob(this.srtText, { |
||||
// type: 'text/plain' |
||||
// }); |
||||
// a.href = URL.createObjectURL(file); |
||||
// a.download = name; |
||||
|
||||
var element = document.createElement('a'); |
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.srtText)); |
||||
if (this.fileName != '') { |
||||
element.setAttribute('download', this.fileName + '.srt'); |
||||
} else { |
||||
element.setAttribute('download', 'translated.srt'); |
||||
} |
||||
|
||||
element.style.display = 'none'; |
||||
document.body.appendChild(element); |
||||
element.click(); |
||||
document.body.removeChild(element); |
||||
} |
||||
} |
||||
}) |
||||
|
||||
</script> |
||||
<style> |
||||
.tools-upload { |
||||
margin: 10px 0; |
||||
display: flex; |
||||
flex-direction: row; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
} |
||||
|
||||
.tools-right { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
font-size: 15px; |
||||
} |
||||
|
||||
.tool-item { |
||||
margin-left: 10px; |
||||
} |
||||
|
||||
.tools-btn { |
||||
margin: 10px 0; |
||||
} |
||||
</style> |
||||
</html> |
||||
{% endraw %} |
@ -0,0 +1,6 @@ |
||||
--- |
||||
title: 友情链接 |
||||
date: 2022-02-22 10:34:50 |
||||
type: 'link' |
||||
random: true |
||||
--- |
@ -0,0 +1,6 @@ |
||||
--- |
||||
title: 标签 |
||||
date: 2022-02-22 09:57:47 |
||||
type: "tags" |
||||
comments: false |
||||
--- |
@ -0,0 +1,13 @@ |
||||
# These are supported funding model platforms |
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] |
||||
patreon: # Replace with a single Patreon username |
||||
open_collective: # Replace with a single Open Collective username |
||||
ko_fi: # Replace with a single Ko-fi username |
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel |
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry |
||||
liberapay: # Replace with a single Liberapay username |
||||
issuehunt: # Replace with a single IssueHunt username |
||||
otechie: # Replace with a single Otechie username |
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry |
||||
custom: ['https://buy.stripe.com/3cs6rP6YA91sbbG5kk','https://jsd.012700.xyz/gh/jerryc127/CDN/Photo/wechat.jpg'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] |
@ -0,0 +1,82 @@ |
||||
name: Bug report |
||||
description: Create a report to help us improve |
||||
title: '[Bug]: ' |
||||
|
||||
body: |
||||
- type: markdown |
||||
attributes: |
||||
value: | |
||||
重要:請依照該模板來提交 |
||||
Please follow the template to create a new issue |
||||
- type: input |
||||
id: butterfly-ver |
||||
attributes: |
||||
label: 使用的 Butterfly 版本? | What version of Butterfly are you use? |
||||
description: 檢視主題的 package.json | Check the theme's package.json |
||||
validations: |
||||
required: true |
||||
|
||||
- type: dropdown |
||||
id: modify |
||||
attributes: |
||||
label: 是否修改过主题文件? || Has the theme files been modified? |
||||
options: |
||||
- 是 (Yes) |
||||
- 不是 (No) |
||||
validations: |
||||
required: true |
||||
|
||||
- type: dropdown |
||||
id: browser |
||||
attributes: |
||||
label: 使用的瀏覽器? || What browse are you using? |
||||
options: |
||||
- Chrome |
||||
- Edge |
||||
- Safari |
||||
- Opera |
||||
- Other |
||||
validations: |
||||
required: true |
||||
|
||||
- type: dropdown |
||||
id: platform |
||||
attributes: |
||||
label: 使用的系統? || What operating system are you using? |
||||
options: |
||||
- Windows |
||||
- macOS |
||||
- Linux |
||||
- Android |
||||
- iOS |
||||
- Other |
||||
validations: |
||||
required: true |
||||
|
||||
- type: textarea |
||||
id: dependencies |
||||
attributes: |
||||
label: 依賴插件 | Package dependencies Information |
||||
description: 在 Hexo 根目錄下執行`npm ls --depth 0` | Run `npm ls --depth 0` in Hexo root directory |
||||
render: Text |
||||
validations: |
||||
required: true |
||||
|
||||
- type: textarea |
||||
id: description |
||||
attributes: |
||||
label: 問題描述 | Describe the bug |
||||
description: 請描述你的問題現象 | A clear and concise description of what the bug is. |
||||
placeholder: 請儘量提供截圖來定位問題 | If applicable, add screenshots to help explain your problem |
||||
value: |
||||
validations: |
||||
required: true |
||||
|
||||
- type: input |
||||
id: website |
||||
attributes: |
||||
label: 出現問題網站 | Website |
||||
description: 請提供下可復現網站地址 | Please supply a website url which can reproduce problem. |
||||
placeholder: |
||||
validations: |
||||
required: true |
@ -0,0 +1,18 @@ |
||||
blank_issues_enabled: false |
||||
contact_links: |
||||
- name: Questions about Butterfly |
||||
url: https://github.com/jerryc127/hexo-theme-butterfly/discussions |
||||
about: 一些使用問題請到 Discussion 詢問。 Please ask questions in Discussion. |
||||
|
||||
- name: Butterfly Q&A |
||||
url: https://butterfly.js.org/posts/98d20436/ |
||||
about: Butterfly Q&A |
||||
|
||||
- name: Telegram |
||||
url: https://t.me/bu2fly |
||||
about: 'Official Telegram Group' |
||||
|
||||
- name: QQ 群 |
||||
url: https://jq.qq.com/?_wv=1027&k=KU9105XR |
||||
about: '群號 1070540070' |
||||
|
@ -0,0 +1,14 @@ |
||||
name: Feature request |
||||
description: Suggest an idea for this project |
||||
title: '[Feature]: ' |
||||
|
||||
body: |
||||
- type: textarea |
||||
id: feature-request |
||||
attributes: |
||||
label: 想要的功能 | What feature do you want? |
||||
description: 請描述你需要的新功能 | A clear and concise description of what the feature is. |
||||
placeholder: |
||||
value: |
||||
validations: |
||||
require: true |
@ -0,0 +1,19 @@ |
||||
name: npm publish |
||||
|
||||
on: |
||||
release: |
||||
types: [created] |
||||
jobs: |
||||
build: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- uses: actions/checkout@v2 |
||||
# Setup .npmrc file to publish to npm |
||||
- uses: actions/setup-node@v1 |
||||
with: |
||||
node-version: '12.x' |
||||
registry-url: 'https://registry.npmjs.org' |
||||
- run: npm install |
||||
- run: npm publish |
||||
env: |
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} |
@ -0,0 +1,19 @@ |
||||
name: 'Close stale issues and PRs' |
||||
on: |
||||
schedule: |
||||
- cron: '30 1 * * *' |
||||
|
||||
jobs: |
||||
stale: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- uses: actions/stale@v5 |
||||
with: |
||||
days-before-issue-stale: 30 |
||||
days-before-pr-stale: -1 |
||||
days-before-close: 7 |
||||
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' |
||||
close-pr-message: 'This issue has not seen any activity since it was marked stale. Closing.' |
||||
stale-issue-label: 'Stale' |
||||
exempt-issue-labels: 'pinned,bug,enhancement,documentation,Plan' |
||||
operations-per-run: 1000 |
@ -0,0 +1,202 @@ |
||||
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
@ -0,0 +1,111 @@ |
||||
<div align="right"> |
||||
<a title="Chinese" href="/README_CN.md">中文</a> |
||||
</div> |
||||
|
||||
# hexo-theme-butterfly |
||||
|
||||
![master version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/master?color=%231ab1ad&label=master) |
||||
![master version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/dev?label=dev) |
||||
![https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff](https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff) |
||||
![hexo version](https://img.shields.io/badge/hexo-5.3.0+-0e83c) |
||||
![license](https://img.shields.io/github/license/jerryc127/hexo-theme-butterfly?color=FF5531) |
||||
|
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/theme-butterfly-readme.png) |
||||
|
||||
📢 Demo: [Butterfly](https://butterfly.js.org/) || [CrazyWong](https://blog.crazywong.com/) |
||||
|
||||
📖 Docs: [English](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/) || [Chinese](https://butterfly.js.org/posts/21cfbf15/) |
||||
|
||||
Based on [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody) theme. |
||||
|
||||
## 💻 Installation |
||||
|
||||
### GIT |
||||
|
||||
> If you are in Mainland China, you can download in [Gitee](https://gitee.com/immyw/hexo-theme-butterfly.git) |
||||
|
||||
Stable branch [recommend]: |
||||
|
||||
``` |
||||
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly |
||||
``` |
||||
|
||||
Dev branch: |
||||
|
||||
``` |
||||
git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly |
||||
``` |
||||
|
||||
### NPM |
||||
|
||||
> It supports Hexo 5.0.0 or later |
||||
|
||||
In Hexo site root directory |
||||
|
||||
```powershell |
||||
npm i hexo-theme-butterfly |
||||
``` |
||||
|
||||
## ⚙ Configuration |
||||
|
||||
Set theme in the hexo work folder's root config file `_config.yml`: |
||||
|
||||
> theme: butterfly |
||||
|
||||
If you don't have pug & stylus renderer, try this: |
||||
|
||||
> npm install hexo-renderer-pug hexo-renderer-stylus |
||||
|
||||
## 🎉 Features |
||||
|
||||
- [x] Card UI Design |
||||
- [X] Support sub-menu |
||||
- [x] Two-column layout |
||||
- [x] Responsive Web Design |
||||
- [x] Dark Mode |
||||
- [x] Pjax |
||||
- [x] Read Mode |
||||
- [x] Conversion between Traditional and Simplified Chinese |
||||
- [X] TOC catalog is available for both computers and mobile phones |
||||
- [X] Built-in Syntax Highlighting Themes (darker/pale night/light/ocean/mac/mac light), also support customization |
||||
- [X] Code Blocks (Display code language/close or expand Code Blocks/Copy Button/word wrap) |
||||
- [X] Disable copy/Add a Copyright Notice to the Copied Text |
||||
- [X] Search (Algolia Search/Local Search) |
||||
- [x] Mathjax and Katex |
||||
- [x] Built-in 404 page |
||||
- [x] WordCount |
||||
- [x] Related articles |
||||
- [x] Displays outdated notice for a post |
||||
- [x] Share (Sharejs/Addtoany) |
||||
- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk) |
||||
- [x] Multiple Comment System Support |
||||
- [x] Online Chats (Chatra/Tidio/Daovoice/Crisp/messenger) |
||||
- [x] Web analytics |
||||
- [x] Google AdSense |
||||
- [x] Webmaster Verification |
||||
- [x] Change website colour scheme |
||||
- [x] Typewriter Effect: activate_power_mode |
||||
- [x] Background effects (Canvas ribbon/canvas_ribbon_piao/canvas_nest) |
||||
- [x] Mouse click effects (Fireworks/Heart/Text) |
||||
- [x] Preloader/Loading Animation/pace.js |
||||
- [x] Busuanzi visitor counter |
||||
- [x] Medium Zoom/Fancybox |
||||
- [x] Mermaid |
||||
- [x] Justified Gallery |
||||
- [x] Lazyload images |
||||
- [x] Instantpage/Pangu/Snackbar notification toast/PWA...... |
||||
|
||||
## ✨ Contributors |
||||
|
||||
<a href="https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors"> |
||||
<img src="https://contrib.rocks/image?repo=jerryc127/hexo-theme-butterfly" /> |
||||
</a> |
||||
|
||||
## 📷 Screenshots |
||||
|
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-1.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-2.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-3.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-4.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-1.png) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-2.png) |
@ -0,0 +1,111 @@ |
||||
<div align="right"> |
||||
<a title="English" href="/README.md">English</a> |
||||
</div> |
||||
|
||||
# hexo-theme-butterfly |
||||
|
||||
![master version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/master?color=%231ab1ad&label=master) |
||||
![master version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/dev?label=dev) |
||||
![https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff](https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff) |
||||
![hexo version](https://img.shields.io/badge/hexo-5.3.0+-0e83c) |
||||
![license](https://img.shields.io/github/license/jerryc127/hexo-theme-butterfly?color=FF5531) |
||||
|
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/theme-butterfly-readme.png) |
||||
|
||||
📢 預覽: [Butterfly](https://butterfly.js.org/) || [CrazyWong](https://blog.crazywong.com/) |
||||
|
||||
📖 文檔: [中文](https://butterfly.js.org/posts/21cfbf15/) || [English](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/) |
||||
|
||||
一款基於[hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody)修改的主題 |
||||
|
||||
## 💻 安裝 |
||||
|
||||
### Git 安裝 |
||||
|
||||
> 本倉庫同時上傳到 [Gitee](https://gitee.com/immyw/hexo-theme-butterfly.git),如果你訪問 Github 緩慢,可從 Gitee 中下載。 |
||||
|
||||
在博客根目錄裡安裝穩定版【推薦】 |
||||
|
||||
```powershell |
||||
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly |
||||
``` |
||||
|
||||
如果想要安裝比較新的dev分支,可以 |
||||
|
||||
```powershell |
||||
git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly |
||||
``` |
||||
|
||||
### npm 安裝 |
||||
|
||||
> 此方法只支持Hexo 5.0.0以上版本 |
||||
|
||||
在博客根目錄裡 |
||||
|
||||
```powershell |
||||
npm i hexo-theme-butterfly |
||||
``` |
||||
|
||||
## ⚙ 應用主題 |
||||
|
||||
修改hexo配置文件`_config.yml`,把主題改為`Butterfly` |
||||
|
||||
``` |
||||
theme: butterfly |
||||
``` |
||||
|
||||
>如果你沒有pug以及stylus的渲染器,請下載安裝: npm install hexo-renderer-pug hexo-renderer-stylus --save |
||||
|
||||
## 🎉 特色 |
||||
|
||||
- [x] 卡片化設計 |
||||
- [X] 支持二級目錄 |
||||
- [x] 雙欄設計 |
||||
- [x] 響應式主題 |
||||
- [x] 夜間模式 |
||||
- [x] Pjax |
||||
- [x] 文章閲讀模式 |
||||
- [x] 簡體和繁體轉換 |
||||
- [X] 電腦和手機都可查看TOC目錄 |
||||
- [X] 內置多種代碼配色(darker/pale night/light/ocean/mac/mac light),可自定義代碼配色 |
||||
- [X] 代碼塊顯示代碼語言/關閉或展開代碼塊/代碼複製/代碼自動換行 |
||||
- [X] 可關閉文字複製/可開啟內容複製增加版權信息) |
||||
- [X] 兩種搜索( Algolia 搜索和本地搜索) |
||||
- [x] Mathjax 和 Katex |
||||
- [x] 內置404頁面 |
||||
- [x] 顯示字數統計 |
||||
- [x] 顯示相關文章 |
||||
- [x] 過期文章提醒 |
||||
- [x] 多種分享系統(Sharejs/Addtoany) |
||||
- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk) |
||||
- [x] 支持雙評論部署 |
||||
- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Crisp/messenger) |
||||
- [x] 多種分析系統 |
||||
- [x] 谷歌廣告/手動廣告位置 |
||||
- [x] 各種站長驗證 |
||||
- [x] 修改網站配色 |
||||
- [x] 打字特效 activate_power_mode |
||||
- [x] 多種背景特效(靜止彩帶/動態彩帶/Canvas Nest) |
||||
- [x] 多種鼠標點擊特效(煙花/文字/愛心) |
||||
- [x] 內置一種 Preloader 加載動畫和 pace.js 加載動畫條 |
||||
- [x] 不蒜子訪問統計 |
||||
- [x] 兩種大圖模式(Medium Zoom/Fancybox) |
||||
- [x] Mermaid 圖表顯示 |
||||
- [x] 照片牆 |
||||
- [x] 圖片懶加載 |
||||
- [x] Instantpage/Pangu/Snackbar彈窗/PWA...... |
||||
|
||||
## ✨ 貢獻者 |
||||
|
||||
<a href="https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors"> |
||||
<img src="https://contrib.rocks/image?repo=jerryc127/hexo-theme-butterfly" /> |
||||
</a> |
||||
|
||||
## 📷 截圖 |
||||
|
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-1.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-2.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-3.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-4.jpg) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-1.png) |
||||
![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-2.png) |
@ -0,0 +1,994 @@ |
||||
# Navigation bar settings (導航欄設置) |
||||
# see https://butterfly.js.org/posts/4aa8abbe/##導航欄設置-Navigation-bar-settings |
||||
# -------------------------------------- |
||||
|
||||
nav: |
||||
logo: # image |
||||
display_title: true |
||||
fixed: false # fixed navigation bar |
||||
|
||||
# Menu 目錄 |
||||
menu: |
||||
# Home: / || fas fa-home |
||||
# Archives: /archives/ || fas fa-archive |
||||
# Tags: /tags/ || fas fa-tags |
||||
# Categories: /categories/ || fas fa-folder-open |
||||
# List||fas fa-list: |
||||
# Music: /music/ || fas fa-music |
||||
# Movie: /movies/ || fas fa-video |
||||
# Link: /link/ || fas fa-link |
||||
# About: /about/ || fas fa-heart |
||||
|
||||
# Code Blocks (代碼相關) |
||||
# -------------------------------------- |
||||
|
||||
highlight_theme: light # darker / pale night / light / ocean / mac / mac light / false |
||||
highlight_copy: true # copy button |
||||
highlight_lang: true # show the code language |
||||
highlight_shrink: false # true: shrink the code blocks / false: expand the code blocks | none: expand code blocks and hide the button |
||||
highlight_height_limit: false # unit: px |
||||
code_word_wrap: false |
||||
|
||||
# Social Settings (社交圖標設置) |
||||
# formal: |
||||
# icon: link || the description || color |
||||
social: |
||||
# fab fa-github: https://github.com/xxxxx || Github || '#24292e' |
||||
# fas fa-envelope: mailto:xxxxxx@gmail.com || Email || '#4a7dbe' |
||||
|
||||
# Image (圖片設置) |
||||
# -------------------------------------- |
||||
|
||||
# Favicon(網站圖標) |
||||
favicon: /img/favicon.png |
||||
|
||||
# Avatar (頭像) |
||||
avatar: |
||||
img: https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png |
||||
effect: false |
||||
|
||||
# Disable all banner image |
||||
disable_top_img: false |
||||
|
||||
# The banner image of home page |
||||
index_img: |
||||
|
||||
# If the banner of page not setting, it will show the top_img |
||||
default_top_img: |
||||
|
||||
# The banner image of archive page |
||||
archive_img: |
||||
|
||||
# If the banner of tag page not setting, it will show the top_img |
||||
# note: tag page, not tags page (子標籤頁面的 top_img) |
||||
tag_img: |
||||
|
||||
# The banner image of tag page |
||||
# format: |
||||
# - tag name: xxxxx |
||||
tag_per_img: |
||||
|
||||
# If the banner of category page not setting, it will show the top_img |
||||
# note: category page, not categories page (子分類頁面的 top_img) |
||||
category_img: |
||||
|
||||
# The banner image of category page |
||||
# format: |
||||
# - category name: xxxxx |
||||
category_per_img: |
||||
|
||||
cover: |
||||
# display the cover or not (是否顯示文章封面) |
||||
index_enable: true |
||||
aside_enable: true |
||||
archives_enable: true |
||||
# the position of cover in home page (封面顯示的位置) |
||||
# left/right/both |
||||
position: both |
||||
# When cover is not set, the default cover is displayed (當沒有設置cover時,默認的封面顯示) |
||||
default_cover: |
||||
# - https://i.loli.net/2020/05/01/gkihqEjXxJ5UZ1C.jpg |
||||
|
||||
# Replace Broken Images (替換無法顯示的圖片) |
||||
error_img: |
||||
flink: /img/friend_404.gif |
||||
post_page: /img/404.jpg |
||||
|
||||
# A simple 404 page |
||||
error_404: |
||||
enable: false |
||||
subtitle: 'Page Not Found' |
||||
background: https://i.loli.net/2020/05/19/aKOcLiyPl2JQdFD.png |
||||
|
||||
post_meta: |
||||
page: # Home Page |
||||
date_type: created # created or updated or both 主頁文章日期是創建日或者更新日或都顯示 |
||||
date_format: date # date/relative 顯示日期還是相對日期 |
||||
categories: true # true or false 主頁是否顯示分類 |
||||
tags: false # true or false 主頁是否顯示標籤 |
||||
label: true # true or false 顯示描述性文字 |
||||
post: |
||||
date_type: both # created or updated or both 文章頁日期是創建日或者更新日或都顯示 |
||||
date_format: date # date/relative 顯示日期還是相對日期 |
||||
categories: true # true or false 文章頁是否顯示分類 |
||||
tags: true # true or false 文章頁是否顯示標籤 |
||||
label: true # true or false 顯示描述性文字 |
||||
|
||||
# Display the article introduction on homepage |
||||
# 1: description |
||||
# 2: both (if the description exists, it will show description, or show the auto_excerpt) |
||||
# 3: auto_excerpt (default) |
||||
# false: do not show the article introduction |
||||
index_post_content: |
||||
method: 3 |
||||
length: 500 # if you set method to 2 or 3, the length need to config |
||||
|
||||
# anchor |
||||
anchor: |
||||
# when you scroll, the URL will update according to header id. |
||||
auto_update: false |
||||
# Click the headline to scroll and update the anchor |
||||
click_to_scroll: false |
||||
|
||||
# figcaption (圖片描述文字) |
||||
photofigcaption: false |
||||
|
||||
# copy settings |
||||
# copyright: Add the copyright information after copied content (複製的內容後面加上版權信息) |
||||
copy: |
||||
enable: true |
||||
copyright: |
||||
enable: false |
||||
limit_count: 50 |
||||
|
||||
# Post |
||||
# -------------------------------------- |
||||
|
||||
# toc (目錄) |
||||
toc: |
||||
post: true |
||||
page: false |
||||
number: true |
||||
expand: false |
||||
style_simple: false # for post |
||||
scroll_percent: true |
||||
|
||||
post_copyright: |
||||
enable: true |
||||
decode: false |
||||
author_href: |
||||
license: CC BY-NC-SA 4.0 |
||||
license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/ |
||||
|
||||
# Sponsor/reward |
||||
reward: |
||||
enable: false |
||||
text: |
||||
QR_code: |
||||
# - img: /img/wechat.jpg |
||||
# link: |
||||
# text: wechat |
||||
# - img: /img/alipay.jpg |
||||
# link: |
||||
# text: alipay |
||||
|
||||
# Post edit |
||||
# Easily browse and edit blog source code online. |
||||
post_edit: |
||||
enable: false |
||||
# url: https://github.com/user-name/repo-name/edit/branch-name/subdirectory-name/ |
||||
# For example: https://github.com/jerryc127/butterfly.js.org/edit/main/source/ |
||||
url: |
||||
|
||||
# Related Articles |
||||
related_post: |
||||
enable: true |
||||
limit: 6 # Number of posts displayed |
||||
date_type: created # or created or updated 文章日期顯示創建日或者更新日 |
||||
|
||||
# post_pagination (分頁) |
||||
# value: 1 || 2 || false |
||||
# 1: The 'next post' will link to old post |
||||
# 2: The 'next post' will link to new post |
||||
# false: disable pagination |
||||
post_pagination: 1 |
||||
|
||||
# Displays outdated notice for a post (文章過期提醒) |
||||
noticeOutdate: |
||||
enable: false |
||||
style: flat # style: simple/flat |
||||
limit_day: 500 # When will it be shown |
||||
position: top # position: top/bottom |
||||
message_prev: It has been |
||||
message_next: days since the last update, the content of the article may be outdated. |
||||
|
||||
# Footer Settings |
||||
# -------------------------------------- |
||||
footer: |
||||
owner: |
||||
enable: true |
||||
since: 2020 |
||||
custom_text: |
||||
copyright: true # Copyright of theme and framework |
||||
|
||||
# aside (側邊欄) |
||||
# -------------------------------------- |
||||
|
||||
aside: |
||||
enable: true |
||||
hide: false |
||||
button: true |
||||
mobile: true # display on mobile |
||||
position: right # left or right |
||||
display: |
||||
archive: true |
||||
tag: true |
||||
category: true |
||||
card_author: |
||||
enable: true |
||||
description: |
||||
button: |
||||
enable: true |
||||
icon: fab fa-github |
||||
text: Follow Me |
||||
link: https://github.com/xxxxxx |
||||
card_announcement: |
||||
enable: true |
||||
content: This is my Blog |
||||
card_recent_post: |
||||
enable: true |
||||
limit: 5 # if set 0 will show all |
||||
sort: date # date or updated |
||||
sort_order: # Don't modify the setting unless you know how it works |
||||
card_categories: |
||||
enable: true |
||||
limit: 8 # if set 0 will show all |
||||
expand: none # none/true/false |
||||
sort_order: # Don't modify the setting unless you know how it works |
||||
card_tags: |
||||
enable: true |
||||
limit: 40 # if set 0 will show all |
||||
color: false |
||||
orderby: random # Order of tags, random/name/length |
||||
order: 1 # Sort of order. 1, asc for ascending; -1, desc for descending |
||||
sort_order: # Don't modify the setting unless you know how it works |
||||
card_archives: |
||||
enable: true |
||||
type: monthly # yearly or monthly |
||||
format: MMMM YYYY # eg: YYYY年MM月 |
||||
order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending |
||||
limit: 8 # if set 0 will show all |
||||
sort_order: # Don't modify the setting unless you know how it works |
||||
card_webinfo: |
||||
enable: true |
||||
post_count: true |
||||
last_push_date: true |
||||
sort_order: # Don't modify the setting unless you know how it works |
||||
card_post_series: |
||||
enable: true |
||||
series_title: false # The title shows the series name |
||||
orderBy: 'date' # Order by title or date |
||||
order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending |
||||
|
||||
# busuanzi count for PV / UV in site |
||||
# 訪問人數 |
||||
busuanzi: |
||||
site_uv: true |
||||
site_pv: true |
||||
page_pv: true |
||||
|
||||
# Time difference between publish date and now (網頁運行時間) |
||||
# Formal: Month/Day/Year Time or Year/Month/Day Time |
||||
runtimeshow: |
||||
enable: false |
||||
publish_date: |
||||
|
||||
# Aside widget - Newest Comments |
||||
newest_comments: |
||||
enable: false |
||||
sort_order: # Don't modify the setting unless you know how it works |
||||
limit: 6 |
||||
storage: 10 # unit: mins, save data to localStorage |
||||
avatar: true |
||||
|
||||
# Bottom right button (右下角按鈕) |
||||
# -------------------------------------- |
||||
|
||||
# Conversion between Traditional and Simplified Chinese (簡繁轉換) |
||||
translate: |
||||
enable: false |
||||
# The text of a button |
||||
default: 繁 |
||||
# the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese) |
||||
defaultEncoding: 2 |
||||
# Time delay |
||||
translateDelay: 0 |
||||
# The text of the button when the language is Simplified Chinese |
||||
msgToTraditionalChinese: '繁' |
||||
# The text of the button when the language is Traditional Chinese |
||||
msgToSimplifiedChinese: '簡' |
||||
|
||||
# Read Mode (閲讀模式) |
||||
readmode: true |
||||
|
||||
# dark mode |
||||
darkmode: |
||||
enable: true |
||||
# Toggle Button to switch dark/light mode |
||||
button: true |
||||
# Switch dark/light mode automatically (自動切換 dark mode和 light mode) |
||||
# autoChangeMode: 1 Following System Settings, if the system doesn't support dark mode, it will switch dark mode between 6 pm to 6 am |
||||
# autoChangeMode: 2 Switch dark mode between 6 pm to 6 am |
||||
# autoChangeMode: false |
||||
autoChangeMode: false |
||||
# Set the light mode time. The value is between 0 and 24. If not set, the default value is 6 and 18 |
||||
start: # 8 |
||||
end: # 22 |
||||
|
||||
# show scroll percent in scroll-to-top button |
||||
rightside_scroll_percent: false |
||||
|
||||
# Don't modify the following settings unless you know how they work (非必要請不要修改 ) |
||||
# Choose: readmode,translate,darkmode,hideAside,toc,chat,comment |
||||
# Don't repeat 不要重複 |
||||
rightside_item_order: |
||||
enable: false |
||||
hide: # readmode,translate,darkmode,hideAside |
||||
show: # toc,chat,comment |
||||
|
||||
# Math (數學) |
||||
# -------------------------------------- |
||||
# About the per_page |
||||
# if you set it to true, it will load mathjax/katex script in each page (true 表示每一頁都加載js) |
||||
# if you set it to false, it will load mathjax/katex script according to your setting (add the 'mathjax: true' in page's front-matter) |
||||
# (false 需要時加載,須在使用的 Markdown Front-matter 加上 mathjax: true) |
||||
|
||||
# MathJax |
||||
mathjax: |
||||
enable: false |
||||
per_page: false |
||||
|
||||
# KaTeX |
||||
katex: |
||||
enable: false |
||||
per_page: false |
||||
hide_scrollbar: true |
||||
|
||||
# search (搜索) |
||||
# see https://butterfly.js.org/posts/ceeb73f/#搜索系統 |
||||
# -------------------------------------- |
||||
|
||||
# Algolia search |
||||
algolia_search: |
||||
enable: false |
||||
hits: |
||||
per_page: 6 |
||||
|
||||
# Local search |
||||
local_search: |
||||
enable: false |
||||
# Preload the search data when the page loads. |
||||
preload: false |
||||
# Show top n results per article, show all results by setting to -1 |
||||
top_n_per_article: 1 |
||||
# Unescape html strings to the readable one. |
||||
unescape: false |
||||
CDN: |
||||
|
||||
# Docsearch |
||||
docsearch: |
||||
enable: false |
||||
appId: |
||||
apiKey: |
||||
indexName: |
||||
option: |
||||
|
||||
# Share System (分享) |
||||
# -------------------------------------- |
||||
|
||||
# Share.js |
||||
# https://github.com/overtrue/share.js |
||||
sharejs: |
||||
enable: true |
||||
sites: facebook,twitter,wechat,weibo,qq |
||||
|
||||
# AddToAny |
||||
# https://www.addtoany.com/ |
||||
addtoany: |
||||
enable: false |
||||
item: facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link |
||||
|
||||
# Comments System |
||||
# -------------------------------------- |
||||
|
||||
comments: |
||||
# Up to two comments system, the first will be shown as default |
||||
# Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/Artalk |
||||
use: # Valine,Disqus |
||||
text: true # Display the comment name next to the button |
||||
# lazyload: The comment system will be load when comment element enters the browser's viewport. |
||||
# If you set it to true, the comment count will be invalid |
||||
lazyload: false |
||||
count: false # Display comment count in post's top_img |
||||
card_post_count: false # Display comment count in Home Page |
||||
|
||||
# disqus |
||||
# https://disqus.com/ |
||||
disqus: |
||||
shortname: |
||||
apikey: # For newest comments widget |
||||
|
||||
# Alternative Disqus - Render comments with Disqus API |
||||
# DisqusJS 評論系統,可以實現在網路審查地區載入 Disqus 評論列表,兼容原版 |
||||
# https://github.com/SukkaW/DisqusJS |
||||
disqusjs: |
||||
shortname: |
||||
apikey: |
||||
option: |
||||
|
||||
# livere (來必力) |
||||
# https://www.livere.com/ |
||||
livere: |
||||
uid: |
||||
|
||||
# gitalk |
||||
# https://github.com/gitalk/gitalk |
||||
gitalk: |
||||
client_id: |
||||
client_secret: |
||||
repo: |
||||
owner: |
||||
admin: |
||||
option: |
||||
|
||||
# valine |
||||
# https://valine.js.org |
||||
valine: |
||||
appId: # leancloud application app id |
||||
appKey: # leancloud application app key |
||||
avatar: monsterid # gravatar style https://valine.js.org/#/avatar |
||||
serverURLs: # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in) |
||||
bg: # valine background |
||||
visitor: false |
||||
option: |
||||
|
||||
# waline - A simple comment system with backend support fork from Valine |
||||
# https://waline.js.org/ |
||||
waline: |
||||
serverURL: # Waline server address url |
||||
bg: # waline background |
||||
pageview: false |
||||
option: |
||||
|
||||
# utterances |
||||
# https://utteranc.es/ |
||||
utterances: |
||||
repo: |
||||
# Issue Mapping: pathname/url/title/og:title |
||||
issue_term: pathname |
||||
# Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark |
||||
light_theme: github-light |
||||
dark_theme: photon-dark |
||||
|
||||
# Facebook Comments Plugin |
||||
# https://developers.facebook.com/docs/plugins/comments/ |
||||
facebook_comments: |
||||
app_id: |
||||
user_id: # optional |
||||
pageSize: 10 # The number of comments to show |
||||
order_by: social # social/time/reverse_time |
||||
lang: zh_TW # Language en_US/zh_CN/zh_TW and so on |
||||
|
||||
# Twikoo |
||||
# https://github.com/imaegoo/twikoo |
||||
twikoo: |
||||
envId: |
||||
region: |
||||
visitor: false |
||||
option: |
||||
|
||||
# Giscus |
||||
# https://giscus.app/ |
||||
giscus: |
||||
repo: |
||||
repo_id: |
||||
category_id: |
||||
theme: |
||||
light: light |
||||
dark: dark |
||||
option: |
||||
|
||||
# Remark42 |
||||
# https://remark42.com/docs/configuration/frontend/ |
||||
remark42: |
||||
host: # Your Host URL |
||||
siteId: # Your Site ID |
||||
option: |
||||
|
||||
# Artalk |
||||
# https://artalk.js.org/guide/frontend/config.html |
||||
artalk: |
||||
server: |
||||
site: |
||||
visitor: false |
||||
option: |
||||
|
||||
# Chat Services |
||||
# -------------------------------------- |
||||
|
||||
# Chat Button [recommend] |
||||
# It will create a button in the bottom right corner of website, and hide the origin button |
||||
chat_btn: false |
||||
|
||||
# The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down |
||||
chat_hide_show: false |
||||
|
||||
# chatra |
||||
# https://chatra.io/ |
||||
chatra: |
||||
enable: false |
||||
id: |
||||
|
||||
# tidio |
||||
# https://www.tidio.com/ |
||||
tidio: |
||||
enable: false |
||||
public_key: |
||||
|
||||
# daovoice |
||||
# http://dashboard.daovoice.io/app |
||||
daovoice: |
||||
enable: false |
||||
app_id: |
||||
|
||||
# crisp |
||||
# https://crisp.chat/en/ |
||||
crisp: |
||||
enable: false |
||||
website_id: |
||||
|
||||
# messenger |
||||
# https://developers.facebook.com/docs/messenger-platform/discovery/facebook-chat-plugin/ |
||||
messenger: |
||||
enable: false |
||||
pageID: |
||||
lang: zh_TW # Language en_US/zh_CN/zh_TW and so on |
||||
|
||||
# Analysis |
||||
# -------------------------------------- |
||||
|
||||
# Baidu Analytics |
||||
# https://tongji.baidu.com/web/welcome/login |
||||
baidu_analytics: |
||||
|
||||
# Google Analytics |
||||
# https://analytics.google.com/analytics/web/ |
||||
google_analytics: |
||||
|
||||
# Cloudflare Analytics |
||||
# https://www.cloudflare.com/zh-tw/web-analytics/ |
||||
cloudflare_analytics: |
||||
|
||||
# Microsoft Clarity |
||||
# https://clarity.microsoft.com/ |
||||
microsoft_clarity: |
||||
|
||||
# Advertisement |
||||
# -------------------------------------- |
||||
|
||||
# Google Adsense (谷歌廣告) |
||||
google_adsense: |
||||
enable: false |
||||
auto_ads: true |
||||
js: https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js |
||||
client: |
||||
enable_page_level_ads: true |
||||
|
||||
# Insert ads manually (手動插入廣告) |
||||
# ad: |
||||
# index: |
||||
# aside: |
||||
# post: |
||||
|
||||
# Verification (站長驗證) |
||||
# -------------------------------------- |
||||
|
||||
site_verification: |
||||
# - name: google-site-verification |
||||
# content: xxxxxx |
||||
# - name: baidu-site-verification |
||||
# content: xxxxxxx |
||||
|
||||
# Beautify/Effect (美化/效果) |
||||
# -------------------------------------- |
||||
|
||||
# Theme color for customize |
||||
# Notice: color value must in double quotes like "#000" or may cause error! |
||||
|
||||
# theme_color: |
||||
# enable: true |
||||
# main: "#49B1F5" |
||||
# paginator: "#00c4b6" |
||||
# button_hover: "#FF7242" |
||||
# text_selection: "#00c4b6" |
||||
# link_color: "#99a9bf" |
||||
# meta_color: "#858585" |
||||
# hr_color: "#A4D8FA" |
||||
# code_foreground: "#F47466" |
||||
# code_background: "rgba(27, 31, 35, .05)" |
||||
# toc_color: "#00c4b6" |
||||
# blockquote_padding_color: "#49b1f5" |
||||
# blockquote_background_color: "#49b1f5" |
||||
# scrollbar_color: "#49b1f5" |
||||
# meta_theme_color_light: "ffffff" |
||||
# meta_theme_color_dark: "#0d0d0d" |
||||
|
||||
# The top_img settings of home page |
||||
# default: top img - full screen, site info - middle (默認top_img全屏,site_info在中間) |
||||
# The position of site info, eg: 300px/300em/300rem/10% (主頁標題距離頂部距離) |
||||
index_site_info_top: |
||||
# The height of top_img, eg: 300px/300em/300rem (主頁top_img高度) |
||||
index_top_img_height: |
||||
|
||||
# The user interface setting of category and tag page (category和tag頁的UI設置) |
||||
# index - same as Homepage UI (index 值代表 UI將與首頁的UI一樣) |
||||
# default - same as archives UI 默認跟archives頁面UI一樣 |
||||
category_ui: # 留空或 index |
||||
tag_ui: # 留空或 index |
||||
|
||||
# Stretches the lines so that each line has equal width(文字向兩側對齊,對最後一行無效) |
||||
text_align_justify: false |
||||
|
||||
# Website Background (設置網站背景) |
||||
# can set it to color or image (可設置圖片 或者 顔色) |
||||
# The formal of image: url(http://xxxxxx.com/xxx.jpg) |
||||
background: |
||||
|
||||
# Footer Background |
||||
footer_bg: false |
||||
|
||||
# Add mask to header or footer (为 header 或 footer 添加黑色半透遮罩) |
||||
mask: |
||||
header: true |
||||
footer: true |
||||
|
||||
# the position of bottom right button/default unit: px (右下角按鈕距離底部的距離/默認單位為px) |
||||
rightside_bottom: |
||||
|
||||
# Enter transitions (開啓網頁進入效果) |
||||
enter_transitions: true |
||||
|
||||
# Typewriter Effect (打字效果) |
||||
# https://github.com/disjukr/activate-power-mode |
||||
activate_power_mode: |
||||
enable: false |
||||
colorful: true # open particle animation (冒光特效) |
||||
shake: true # open shake (抖動特效) |
||||
mobile: false |
||||
|
||||
# Background effects (背景特效) |
||||
# -------------------------------------- |
||||
|
||||
# canvas_ribbon (靜止彩帶背景) |
||||
# See: https://github.com/hustcc/ribbon.js |
||||
canvas_ribbon: |
||||
enable: false |
||||
size: 150 |
||||
alpha: 0.6 |
||||
zIndex: -1 |
||||
click_to_change: false |
||||
mobile: false |
||||
|
||||
# Fluttering Ribbon (動態彩帶) |
||||
canvas_fluttering_ribbon: |
||||
enable: false |
||||
mobile: false |
||||
|
||||
# canvas_nest |
||||
# https://github.com/hustcc/canvas-nest.js |
||||
canvas_nest: |
||||
enable: false |
||||
color: '0,0,255' #color of lines, default: '0,0,0'; RGB values: (R,G,B).(note: use ',' to separate.) |
||||
opacity: 0.7 # the opacity of line (0~1), default: 0.5. |
||||
zIndex: -1 # z-index property of the background, default: -1. |
||||
count: 99 # the number of lines, default: 99. |
||||
mobile: false |
||||
|
||||
# Mouse click effects: fireworks (鼠標點擊效果: 煙火特效) |
||||
fireworks: |
||||
enable: false |
||||
zIndex: 9999 # -1 or 9999 |
||||
mobile: false |
||||
|
||||
# Mouse click effects: Heart symbol (鼠標點擊效果: 愛心) |
||||
click_heart: |
||||
enable: false |
||||
mobile: false |
||||
|
||||
# Mouse click effects: words (鼠標點擊效果: 文字) |
||||
clickShowText: |
||||
enable: false |
||||
text: |
||||
# - I |
||||
# - LOVE |
||||
# - YOU |
||||
fontSize: 15px |
||||
random: false |
||||
mobile: false |
||||
|
||||
# Default display mode (網站默認的顯示模式) |
||||
# light (default) / dark |
||||
display_mode: light |
||||
|
||||
# Beautify (美化頁面顯示) |
||||
beautify: |
||||
enable: false |
||||
field: post # site/post |
||||
title-prefix-icon: # '\f0c1' |
||||
title-prefix-icon-color: # '#F47466' |
||||
|
||||
# Global font settings |
||||
# Don't modify the following settings unless you know how they work (非必要不要修改) |
||||
font: |
||||
global-font-size: |
||||
code-font-size: |
||||
font-family: |
||||
code-font-family: |
||||
|
||||
# Font settings for the site title and site subtitle |
||||
# 左上角網站名字 主頁居中網站名字 |
||||
blog_title_font: |
||||
font_link: |
||||
font-family: |
||||
|
||||
# The setting of divider icon (水平分隔線圖標設置) |
||||
hr_icon: |
||||
enable: true |
||||
icon: # the unicode value of Font Awesome icon, such as '\3423' |
||||
icon-top: |
||||
|
||||
# the subtitle on homepage (主頁subtitle) |
||||
subtitle: |
||||
enable: false |
||||
# Typewriter Effect (打字效果) |
||||
effect: true |
||||
# Customize typed.js (配置typed.js) |
||||
# https://github.com/mattboldt/typed.js/#customization |
||||
typed_option: |
||||
# source 調用第三方服務 |
||||
# source: false 關閉調用 |
||||
# source: 1 調用一言網的一句話(簡體) https://hitokoto.cn/ |
||||
# source: 2 調用一句網(簡體) https://yijuzhan.com/ |
||||
# source: 3 調用今日詩詞(簡體) https://www.jinrishici.com/ |
||||
# subtitle 會先顯示 source , 再顯示 sub 的內容 |
||||
source: false |
||||
# 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字 |
||||
sub: |
||||
|
||||
# Loading Animation (加載動畫) |
||||
preloader: |
||||
enable: false |
||||
# source |
||||
# 1. fullpage-loading |
||||
# 2. pace (progress bar) |
||||
source: 1 |
||||
# pace theme (see https://codebyzach.github.io/pace/) |
||||
pace_css_url: |
||||
|
||||
# wordcount (字數統計) |
||||
# see https://butterfly.js.org/posts/ceeb73f/#字數統計 |
||||
wordcount: |
||||
enable: false |
||||
post_wordcount: true |
||||
min2read: true |
||||
total_wordcount: true |
||||
|
||||
# Lightbox (圖片大圖查看模式) |
||||
# -------------------------------------- |
||||
# You can only choose one, or neither (只能選擇一個 或者 兩個都不選) |
||||
|
||||
# medium-zoom |
||||
# https://github.com/francoischalifour/medium-zoom |
||||
medium_zoom: false |
||||
|
||||
# fancybox |
||||
# https://fancyapps.com/fancybox/ |
||||
fancybox: true |
||||
|
||||
# Tag Plugins settings (標籤外掛) |
||||
# -------------------------------------- |
||||
|
||||
# series (系列文章) |
||||
series: |
||||
enable: true |
||||
orderBy: 'title' # Order by title or date |
||||
order: 1 # Sort of order. 1, asc for ascending; -1, desc for descending |
||||
number: true |
||||
|
||||
# abcjs (樂譜渲染) |
||||
# See https://github.com/paulrosen/abcjs |
||||
abcjs: |
||||
enable: false |
||||
per_page: true |
||||
|
||||
# mermaid |
||||
# see https://github.com/mermaid-js/mermaid |
||||
mermaid: |
||||
enable: false |
||||
# built-in themes: default/forest/dark/neutral |
||||
theme: |
||||
light: default |
||||
dark: dark |
||||
|
||||
# Note (Bootstrap Callout) |
||||
note: |
||||
# Note tag style values: |
||||
# - simple bs-callout old alert style. Default. |
||||
# - modern bs-callout new (v2-v3) alert style. |
||||
# - flat flat callout style with background, like on Mozilla or StackOverflow. |
||||
# - disabled disable all CSS styles import of note tag. |
||||
style: flat |
||||
icons: true |
||||
border_radius: 3 |
||||
# Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6). |
||||
# Offset also applied to label tag variables. This option can work with disabled note tag. |
||||
light_bg_offset: 0 |
||||
|
||||
# other |
||||
# -------------------------------------- |
||||
|
||||
# Pjax |
||||
# It may contain bugs and unstable, give feedback when you find the bugs. |
||||
# https://github.com/MoOx/pjax |
||||
pjax: |
||||
enable: false |
||||
exclude: |
||||
# - xxxx |
||||
# - xxxx |
||||
|
||||
# Inject the css and script (aplayer/meting) |
||||
aplayerInject: |
||||
enable: false |
||||
per_page: true |
||||
|
||||
# Snackbar (Toast Notification 彈窗) |
||||
# https://github.com/polonel/SnackBar |
||||
# position 彈窗位置 |
||||
# 可選 top-left / top-center / top-right / bottom-left / bottom-center / bottom-right |
||||
snackbar: |
||||
enable: false |
||||
position: bottom-left |
||||
bg_light: '#49b1f5' # The background color of Toast Notification in light mode |
||||
bg_dark: '#1f1f1f' # The background color of Toast Notification in dark mode |
||||
|
||||
# https://instant.page/ |
||||
# prefetch (預加載) |
||||
instantpage: false |
||||
|
||||
# https://github.com/vinta/pangu.js |
||||
# Insert a space between Chinese character and English character (中英文之間添加空格) |
||||
pangu: |
||||
enable: false |
||||
field: site # site/post |
||||
|
||||
# Lazyload (圖片懶加載) |
||||
# https://github.com/verlok/vanilla-lazyload |
||||
lazyload: |
||||
enable: false |
||||
field: site # site/post |
||||
placeholder: |
||||
blur: false |
||||
|
||||
# PWA |
||||
# See https://github.com/JLHwung/hexo-offline |
||||
# --------------- |
||||
# pwa: |
||||
# enable: false |
||||
# manifest: /pwa/manifest.json |
||||
# apple_touch_icon: /pwa/apple-touch-icon.png |
||||
# favicon_32_32: /pwa/32.png |
||||
# favicon_16_16: /pwa/16.png |
||||
# mask_icon: /pwa/safari-pinned-tab.svg |
||||
|
||||
# Open graph meta tags |
||||
# https://developers.facebook.com/docs/sharing/webmasters/ |
||||
Open_Graph_meta: |
||||
enable: true |
||||
option: |
||||
# twitter_card: |
||||
# twitter_image: |
||||
# twitter_id: |
||||
# twitter_site: |
||||
# google_plus: |
||||
# fb_admins: |
||||
# fb_app_id: |
||||
|
||||
# Add the vendor prefixes to ensure compatibility |
||||
css_prefix: true |
||||
|
||||
# Inject |
||||
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag) |
||||
# 插入代码到头部 </head> 之前 和 底部 </body> 之前 |
||||
inject: |
||||
head: |
||||
# - <link rel="stylesheet" href="/xxx.css"> |
||||
bottom: |
||||
# - <script src="xxxx"></script> |
||||
|
||||
# CDN |
||||
# Don't modify the following settings unless you know how they work |
||||
# 非必要請不要修改 |
||||
CDN: |
||||
# The CDN provider of internal scripts (主題內部 js 的 cdn 配置) |
||||
# option: local/jsdelivr/unpkg/cdnjs/custom |
||||
# Dev version can only choose. ( dev版的主題只能設置為 local ) |
||||
internal_provider: local |
||||
|
||||
# The CDN provider of third party scripts (第三方 js 的 cdn 配置) |
||||
# option: local/jsdelivr/unpkg/cdnjs/custom |
||||
# when set it to local, you need to install hexo-butterfly-extjs |
||||
third_party_provider: jsdelivr |
||||
|
||||
# Add version number to url, true or false |
||||
version: true |
||||
|
||||
# Custom format |
||||
# For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file} |
||||
custom_format: |
||||
|
||||
option: |
||||
# abcjs_basic_js: |
||||
# activate_power_mode: |
||||
# algolia_js: |
||||
# algolia_search: |
||||
# aplayer_css: |
||||
# aplayer_js: |
||||
# artalk_css: |
||||
# artalk_js: |
||||
# blueimp_md5: |
||||
# busuanzi: |
||||
# canvas_fluttering_ribbon: |
||||
# canvas_nest: |
||||
# canvas_ribbon: |
||||
# click_heart: |
||||
# clickShowText: |
||||
# disqusjs: |
||||
# disqusjs_css: |
||||
# docsearch_css: |
||||
# docsearch_js: |
||||
# egjs_infinitegrid: |
||||
# fancybox: |
||||
# fancybox_css: |
||||
# fireworks: |
||||
# fontawesome: |
||||
# gitalk: |
||||
# gitalk_css: |
||||
# giscus: |
||||
# instantpage: |
||||
# instantsearch: |
||||
# katex: |
||||
# katex_copytex: |
||||
# lazyload: |
||||
# local_search: |
||||
# main: |
||||
# main_css: |
||||
# mathjax: |
||||
# medium_zoom: |
||||
# mermaid: |
||||
# meting_js: |
||||
# pangu: |
||||
# prismjs_autoloader: |
||||
# prismjs_js: |
||||
# prismjs_lineNumber_js: |
||||
# pjax: |
||||
# sharejs: |
||||
# sharejs_css: |
||||
# snackbar: |
||||
# snackbar_css: |
||||
# translate: |
||||
# twikoo: |
||||
# typed: |
||||
# utils: |
||||
# valine: |
||||
# waline_css: |
||||
# waline_js: |
@ -0,0 +1,123 @@ |
||||
footer: |
||||
framework: Framework |
||||
theme: Theme |
||||
|
||||
copy: |
||||
success: Copy Successful |
||||
error: Copy Error |
||||
noSupport: Browser Not Supported |
||||
|
||||
page: |
||||
articles: Articles |
||||
tag: Tag |
||||
category: Category |
||||
archives: Archives |
||||
|
||||
card_post_count: comments |
||||
|
||||
no_title: Untitled |
||||
|
||||
post: |
||||
created: Created |
||||
updated: Updated |
||||
wordcount: Word Count |
||||
min2read: Reading Time |
||||
min2read_unit: mins |
||||
page_pv: Post Views |
||||
comments: Comments |
||||
copyright: |
||||
author: Author |
||||
link: Link |
||||
copyright_notice: Copyright Notice |
||||
copyright_content: 'All articles in this blog are licensed under <a href="%s">%s</a> unless stating additionally.' |
||||
recommend: Related Articles |
||||
edit: Edited on |
||||
|
||||
search: |
||||
title: Search |
||||
load_data: Loading the Database |
||||
algolia_search: |
||||
input_placeholder: Search for Posts |
||||
hits_empty: "We didn't find any results for the search: ${query}." |
||||
hits_stats: '${hits} results found in ${time} ms' |
||||
|
||||
local_search: |
||||
input_placeholder: Search for Posts |
||||
hits_empty: "We didn't find any results for the search: ${query}" |
||||
hits_stats: '${hits} results found' |
||||
|
||||
pagination: |
||||
prev: Previous |
||||
next: Next |
||||
|
||||
comment: Comment |
||||
|
||||
aside: |
||||
articles: Articles |
||||
tags: Tags |
||||
categories: Categories |
||||
card_announcement: Announcement |
||||
card_categories: Categories |
||||
card_tags: Tags |
||||
card_archives: Archives |
||||
card_recent_post: Recent Post |
||||
card_webinfo: |
||||
headline: Info |
||||
article_name: Article |
||||
runtime: |
||||
name: Runtime |
||||
unit: days |
||||
last_push_date: |
||||
name: Last Update |
||||
site_wordcount: Total Count |
||||
site_uv_name: UV |
||||
site_pv_name: PV |
||||
more_button: View More |
||||
card_newest_comments: |
||||
headline: Latest Comments |
||||
loading_text: loading... |
||||
error: Unable to retrieve comments, please check the configuration |
||||
zero: No comments |
||||
image: image |
||||
link: link |
||||
code: code |
||||
card_toc: Contents |
||||
card_post_series: Series |
||||
|
||||
date_suffix: |
||||
just: Just now |
||||
min: minutes ago |
||||
hour: hours ago |
||||
day: days ago |
||||
month: months ago |
||||
|
||||
donate: Sponsor |
||||
share: Share |
||||
|
||||
rightside: |
||||
readmode_title: Read Mode |
||||
translate_title: Toggle Between Traditional Chinese And Simplified Chinese |
||||
night_mode_title: Toggle Between Light And Dark Mode |
||||
back_to_top: Back To Top |
||||
toc: Table Of Contents |
||||
scroll_to_comment: Scroll To Comments |
||||
setting: Setting |
||||
aside: Toggle between Single-column and Double-column |
||||
chat: Chat |
||||
|
||||
copy_copyright: |
||||
author: Author |
||||
link: Link |
||||
source: Source |
||||
info: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source. |
||||
|
||||
Snackbar: |
||||
chs_to_cht: You have switched to Traditional Chinese |
||||
cht_to_chs: You have switched to Simplified Chinese |
||||
day_to_night: You have switched to Dark Mode |
||||
night_to_day: You have switched to Light Mode |
||||
|
||||
loading: Loading... |
||||
load_more: Load More |
||||
|
||||
error404: Page Not Found |
@ -0,0 +1,123 @@ |
||||
footer: |
||||
framework: Framework |
||||
theme: Theme |
||||
|
||||
copy: |
||||
success: Copy Successful |
||||
error: Copy Error |
||||
noSupport: Browser Not Supported |
||||
|
||||
page: |
||||
articles: Articles |
||||
tag: Tag |
||||
category: Category |
||||
archives: Archives |
||||
|
||||
card_post_count: comments |
||||
|
||||
no_title: Untitled |
||||
|
||||
post: |
||||
created: Created |
||||
updated: Updated |
||||
wordcount: Word Count |
||||
min2read: Reading Time |
||||
min2read_unit: mins |
||||
page_pv: Post Views |
||||
comments: Comments |
||||
copyright: |
||||
author: Author |
||||
link: Link |
||||
copyright_notice: Copyright Notice |
||||
copyright_content: 'All articles in this blog are licensed under <a href="%s">%s</a> unless stating additionally.' |
||||
recommend: Related Articles |
||||
edit: Edited on |
||||
|
||||
search: |
||||
title: Search |
||||
load_data: Loading the Database |
||||
algolia_search: |
||||
input_placeholder: Search for Posts |
||||
hits_empty: "We didn't find any results for the search: ${query}." |
||||
hits_stats: '${hits} results found in ${time} ms' |
||||
|
||||
local_search: |
||||
input_placeholder: Search for Posts |
||||
hits_empty: "We didn't find any results for the search: ${query}" |
||||
hits_stats: '${hits} results found' |
||||
|
||||
pagination: |
||||
prev: Previous |
||||
next: Next |
||||
|
||||
comment: Comment |
||||
|
||||
aside: |
||||
articles: Articles |
||||
tags: Tags |
||||
categories: Categories |
||||
card_announcement: Announcement |
||||
card_categories: Categories |
||||
card_tags: Tags |
||||
card_archives: Archives |
||||
card_recent_post: Recent Post |
||||
card_webinfo: |
||||
headline: Info |
||||
article_name: Article |
||||
runtime: |
||||
name: Runtime |
||||
unit: days |
||||
last_push_date: |
||||
name: Last Update |
||||
site_wordcount: Total Count |
||||
site_uv_name: UV |
||||
site_pv_name: PV |
||||
more_button: View More |
||||
card_newest_comments: |
||||
headline: Latest Comments |
||||
loading_text: loading... |
||||
error: Unable to retrieve comments, please check the configuration |
||||
zero: No comments |
||||
image: image |
||||
link: link |
||||
code: code |
||||
card_toc: Contents |
||||
card_post_series: Series |
||||
|
||||
date_suffix: |
||||
just: Just now |
||||
min: minutes ago |
||||
hour: hours ago |
||||
day: days ago |
||||
month: months ago |
||||
|
||||
donate: Sponsor |
||||
share: Share |
||||
|
||||
rightside: |
||||
readmode_title: Read Mode |
||||
translate_title: Toggle Between Traditional Chinese And Simplified Chinese |
||||
night_mode_title: Toggle Between Light And Dark Mode |
||||
back_to_top: Back To Top |
||||
toc: Table Of Contents |
||||
scroll_to_comment: Scroll To Comments |
||||
setting: Setting |
||||
aside: Toggle between Single-column and Double-column |
||||
chat: Chat |
||||
|
||||
copy_copyright: |
||||
author: Author |
||||
link: Link |
||||
source: Source |
||||
info: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source. |
||||
|
||||
Snackbar: |
||||
chs_to_cht: You have switched to Traditional Chinese |
||||
cht_to_chs: You have switched to Simplified Chinese |
||||
day_to_night: You have switched to Dark Mode |
||||
night_to_day: You have switched to Light Mode |
||||
|
||||
loading: Loading... |
||||
load_more: Load More |
||||
|
||||
error404: Page Not Found |
@ -0,0 +1,124 @@ |
||||
footer: |
||||
framework: 框架 |
||||
theme: 主题 |
||||
|
||||
copy: |
||||
success: 复制成功 |
||||
error: 复制错误 |
||||
noSupport: 浏览器不支持 |
||||
|
||||
page: |
||||
articles: 文章总览 |
||||
tag: 标签 |
||||
category: 分类 |
||||
archives: 归档 |
||||
|
||||
card_post_count: 条评论 |
||||
|
||||
no_title: 无题 |
||||
|
||||
post: |
||||
created: 发表于 |
||||
updated: 更新于 |
||||
wordcount: 字数总计 |
||||
min2read: 阅读时长 |
||||
min2read_unit: 分钟 |
||||
page_pv: 阅读量 |
||||
comments: 评论数 |
||||
copyright: |
||||
author: 文章作者 |
||||
link: 文章链接 |
||||
copyright_notice: 版权声明 |
||||
copyright_content: '本博客所有文章除特别声明外,均采用 |
||||
<a href="%s" target="_blank">%s</a> 许可协议。转载请注明来自 <a href="%s" target="_blank">%s</a>!' |
||||
recommend: 相关推荐 |
||||
edit: 编辑 |
||||
|
||||
search: |
||||
title: 搜索 |
||||
load_data: 数据库加载中 |
||||
algolia_search: |
||||
input_placeholder: 搜索文章 |
||||
hits_empty: '找不到您查询的内容:${query}' |
||||
hits_stats: '找到 ${hits} 条结果,用时 ${time} 毫秒' |
||||
|
||||
local_search: |
||||
input_placeholder: 搜索文章 |
||||
hits_empty: '找不到您查询的内容:${query}' |
||||
hits_stats: '共找到 ${hits} 篇文章' |
||||
|
||||
pagination: |
||||
prev: 上一篇 |
||||
next: 下一篇 |
||||
|
||||
comment: 评论 |
||||
|
||||
aside: |
||||
articles: 文章 |
||||
tags: 标签 |
||||
categories: 分类 |
||||
card_announcement: 公告 |
||||
card_categories: 分类 |
||||
card_tags: 标签 |
||||
card_archives: 归档 |
||||
card_recent_post: 最新文章 |
||||
card_webinfo: |
||||
headline: 网站资讯 |
||||
article_name: 文章数目 |
||||
runtime: |
||||
name: 已运行时间 |
||||
unit: 天 |
||||
last_push_date: |
||||
name: 最后更新时间 |
||||
site_wordcount: 本站总字数 |
||||
site_uv_name: 本站访客数 |
||||
site_pv_name: 本站总访问量 |
||||
more_button: 查看更多 |
||||
card_newest_comments: |
||||
headline: 最新评论 |
||||
loading_text: 正在加载中... |
||||
error: 无法获取评论,请确认相关配置是否正确 |
||||
zero: 没有评论 |
||||
image: 图片 |
||||
link: 链接 |
||||
code: 代码 |
||||
card_toc: 目录 |
||||
card_post_series: 系列文章 |
||||
|
||||
date_suffix: |
||||
just: 刚刚 |
||||
min: 分钟前 |
||||
hour: 小时前 |
||||
day: 天前 |
||||
month: 个月前 |
||||
|
||||
donate: 赞助 |
||||
share: 分享 |
||||
|
||||
rightside: |
||||
readmode_title: 阅读模式 |
||||
translate_title: 简繁转换 |
||||
night_mode_title: 浅色和深色模式转换 |
||||
back_to_top: 回到顶部 |
||||
toc: 目录 |
||||
scroll_to_comment: 直达评论 |
||||
setting: 设置 |
||||
aside: 单栏和双栏切换 |
||||
chat: 聊天 |
||||
|
||||
copy_copyright: |
||||
author: 作者 |
||||
link: 链接 |
||||
source: 来源 |
||||
info: 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 |
||||
|
||||
Snackbar: |
||||
chs_to_cht: 你已切换为繁体中文 |
||||
cht_to_chs: 你已切换为简体中文 |
||||
day_to_night: 你已切换为深色模式 |
||||
night_to_day: 你已切换为浅色模式 |
||||
|
||||
loading: 加载中... |
||||
load_more: 加载更多 |
||||
|
||||
error404: 页面没有找到 |
@ -0,0 +1,124 @@ |
||||
footer: |
||||
framework: 框架 |
||||
theme: 主題 |
||||
|
||||
copy: |
||||
success: 複製成功 |
||||
error: 複製錯誤 |
||||
noSupport: 瀏覽器不支援 |
||||
|
||||
page: |
||||
articles: 文章總覽 |
||||
tag: 標籤 |
||||
category: 分類 |
||||
archives: 歸檔 |
||||
|
||||
card_post_count: 條評論 |
||||
|
||||
no_title: 無標題 |
||||
|
||||
post: |
||||
created: 發表於 |
||||
updated: 更新於 |
||||
wordcount: 字數總計 |
||||
min2read: 閱讀時長 |
||||
min2read_unit: 分鐘 |
||||
page_pv: 閱讀量 |
||||
comments: 評論數 |
||||
copyright: |
||||
author: 文章作者 |
||||
link: 文章連結 |
||||
copyright_notice: 版權聲明 |
||||
copyright_content: '本部落格所有文章除特別聲明外,均採用 |
||||
<a href="%s" target="_blank">%s</a> 許可協議。轉載請註明來自 <a href="%s" target="_blank">%s</a>!' |
||||
recommend: 相關推薦 |
||||
edit: 編輯 |
||||
|
||||
search: |
||||
title: 搜尋 |
||||
load_data: 資料庫載入中 |
||||
algolia_search: |
||||
input_placeholder: 搜尋文章 |
||||
hits_empty: '找不到您查詢的內容:${query}' |
||||
hits_stats: '找到 ${hits} 條結果,用時 ${time} 毫秒' |
||||
|
||||
local_search: |
||||
input_placeholder: 搜尋文章 |
||||
hits_empty: '找不到您查詢的內容:${query}' |
||||
hits_stats: '共找到 ${hits} 篇文章' |
||||
|
||||
pagination: |
||||
prev: 上一篇 |
||||
next: 下一篇 |
||||
|
||||
comment: 評論 |
||||
|
||||
aside: |
||||
articles: 文章 |
||||
tags: 標籤 |
||||
categories: 分類 |
||||
card_announcement: 公告 |
||||
card_categories: 分類 |
||||
card_tags: 標籤 |
||||
card_archives: 歸檔 |
||||
card_recent_post: 最新文章 |
||||
card_webinfo: |
||||
headline: 網站資訊 |
||||
article_name: 文章數目 |
||||
runtime: |
||||
name: 已執行時間 |
||||
unit: 天 |
||||
last_push_date: |
||||
name: 最後更新時間 |
||||
site_wordcount: 本站總字數 |
||||
site_uv_name: 本站訪客數 |
||||
site_pv_name: 本站總訪問量 |
||||
more_button: 檢視更多 |
||||
card_newest_comments: |
||||
headline: 最新評論 |
||||
loading_text: 正在載入中... |
||||
error: 無法獲取評論,請確認相關配置是否正確 |
||||
zero: 沒有評論 |
||||
image: 圖片 |
||||
link: 連結 |
||||
code: 程式碼 |
||||
card_toc: 目錄 |
||||
card_post_series: 文章系列 |
||||
|
||||
date_suffix: |
||||
just: 剛剛 |
||||
min: 分鐘前 |
||||
hour: 小時前 |
||||
day: 天前 |
||||
month: 個月前 |
||||
|
||||
donate: 贊助 |
||||
share: 分享 |
||||
|
||||
rightside: |
||||
readmode_title: 閱讀模式 |
||||
translate_title: 簡繁轉換 |
||||
night_mode_title: 淺色和深色模式轉換 |
||||
back_to_top: 返回頂部 |
||||
toc: 目錄 |
||||
scroll_to_comment: 直達評論 |
||||
setting: 設定 |
||||
aside: 單欄和雙欄切換 |
||||
chat: 聊天 |
||||
|
||||
copy_copyright: |
||||
author: 作者 |
||||
link: 連結 |
||||
source: 來源 |
||||
info: 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。 |
||||
|
||||
Snackbar: |
||||
chs_to_cht: 你已切換為繁體中文 |
||||
cht_to_chs: 你已切換為簡體中文 |
||||
day_to_night: 你已切換為深色模式 |
||||
night_to_day: 你已切換為淺色模式 |
||||
|
||||
loading: 載入中... |
||||
load_more: 載入更多 |
||||
|
||||
error404: 頁面沒有找到 |
@ -0,0 +1,8 @@ |
||||
extends includes/layout.pug |
||||
|
||||
block content |
||||
include ./includes/mixins/article-sort.pug |
||||
#archive |
||||
.article-sort-title= `${_p('page.articles')} - ${getArchiveLength()}` |
||||
+articleSort(page.posts) |
||||
include includes/pagination.pug |
@ -0,0 +1,14 @@ |
||||
extends includes/layout.pug |
||||
|
||||
block content |
||||
if theme.category_ui == 'index' |
||||
include ./includes/mixins/post-ui.pug |
||||
#recent-posts.recent-posts.category_ui |
||||
+postUI |
||||
include includes/pagination.pug |
||||
else |
||||
include ./includes/mixins/article-sort.pug |
||||
#category |
||||
.article-sort-title= _p('page.category') + ' - ' + page.category |
||||
+articleSort(page.posts) |
||||
include includes/pagination.pug |
@ -0,0 +1,12 @@ |
||||
- var top_img_404 = theme.error_404.background || theme.default_top_img |
||||
|
||||
#body-wrap.error404 |
||||
include ./header/index.pug |
||||
|
||||
#error-wrap |
||||
.error-content |
||||
.error-img |
||||
img(src=url_for(top_img_404) alt='Page not found') |
||||
.error-info |
||||
h1.error_title= '404' |
||||
.error_subtitle= theme.error_404.subtitle || _p('error404') |
@ -0,0 +1,65 @@ |
||||
div |
||||
script(src=url_for(theme.asset.utils)) |
||||
script(src=url_for(theme.asset.main)) |
||||
|
||||
if theme.translate.enable |
||||
script(src=url_for(theme.asset.translate)) |
||||
|
||||
if theme.medium_zoom |
||||
script(src=url_for(theme.asset.medium_zoom)) |
||||
else if theme.fancybox |
||||
script(src=url_for(theme.asset.fancybox)) |
||||
|
||||
if theme.instantpage |
||||
script(src=url_for(theme.asset.instantpage), type='module') |
||||
|
||||
if theme.lazyload.enable |
||||
script(src=url_for(theme.asset.lazyload)) |
||||
|
||||
if theme.snackbar.enable |
||||
script(src=url_for(theme.asset.snackbar)) |
||||
|
||||
if theme.pangu.enable |
||||
!= partial("includes/third-party/pangu.pug", {}, { cache: true }) |
||||
|
||||
.js-pjax |
||||
if needLoadCountJs |
||||
!= partial("includes/third-party/card-post-count/index", {}, { cache: true }) |
||||
|
||||
if loadSubJs |
||||
include ./third-party/subtitle.pug |
||||
|
||||
include ./third-party/math/index.pug |
||||
|
||||
include ./third-party/abcjs/index.pug |
||||
|
||||
if commentsJsLoad |
||||
include ./third-party/comments/js.pug |
||||
|
||||
!= partial("includes/third-party/prismjs", {}, { cache: true }) |
||||
|
||||
if theme.aside.enable && theme.newest_comments.enable |
||||
if theme.pjax.enable |
||||
!= partial("includes/third-party/newest-comments/index", {}, { cache: true }) |
||||
else if (!is_post() && page.aside !== false) |
||||
!= partial("includes/third-party/newest-comments/index", {}, { cache: true }) |
||||
|
||||
!= fragment_cache('injectBottom', function(){return injectHtml(theme.inject.bottom)}) |
||||
|
||||
!= partial("includes/third-party/effect", {}, { cache: true }) |
||||
|
||||
!= partial("includes/third-party/chat/index", {}, { cache: true }) |
||||
|
||||
if theme.aplayerInject && theme.aplayerInject.enable |
||||
if theme.pjax.enable || theme.aplayerInject.per_page |
||||
include ./third-party/aplayer.pug |
||||
else if page.aplayer |
||||
include ./third-party/aplayer.pug |
||||
|
||||
if theme.pjax.enable |
||||
!= partial("includes/third-party/pjax", {}, { cache: true }) |
||||
|
||||
if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv |
||||
script(async data-pjax src= theme.asset.busuanzi || '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js') |
||||
|
||||
!=partial('includes/third-party/search/index', {}, {cache: true}) |
@ -0,0 +1,17 @@ |
||||
#footer-wrap |
||||
if theme.footer.owner.enable |
||||
- var now = new Date() |
||||
- var nowYear = now.getFullYear() |
||||
if theme.footer.owner.since && theme.footer.owner.since != nowYear |
||||
.copyright!= `©${theme.footer.owner.since} - ${nowYear} By ${config.author}` |
||||
else |
||||
.copyright!= `©${nowYear} By ${config.author}` |
||||
if theme.footer.copyright |
||||
.framework-info |
||||
span= _p('footer.framework') + ' ' |
||||
a(href='https://hexo.io')= 'Hexo' |
||||
span.footer-separator | |
||||
span= _p('footer.theme') + ' ' |
||||
a(href='https://github.com/jerryc127/hexo-theme-butterfly')= 'Butterfly' |
||||
if theme.footer.custom_text |
||||
.footer_custom_text!=`${theme.footer.custom_text}` |
@ -0,0 +1,68 @@ |
||||
- var pageTitle |
||||
- is_archive() ? page.title = findArchivesTitle(page, theme.menu, date) : '' |
||||
- if (is_tag()) pageTitle = _p('page.tag') + ': ' + page.tag |
||||
- else if (is_category()) pageTitle = _p('page.category') + ': ' + page.category |
||||
- else if (is_current('/404.html', [strict])) pageTitle = _p('error404') |
||||
- else pageTitle = page.title || config.title || '' |
||||
|
||||
- var isSubtitle = config.subtitle ? ' - ' + config.subtitle : '' |
||||
- var tabTitle = is_home() || !pageTitle ? config.title + isSubtitle : pageTitle + ' | ' + config.title |
||||
- var pageAuthor = config.email ? config.author + ',' + config.email : config.author |
||||
- var pageCopyright = config.copyright || config.author |
||||
- var themeColorLight = theme.theme_color && theme.theme_color.enable && theme.theme_color.meta_theme_color_light || '#ffffff' |
||||
- var themeColorDark = theme.theme_color && theme.theme_color.enable && theme.theme_color.meta_theme_color_dark || '#0d0d0d' |
||||
- var themeColor = theme.display_mode === 'dark' ? themeColorDark : themeColorLight |
||||
|
||||
meta(charset='UTF-8') |
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge") |
||||
meta(name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover") |
||||
title= tabTitle |
||||
meta(name="author" content=pageAuthor) |
||||
meta(name="copyright" content=pageCopyright) |
||||
meta(name ="format-detection" content="telephone=no") |
||||
meta(name="theme-color" content=themeColor) |
||||
|
||||
//- Open_Graph |
||||
include ./head/Open_Graph.pug |
||||
|
||||
!=favicon_tag(theme.favicon || config.favicon) |
||||
link(rel="canonical" href=urlNoIndex(null,config.pretty_urls.trailing_index,config.pretty_urls.trailing_html)) |
||||
|
||||
//- 預解析 |
||||
!=partial('includes/head/preconnect', {}, {cache: true}) |
||||
|
||||
//- 網站驗證 |
||||
!=partial('includes/head/site_verification', {}, {cache: true}) |
||||
|
||||
//- PWA |
||||
if (theme.pwa && theme.pwa.enable) |
||||
!=partial('includes/head/pwa', {}, {cache: true}) |
||||
|
||||
//- main css |
||||
link(rel='stylesheet', href=url_for(theme.asset.main_css)) |
||||
link(rel='stylesheet', href=url_for(theme.asset.fontawesome)) |
||||
|
||||
if (theme.snackbar && theme.snackbar.enable) |
||||
link(rel='stylesheet', href=url_for(theme.asset.snackbar_css) media="print" onload="this.media='all'") |
||||
|
||||
if theme.fancybox |
||||
link(rel='stylesheet' href=url_for(theme.asset.fancybox_css) media="print" onload="this.media='all'") |
||||
|
||||
//- google_adsense |
||||
!=partial('includes/head/google_adsense', {}, {cache: true}) |
||||
|
||||
//- analytics |
||||
!=partial('includes/head/analytics', {}, {cache: true}) |
||||
|
||||
//- font |
||||
if theme.blog_title_font && theme.blog_title_font.font_link |
||||
link(rel='stylesheet' href=url_for(theme.blog_title_font.font_link) media="print" onload="this.media='all'") |
||||
|
||||
//- global config |
||||
!=partial('includes/head/config', {}, {cache: true}) |
||||
|
||||
include ./head/config_site.pug |
||||
|
||||
!=fragment_cache('injectHeadJs', function(){return inject_head_js()}) |
||||
|
||||
!=fragment_cache('injectHead', function(){return injectHtml(theme.inject.head)}) |
@ -0,0 +1,14 @@ |
||||
if theme.Open_Graph_meta.enable |
||||
- |
||||
const coverVal = page.cover_type === 'img' ? page.cover : theme.avatar.img |
||||
let ogOption = Object.assign({ |
||||
type: is_post() ? 'article' : 'website', |
||||
image: coverVal ? full_url_for(coverVal) : '', |
||||
fb_admins: theme.facebook_comments.user_id || '', |
||||
fb_app_id: theme.facebook_comments.app_id || '', |
||||
}, theme.Open_Graph_meta.option) |
||||
- |
||||
!= open_graph(ogOption) |
||||
else |
||||
meta(name="description" content=page_description()) |
||||
|
@ -0,0 +1,28 @@ |
||||
if theme.baidu_analytics |
||||
script. |
||||
var _hmt = _hmt || []; |
||||
(function() { |
||||
var hm = document.createElement("script"); |
||||
hm.src = "https://hm.baidu.com/hm.js?!{theme.baidu_analytics}"; |
||||
var s = document.getElementsByTagName("script")[0]; |
||||
s.parentNode.insertBefore(hm, s); |
||||
})(); |
||||
|
||||
if theme.google_analytics |
||||
script(async src=`https://www.googletagmanager.com/gtag/js?id=${theme.google_analytics}`) |
||||
script. |
||||
window.dataLayer = window.dataLayer || []; |
||||
function gtag(){dataLayer.push(arguments);} |
||||
gtag('js', new Date()); |
||||
gtag('config', '!{theme.google_analytics}'); |
||||
|
||||
if theme.cloudflare_analytics |
||||
script(defer data-pjax src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon=`{"token": "${theme.cloudflare_analytics}"}`) |
||||
|
||||
if theme.microsoft_clarity |
||||
script. |
||||
(function(c,l,a,r,i,t,y){ |
||||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; |
||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; |
||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); |
||||
})(window, document, "clarity", "script", "!{theme.microsoft_clarity}"); |
@ -0,0 +1,132 @@ |
||||
- |
||||
let algolia = 'undefined'; |
||||
let env = process.env; |
||||
if (theme.algolia_search.enable) { |
||||
algolia = JSON.stringify({ |
||||
appId: env.ALGOLIA_APP_ID || config.algolia.appId || config.algolia.applicationID, |
||||
apiKey: env.ALGOLIA_API_KEY || config.algolia.apiKey, |
||||
indexName: env.ALGOLIA_INDEX_NAME || config.algolia.indexName, |
||||
hits: theme.algolia_search.hits, |
||||
// search languages |
||||
languages: { |
||||
input_placeholder: _p("search.algolia_search.input_placeholder"), |
||||
hits_empty: _p("search.algolia_search.hits_empty"), |
||||
hits_stats: _p("search.algolia_search.hits_stats"), |
||||
} |
||||
}) |
||||
} |
||||
|
||||
let localSearch = 'undefined'; |
||||
if (theme.local_search && theme.local_search.enable) { |
||||
localSearch = JSON.stringify({ |
||||
path: theme.local_search.CDN ? theme.local_search.CDN : config.root + config.search.path, |
||||
preload: theme.local_search.preload, |
||||
top_n_per_article: theme.local_search.top_n_per_article, |
||||
unescape: theme.local_search.unescape, |
||||
languages: { |
||||
// search languages |
||||
hits_empty: _p("search.local_search.hits_empty"), |
||||
hits_stats: _p("search.local_search.hits_stats"), |
||||
} |
||||
}) |
||||
} |
||||
|
||||
let translate = 'undefined'; |
||||
if (theme.translate && theme.translate.enable){ |
||||
translate = JSON.stringify({ |
||||
defaultEncoding: theme.translate.defaultEncoding, |
||||
translateDelay: theme.translate.translateDelay, |
||||
msgToTraditionalChinese: theme.translate.msgToTraditionalChinese, |
||||
msgToSimplifiedChinese: theme.translate.msgToSimplifiedChinese |
||||
}) |
||||
} |
||||
|
||||
let copyright = 'undefined'; |
||||
if (theme.copy.enable && theme.copy.copyright.enable){ |
||||
copyright = JSON.stringify({ |
||||
limitCount: theme.copy.copyright.limit_count, |
||||
languages: { |
||||
author: _p("copy_copyright.author") + ': ' + config.author, |
||||
link: _p("copy_copyright.link") + ': ', |
||||
source: _p("copy_copyright.source") + ': ' + config.title, |
||||
info: _p("copy_copyright.info") |
||||
} |
||||
}) |
||||
} |
||||
|
||||
let Snackbar = 'undefined'; |
||||
if (theme.snackbar && theme.snackbar.enable) { |
||||
Snackbar = JSON.stringify({ |
||||
chs_to_cht: _p("Snackbar.chs_to_cht"), |
||||
cht_to_chs: _p("Snackbar.cht_to_chs"), |
||||
day_to_night: _p("Snackbar.day_to_night"), |
||||
night_to_day: _p("Snackbar.night_to_day"), |
||||
bgLight: theme.snackbar.bg_light, |
||||
bgDark: theme.snackbar.bg_dark, |
||||
position: theme.snackbar.position, |
||||
}) |
||||
} |
||||
|
||||
let noticeOutdate = 'undefined'; |
||||
if (theme.noticeOutdate && theme.noticeOutdate.enable) { |
||||
noticeOutdate = JSON.stringify({ |
||||
limitDay: theme.noticeOutdate.limit_day, |
||||
position: theme.noticeOutdate.position, |
||||
messagePrev: theme.noticeOutdate.message_prev, |
||||
messageNext: theme.noticeOutdate.message_next, |
||||
}) |
||||
} |
||||
|
||||
let highlight = 'undefined'; |
||||
let syntaxHighlighter = config.syntax_highlighter; |
||||
let highlightEnable = syntaxHighlighter ? ['highlight.js', 'prismjs'].includes(syntaxHighlighter) : (config.highlight.enable || config.prismjs.enable); |
||||
if (highlightEnable) { |
||||
highlight = JSON.stringify({ |
||||
plugin: syntaxHighlighter ? syntaxHighlighter : config.highlight.enable ? 'highlight.js' : 'prismjs', |
||||
highlightCopy: theme.highlight_copy, |
||||
highlightLang: theme.highlight_lang, |
||||
highlightHeightLimit: theme.highlight_height_limit |
||||
}) |
||||
} |
||||
|
||||
script. |
||||
const GLOBAL_CONFIG = { |
||||
root: '!{config.root}', |
||||
algolia: !{algolia}, |
||||
localSearch: !{localSearch}, |
||||
translate: !{translate}, |
||||
noticeOutdate: !{noticeOutdate}, |
||||
highlight: !{highlight}, |
||||
copy: { |
||||
success: '!{_p("copy.success")}', |
||||
error: '!{_p("copy.error")}', |
||||
noSupport: '!{_p("copy.noSupport")}' |
||||
}, |
||||
relativeDate: { |
||||
homepage: !{theme.post_meta.page.date_format === 'relative'}, |
||||
post: !{theme.post_meta.post.date_format === 'relative'} |
||||
}, |
||||
runtime: '!{theme.runtimeshow.enable ? _p("aside.card_webinfo.runtime.unit") : ""}', |
||||
dateSuffix: { |
||||
just: '!{_p("date_suffix.just")}', |
||||
min: '!{_p("date_suffix.min")}', |
||||
hour: '!{_p("date_suffix.hour")}', |
||||
day: '!{_p("date_suffix.day")}', |
||||
month: '!{_p("date_suffix.month")}' |
||||
}, |
||||
copyright: !{copyright}, |
||||
lightbox: '!{ theme.medium_zoom ? "mediumZoom" : (theme.fancybox ? "fancybox" : "null" )}', |
||||
Snackbar: !{Snackbar}, |
||||
infinitegrid: { |
||||
js: '!{url_for(theme.asset.egjs_infinitegrid)}', |
||||
buttonText: '!{_p("load_more")}' |
||||
}, |
||||
isPhotoFigcaption: !{theme.photofigcaption}, |
||||
islazyload: !{theme.lazyload.enable}, |
||||
isAnchor: !{theme.anchor.auto_update || false}, |
||||
percent: { |
||||
toc: !{theme.toc.scroll_percent}, |
||||
rightside: !{theme.rightside_scroll_percent}, |
||||
}, |
||||
autoDarkmode: !{theme.darkmode.enable && theme.darkmode.autoChangeMode === 1} |
||||
} |
@ -0,0 +1,30 @@ |
||||
- |
||||
const titleVal = pageTitle.replace(/'/ig,"\\'") |
||||
|
||||
let isHighlightShrink |
||||
if (theme.highlight_shrink == 'none') isHighlightShrink = 'undefined' |
||||
else if (page.highlight_shrink === true || page.highlight_shrink === false) isHighlightShrink = page.highlight_shrink |
||||
else isHighlightShrink = theme.highlight_shrink |
||||
|
||||
var showToc = false |
||||
if (theme.aside.enable && page.aside !== false) { |
||||
let tocEnable = false |
||||
if (is_post()) { |
||||
if (theme.toc.post) tocEnable = true |
||||
} else if (is_page()) { |
||||
if (theme.toc.page) tocEnable = true |
||||
} |
||||
const pageToc = page.toc === true || page.toc === false ? page.toc : tocEnable |
||||
showToc = pageToc && (toc(page.content) !== '' || page.encrypt == true ) |
||||
} |
||||
- |
||||
|
||||
script#config-diff. |
||||
var GLOBAL_CONFIG_SITE = { |
||||
title: '!{titleVal}', |
||||
isPost: !{is_post()}, |
||||
isHome: !{is_home()}, |
||||
isHighlightShrink: !{isHighlightShrink}, |
||||
isToc: !{showToc}, |
||||
postUpdate: '!{full_date(page.updated)}' |
||||
} |
@ -0,0 +1,9 @@ |
||||
if (theme.google_adsense && theme.google_adsense.enable) |
||||
script(async src=theme.google_adsense.js) |
||||
|
||||
if theme.google_adsense.auto_ads |
||||
script. |
||||
(adsbygoogle = window.adsbygoogle || []).push({ |
||||
google_ad_client: '!{theme.google_adsense.client}', |
||||
enable_page_level_ads: '!{theme.google_adsense.enable_page_level_ads}' |
||||
}); |
@ -0,0 +1,35 @@ |
||||
- |
||||
const { internal_provider, third_party_provider, custom_format } = theme.CDN |
||||
const providers = { |
||||
'jsdelivr': '//cdn.jsdelivr.net', |
||||
'cdnjs': '//cdnjs.cloudflare.com', |
||||
'unpkg': '//unpkg.com', |
||||
'custom': custom_format && custom_format.match(/^((https?:)?(\/\/[^/]+)|([^/]+))(\/|$)/)[1] |
||||
} |
||||
- |
||||
|
||||
if internal_provider === third_party_provider && internal_provider !== 'local' |
||||
link(rel="preconnect" href=providers[internal_provider]) |
||||
else |
||||
if internal_provider !== 'local' |
||||
link(rel="preconnect" href=providers[internal_provider]) |
||||
if third_party_provider !== 'local' |
||||
link(rel="preconnect" href=providers[third_party_provider]) |
||||
|
||||
if theme.google_analytics |
||||
link(rel="preconnect" href="//www.google-analytics.com" crossorigin='') |
||||
|
||||
if theme.baidu_analytics |
||||
link(rel="preconnect" href="//hm.baidu.com") |
||||
|
||||
if theme.cloudflare_analytics |
||||
link(rel="preconnect" href="//static.cloudflareinsights.com") |
||||
|
||||
if theme.microsoft_clarity |
||||
link(rel="preconnect" href="//www.clarity.ms") |
||||
|
||||
if theme.blog_title_font && theme.blog_title_font.font_link && theme.blog_title_font.font_link.indexOf('//fonts.googleapis.com') != -1 |
||||
link(rel="preconnect" href="//fonts.googleapis.com" crossorigin='') |
||||
|
||||
if !theme.asset.busuanzi && (theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv) |
||||
link(rel="preconnect" href="//busuanzi.ibruce.info") |
@ -0,0 +1,11 @@ |
||||
link(rel="manifest" href=url_for(theme.pwa.manifest)) |
||||
if(theme.pwa.theme_color) |
||||
meta(name="msapplication-TileColor" content=theme.pwa.theme_color) |
||||
if(theme.pwa.apple_touch_icon) |
||||
link(rel="apple-touch-icon" sizes="180x180" href=url_for(theme.pwa.apple_touch_icon)) |
||||
if(theme.pwa.favicon_32_32) |
||||
link(rel="icon" type="image/png" sizes="32x32" href=url_for(theme.pwa.favicon_32_32)) |
||||
if(theme.pwa.favicon_16_16) |
||||
link(rel="icon" type="image/png" sizes="16x16" href=url_for(theme.pwa.favicon_16_16)) |
||||
if(theme.pwa.mask_icon) |
||||
link(rel="mask-icon" href=url_for(theme.pwa.mask_icon) color="#5bbad5") |
@ -0,0 +1,3 @@ |
||||
if theme.site_verification |
||||
each item in theme.site_verification |
||||
meta(name=item.name content=item.content) |
@ -0,0 +1,52 @@ |
||||
if !theme.disable_top_img && page.top_img !== false |
||||
if is_post() |
||||
- var top_img = page.top_img || page.cover || theme.default_top_img |
||||
else if is_page() |
||||
- var top_img = page.top_img || theme.default_top_img |
||||
else if is_tag() |
||||
- var top_img = theme.tag_per_img && theme.tag_per_img[page.tag] |
||||
- top_img = top_img ? top_img : (theme.tag_img !== false ? theme.tag_img || theme.default_top_img : false) |
||||
else if is_category() |
||||
- var top_img = theme.category_per_img && theme.category_per_img[page.category] |
||||
- top_img = top_img ? top_img : (theme.category_img !== false ? theme.category_img || theme.default_top_img : false) |
||||
else if is_home() |
||||
- var top_img = theme.index_img !== false ? theme.index_img || theme.default_top_img : false |
||||
else if is_archive() |
||||
- var top_img = theme.archive_img !== false ? theme.archive_img || theme.default_top_img : false |
||||
else |
||||
- var top_img = page.top_img || theme.default_top_img |
||||
|
||||
if top_img !== false |
||||
- var imgSource = top_img && isImgOrUrl(top_img) ? `background-image: url('${url_for(top_img)}')` : `background: ${top_img}` |
||||
- var bg_img = top_img ? imgSource : '' |
||||
- var site_title = page.title || page.tag || page.category || config.title |
||||
- var isHomeClass = is_home() ? 'full_page' : 'not-home-page' |
||||
- is_post() ? isHomeClass = 'post-bg' : isHomeClass |
||||
else |
||||
- var isHomeClass = 'not-top-img' |
||||
else |
||||
- var top_img = false |
||||
- var isHomeClass = 'not-top-img' |
||||
|
||||
- const isFixedClass = theme.nav.fixed ? ' fixed' : '' |
||||
|
||||
header#page-header(class=`${isHomeClass+isFixedClass}` style=bg_img) |
||||
!=partial('includes/header/nav', {}, {cache: true}) |
||||
if top_img !== false |
||||
if is_post() |
||||
include ./post-info.pug |
||||
else if is_home() |
||||
#site-info |
||||
h1#site-title=site_title |
||||
if theme.subtitle.enable |
||||
- var loadSubJs = true |
||||
#site-subtitle |
||||
span#subtitle |
||||
if(theme.social) |
||||
#site_social_icons |
||||
!=partial('includes/header/social', {}, {cache: true}) |
||||
#scroll-down |
||||
i.fas.fa-angle-down.scroll-down-effects |
||||
else |
||||
#page-site-info |
||||
h1#site-title=site_title |
@ -0,0 +1,27 @@ |
||||
if theme.menu |
||||
.menus_items |
||||
each value, label in theme.menu |
||||
if typeof value !== 'object' |
||||
.menus_item |
||||
- const valueArray = value.split('||') |
||||
a.site-page(href=url_for(trim(valueArray[0]))) |
||||
if valueArray[1] |
||||
i.fa-fw(class=trim(valueArray[1])) |
||||
span=' '+label |
||||
else |
||||
.menus_item |
||||
- const labelArray = label.split('||') |
||||
- const hideClass = labelArray[2] && trim(labelArray[2]) === 'hide' ? 'hide' : '' |
||||
a.site-page.group(class=`${hideClass}` href='javascript:void(0);') |
||||
if labelArray[1] |
||||
i.fa-fw(class=trim(labelArray[1])) |
||||
span=' '+ trim(labelArray[0]) |
||||
i.fas.fa-chevron-down |
||||
ul.menus_item_child |
||||
each val,lab in value |
||||
- const valArray = val.split('||') |
||||
li |
||||
a.site-page.child(href=url_for(trim(valArray[0]))) |
||||
if valArray[1] |
||||
i.fa-fw(class=trim(valArray[1])) |
||||
span=' '+ lab |
@ -0,0 +1,21 @@ |
||||
nav#nav |
||||
span#blog-info |
||||
a(href=url_for('/') title=config.title) |
||||
if theme.nav.logo |
||||
img.site-icon(src=url_for(theme.nav.logo)) |
||||
if theme.nav.display_title |
||||
span.site-name=config.title |
||||
|
||||
#menus |
||||
if (theme.algolia_search.enable || theme.local_search.enable || theme.docsearch.enable) |
||||
#search-button |
||||
a.site-page.social-icon.search(href="javascript:void(0);") |
||||
i.fas.fa-search.fa-fw |
||||
span=' '+_p('search.title') |
||||
!=partial('includes/header/menu_item', {}, {cache: true}) |
||||
|
||||
#toggle-menu |
||||
a.site-page(href="javascript:void(0);") |
||||
i.fas.fa-bars.fa-fw |
||||
|
||||
|
@ -0,0 +1,144 @@ |
||||
- let comments = theme.comments |
||||
#post-info |
||||
h1.post-title= page.title || _p('no_title') |
||||
if theme.post_edit.enable |
||||
a.post-edit-link(href=theme.post_edit.url + page.source title=_p('post.edit') target="_blank") |
||||
i.fas.fa-pencil-alt |
||||
|
||||
#post-meta |
||||
.meta-firstline |
||||
if (theme.post_meta.post.date_type) |
||||
span.post-meta-date |
||||
if (theme.post_meta.post.date_type === 'both') |
||||
i.far.fa-calendar-alt.fa-fw.post-meta-icon |
||||
span.post-meta-label= _p('post.created') |
||||
time.post-meta-date-created(datetime=date_xml(page.date) title=_p('post.created') + ' ' + full_date(page.date))=date(page.date, config.date_format) |
||||
span.post-meta-separator | |
||||
i.fas.fa-history.fa-fw.post-meta-icon |
||||
span.post-meta-label= _p('post.updated') |
||||
time.post-meta-date-updated(datetime=date_xml(page.updated) title=_p('post.updated') + ' ' + full_date(page.updated))=date(page.updated, config.date_format) |
||||
else |
||||
- let data_type_update = theme.post_meta.post.date_type === 'updated' |
||||
- let date_type = data_type_update ? 'updated' : 'date' |
||||
- let date_icon = data_type_update ? 'fas fa-history' :'far fa-calendar-alt' |
||||
- let date_title = data_type_update ? _p('post.updated') : _p('post.created') |
||||
i.fa-fw.post-meta-icon(class=date_icon) |
||||
span.post-meta-label= date_title |
||||
time(datetime=date_xml(page[date_type]) title=date_title + ' ' + full_date(page[date_type]))=date(page[date_type], config.date_format) |
||||
if (theme.post_meta.post.categories && page.categories.data.length > 0) |
||||
span.post-meta-categories |
||||
if (theme.post_meta.post.date_type) |
||||
span.post-meta-separator | |
||||
|
||||
each item, index in page.categories.data |
||||
i.fas.fa-inbox.fa-fw.post-meta-icon |
||||
a(href=url_for(item.path)).post-meta-categories #[=item.name] |
||||
if (index < page.categories.data.length - 1) |
||||
i.fas.fa-angle-right.post-meta-separator |
||||
|
||||
.meta-secondline |
||||
- let postWordcount = theme.wordcount.enable && (theme.wordcount.post_wordcount || theme.wordcount.min2read) |
||||
if (postWordcount) |
||||
span.post-meta-separator | |
||||
span.post-meta-wordcount |
||||
if theme.wordcount.post_wordcount |
||||
i.far.fa-file-word.fa-fw.post-meta-icon |
||||
span.post-meta-label= _p('post.wordcount') + ':' |
||||
span.word-count= wordcount(page.content) |
||||
if theme.wordcount.min2read |
||||
span.post-meta-separator | |
||||
if theme.wordcount.min2read |
||||
i.far.fa-clock.fa-fw.post-meta-icon |
||||
span.post-meta-label= _p('post.min2read') + ':' |
||||
span= min2read(page.content, {cn: 350, en: 160}) + _p('post.min2read_unit') |
||||
|
||||
//- for pv and count |
||||
mixin pvBlock(parent_id,parent_class,parent_title) |
||||
span.post-meta-separator | |
||||
span(class=parent_class id=parent_id data-flag-title=page.title) |
||||
i.far.fa-eye.fa-fw.post-meta-icon |
||||
span.post-meta-label=_p('post.page_pv') + ':' |
||||
if block |
||||
block |
||||
|
||||
- const commentUse = comments.use |
||||
if page.comments !== false && commentUse && !comments.lazyload |
||||
if commentUse[0] === 'Valine' && theme.valine.visitor |
||||
+pvBlock(url_for(page.path),'leancloud_visitors',page.title) |
||||
span.leancloud-visitors-count |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
else if commentUse[0] === 'Waline' && theme.waline.pageview |
||||
+pvBlock('','','') |
||||
span.waline-pageview-count(data-path=url_for(page.path)) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
else if commentUse[0] === 'Twikoo' && theme.twikoo.visitor |
||||
+pvBlock('','','') |
||||
span#twikoo_visitors |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
else if commentUse[0] === 'Artalk' && theme.artalk.visitor |
||||
+pvBlock('','','') |
||||
span#ArtalkPV |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
else if theme.busuanzi.page_pv |
||||
+pvBlock('','post-meta-pv-cv','') |
||||
span#busuanzi_value_page_pv |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
else if theme.busuanzi.page_pv |
||||
+pvBlock('','post-meta-pv-cv','') |
||||
span#busuanzi_value_page_pv |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
|
||||
if comments.count && !comments.lazyload && page.comments !== false && comments.use |
||||
- var whichCount = comments.use[0] |
||||
|
||||
mixin countBlock |
||||
span.post-meta-separator | |
||||
span.post-meta-commentcount |
||||
i.far.fa-comments.fa-fw.post-meta-icon |
||||
span.post-meta-label= _p('post.comments') + ':' |
||||
if block |
||||
block |
||||
|
||||
case whichCount |
||||
when 'Disqus' |
||||
+countBlock |
||||
a.disqus-comment-count(href=full_url_for(page.path) + '#post-comment') |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Disqusjs' |
||||
+countBlock |
||||
a.disqusjs-comment-count(href=full_url_for(page.path) + '#post-comment') |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Valine' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment' itemprop="discussionUrl") |
||||
span.valine-comment-count(data-xid=url_for(page.path) itemprop="commentCount") |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Waline' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment') |
||||
span.waline-comment-count(data-path=url_for(page.path)) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Gitalk' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment') |
||||
span.gitalk-comment-count |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Twikoo' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment') |
||||
span#twikoo-count |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Facebook Comments' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment') |
||||
span.fb-comments-count(data-href=urlNoIndex()) |
||||
when 'Remark42' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment') |
||||
span.remark42__counter(data-url=urlNoIndex()) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Artalk' |
||||
+countBlock |
||||
a(href=url_for(page.path) + '#post-comment') |
||||
span.artalk-count |
||||
i.fa-solid.fa-spinner.fa-spin |
@ -0,0 +1,4 @@ |
||||
each url, icon in theme.social |
||||
a.social-icon(href=url_for(trim(url.split('||')[0])) target="_blank" |
||||
title=url.split('||')[1] === undefined ? '' : trim(url.split('||')[1])) |
||||
i(class=icon style=url.split('||')[2] === undefined ? '' : `color: ${trim(url.split('||')[2]).replace(/[\'\"]/g, '')};`) |
@ -0,0 +1,47 @@ |
||||
- var htmlClassHideAside = theme.aside.enable && theme.aside.hide ? 'hide-aside' : '' |
||||
- page.aside = is_archive() ? theme.aside.display.archive: is_category() ? theme.aside.display.category : is_tag() ? theme.aside.display.tag : page.aside |
||||
- var hideAside = !theme.aside.enable || page.aside === false ? 'hide-aside' : '' |
||||
- var pageType = is_post() ? 'post' : 'page' |
||||
|
||||
doctype html |
||||
html(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside) |
||||
head |
||||
include ./head.pug |
||||
body |
||||
if theme.preloader.enable |
||||
!=partial('includes/loading/index', {}, {cache: true}) |
||||
|
||||
if theme.background |
||||
#web_bg |
||||
|
||||
!=partial('includes/sidebar', {}, {cache: true}) |
||||
|
||||
if page.type !== '404' |
||||
#body-wrap(class=pageType) |
||||
include ./header/index.pug |
||||
|
||||
main#content-inner.layout(class=hideAside) |
||||
if body |
||||
div!= body |
||||
else |
||||
block content |
||||
if theme.aside.enable && page.aside !== false |
||||
include widget/index.pug |
||||
|
||||
- var footerBg = theme.footer_bg |
||||
if (footerBg) |
||||
if (footerBg === true) |
||||
- var footer_bg = bg_img |
||||
else |
||||
- var footer_bg = isImgOrUrl(theme.footer_bg) ? `background-image: url('${url_for(footerBg)}')` : `background: ${footerBg}` |
||||
else |
||||
- var footer_bg = '' |
||||
|
||||
footer#footer(style=footer_bg) |
||||
!=partial('includes/footer', {}, {cache: true}) |
||||
|
||||
else |
||||
include ./404.pug |
||||
|
||||
include ./rightside.pug |
||||
include ./additional-js.pug |
@ -0,0 +1,33 @@ |
||||
#loading-box |
||||
.loading-left-bg |
||||
.loading-right-bg |
||||
.spinner-box |
||||
.configure-border-1 |
||||
.configure-core |
||||
.configure-border-2 |
||||
.configure-core |
||||
.loading-word= _p('loading') |
||||
|
||||
script. |
||||
(()=>{ |
||||
const $loadingBox = document.getElementById('loading-box') |
||||
const $body = document.body |
||||
const preloader = { |
||||
endLoading: () => { |
||||
$body.style.overflow = '' |
||||
$loadingBox.classList.add('loaded') |
||||
}, |
||||
initLoading: () => { |
||||
$body.style.overflow = 'hidden' |
||||
$loadingBox.classList.remove('loaded') |
||||
} |
||||
} |
||||
|
||||
preloader.initLoading() |
||||
window.addEventListener('load',() => { preloader.endLoading() }) |
||||
|
||||
if (!{theme.pjax && theme.pjax.enable}) { |
||||
document.addEventListener('pjax:send', () => { preloader.initLoading() }) |
||||
document.addEventListener('pjax:complete', () => { preloader.endLoading() }) |
||||
} |
||||
})() |
@ -0,0 +1,4 @@ |
||||
if theme.preloader.source === 1 |
||||
include ./fullpage-loading.pug |
||||
else |
||||
include ./pace.pug |
@ -0,0 +1,11 @@ |
||||
script. |
||||
window.paceOptions = { |
||||
restartOnPushState: false |
||||
} |
||||
|
||||
document.addEventListener('pjax:send', () => { |
||||
Pace.restart() |
||||
}) |
||||
|
||||
link(rel="stylesheet", href=url_for(theme.preloader.pace_css_url || theme.asset.pace_default_css)) |
||||
script(src=url_for(theme.asset.pace_js)) |
@ -0,0 +1,23 @@ |
||||
mixin articleSort(posts) |
||||
.article-sort |
||||
- var year |
||||
- posts.each(function (article) { |
||||
- let tempYear = date(article.date, 'YYYY') |
||||
- let no_cover = article.cover === false || !theme.cover.archives_enable ? 'no-article-cover' : '' |
||||
- let title = article.title || _p('no_title') |
||||
if tempYear !== year |
||||
- year = tempYear |
||||
.article-sort-item.year= year |
||||
.article-sort-item(class=no_cover) |
||||
if article.cover && theme.cover.archives_enable |
||||
a.article-sort-item-img(href=url_for(article.path) title=title) |
||||
if article.cover_type === 'img' |
||||
img(src=url_for(article.cover) alt=title onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'`) |
||||
else |
||||
div(style=`background: ${article.cover}`) |
||||
.article-sort-item-info |
||||
.article-sort-item-time |
||||
i.far.fa-calendar-alt |
||||
time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))= date(article.date, config.date_format) |
||||
a.article-sort-item-title(href=url_for(article.path) title=title)= title |
||||
- }) |
@ -0,0 +1,129 @@ |
||||
mixin postUI(posts) |
||||
each article , index in page.posts.data |
||||
.recent-post-item |
||||
- |
||||
let link = article.link || article.path |
||||
let title = article.title || _p('no_title') |
||||
const position = theme.cover.position |
||||
let leftOrRight = position === 'both' |
||||
? index%2 == 0 ? 'left' : 'right' |
||||
: position === 'left' ? 'left' : 'right' |
||||
let post_cover = article.cover |
||||
let no_cover = article.cover === false || !theme.cover.index_enable ? 'no-cover' : '' |
||||
- |
||||
if post_cover && theme.cover.index_enable |
||||
.post_cover(class=leftOrRight) |
||||
a(href=url_for(link) title=title) |
||||
if article.cover_type === 'img' |
||||
img.post-bg(src=url_for(post_cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title) |
||||
else |
||||
div.post-bg(style=`background: ${post_cover}`) |
||||
.recent-post-info(class=no_cover) |
||||
a.article-title(href=url_for(link) title=title) |
||||
if (is_home() && (article.top || article.sticky > 0)) |
||||
i.fas.fa-thumbtack.sticky |
||||
= title |
||||
.article-meta-wrap |
||||
if (theme.post_meta.page.date_type) |
||||
span.post-meta-date |
||||
if (theme.post_meta.page.date_type === 'both') |
||||
i.far.fa-calendar-alt |
||||
span.article-meta-label=_p('post.created') |
||||
time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))=date(article.date, config.date_format) |
||||
span.article-meta-separator | |
||||
i.fas.fa-history |
||||
span.article-meta-label=_p('post.updated') |
||||
time.post-meta-date-updated(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated))=date(article.updated, config.date_format) |
||||
else |
||||
- let data_type_updated = theme.post_meta.page.date_type === 'updated' |
||||
- let date_type = data_type_updated ? 'updated' : 'date' |
||||
- let date_icon = data_type_updated ? 'fas fa-history' :'far fa-calendar-alt' |
||||
- let date_title = data_type_updated ? _p('post.updated') : _p('post.created') |
||||
i(class=date_icon) |
||||
span.article-meta-label=date_title |
||||
time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format) |
||||
if (theme.post_meta.page.categories && article.categories.data.length > 0) |
||||
span.article-meta |
||||
span.article-meta-separator | |
||||
i.fas.fa-inbox |
||||
each item, index in article.categories.data |
||||
a(href=url_for(item.path)).article-meta__categories #[=item.name] |
||||
if (index < article.categories.data.length - 1) |
||||
i.fas.fa-angle-right.article-meta-link |
||||
if (theme.post_meta.page.tags && article.tags.data.length > 0) |
||||
span.article-meta.tags |
||||
span.article-meta-separator | |
||||
i.fas.fa-tag |
||||
each item, index in article.tags.data |
||||
a(href=url_for(item.path)).article-meta__tags #[=item.name] |
||||
if (index < article.tags.data.length - 1) |
||||
span.article-meta-link #[='•'] |
||||
|
||||
mixin countBlockInIndex |
||||
- needLoadCountJs = true |
||||
span.article-meta |
||||
span.article-meta-separator | |
||||
i.fas.fa-comments |
||||
if block |
||||
block |
||||
span.article-meta-label= ' ' + _p('card_post_count') |
||||
|
||||
if theme.comments.card_post_count && theme.comments.use |
||||
case theme.comments.use[0] |
||||
when 'Disqus' |
||||
when 'Disqusjs' |
||||
+countBlockInIndex |
||||
a.disqus-count(href=full_url_for(link) + '#post-comment') |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Valine' |
||||
+countBlockInIndex |
||||
a(href=url_for(link) + '#post-comment') |
||||
span.valine-comment-count(data-xid=url_for(link)) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Waline' |
||||
+countBlockInIndex |
||||
a(href=url_for(link) + '#post-comment') |
||||
span.waline-comment-count(data-path=url_for(link)) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Twikoo' |
||||
+countBlockInIndex |
||||
a.twikoo-count(href=url_for(link) + '#post-comment') |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Facebook Comments' |
||||
+countBlockInIndex |
||||
a(href=url_for(link) + '#post-comment') |
||||
span.fb-comments-count(data-href=urlNoIndex(article.permalink)) |
||||
when 'Remark42' |
||||
+countBlockInIndex |
||||
a(href=url_for(link) + '#post-comment') |
||||
span.remark42__counter(data-url=urlNoIndex(article.permalink)) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
when 'Artalk' |
||||
+countBlockInIndex |
||||
a(href=url_for(link) + '#post-comment') |
||||
span.artalk-count(data-page-key=url_for(link)) |
||||
i.fa-solid.fa-spinner.fa-spin |
||||
|
||||
//- Display the article introduction on homepage |
||||
case theme.index_post_content.method |
||||
when false |
||||
- break |
||||
when 1 |
||||
.content!= article.description |
||||
when 2 |
||||
if article.description |
||||
.content!= article.description |
||||
else |
||||
- const content = strip_html(article.content) |
||||
- let expert = content.substring(0, theme.index_post_content.length) |
||||
- content.length > theme.index_post_content.length ? expert += ' ...' : '' |
||||
.content!= expert |
||||
default |
||||
- const content = strip_html(article.content) |
||||
- let expert = content.substring(0, theme.index_post_content.length) |
||||
- content.length > theme.index_post_content.length ? expert += ' ...' : '' |
||||
.content!= expert |
||||
|
||||
if theme.ad && theme.ad.index |
||||
if (index + 1) % 3 == 0 |
||||
.recent-post-item.ads-wrap!=theme.ad.index |
@ -0,0 +1 @@ |
||||
.category-lists!= list_categories() |
@ -0,0 +1,2 @@ |
||||
#article-container |
||||
!= page.content |
@ -0,0 +1,82 @@ |
||||
#article-container |
||||
.flink |
||||
- let { content, random, flink_url } = page |
||||
- let pageContent = content |
||||
|
||||
if flink_url || random |
||||
- const linkData = flink_url ? false : site.data.link || false |
||||
script. |
||||
(()=>{ |
||||
const replaceSymbol = (str) => { |
||||
return str.replace(/[\p{P}\p{S}]/gu, "-") |
||||
} |
||||
|
||||
let result = "" |
||||
const add = (str) => { |
||||
for(let i = 0; i < str.length; i++){ |
||||
const replaceClassName = replaceSymbol(str[i].class_name) |
||||
const className = str[i].class_name ? `<h2 id="${replaceClassName}"><a href="#${replaceClassName}" class="headerlink" title="${str[i].class_name}"></a>${str[i].class_name}</h2>` : "" |
||||
const classDesc = str[i].class_desc ? `<div class="flink-desc">${str[i].class_desc}</div>` : "" |
||||
|
||||
let listResult = "" |
||||
const lists = str[i].link_list |
||||
if (!{random === true}) { |
||||
lists.sort(() => Math.random() - 0.5) |
||||
} |
||||
for(let j = 0; j < lists.length; j++){ |
||||
listResult += ` |
||||
<div class="flink-list-item"> |
||||
<a href="${lists[j].link}" title="${lists[j].name}" target="_blank"> |
||||
<div class="flink-item-icon"> |
||||
<img class="no-lightbox" src="${lists[j].avatar}" onerror='this.onerror=null;this.src="!{url_for(theme.error_img.flink)}"' alt="${lists[j].name}" /> |
||||
</div> |
||||
<div class="flink-item-name">${lists[j].name}</div> |
||||
<div class="flink-item-desc" title="${lists[j].descr}">${lists[j].descr}</div> |
||||
</a> |
||||
</div>` |
||||
} |
||||
|
||||
result += `${className}${classDesc} <div class="flink-list">${listResult}</div>` |
||||
} |
||||
|
||||
document.querySelector(".flink").insertAdjacentHTML("afterbegin", result) |
||||
window.lazyLoadInstance && window.lazyLoadInstance.update() |
||||
} |
||||
|
||||
const linkData = !{JSON.stringify(linkData)} |
||||
if (!{Boolean(flink_url)}) { |
||||
fetch("!{url_for(flink_url)}") |
||||
.then(response => response.json()) |
||||
.then(add) |
||||
} else if (linkData) { |
||||
add(linkData) |
||||
} |
||||
})() |
||||
|
||||
else |
||||
if site.data.link |
||||
- let result = "" |
||||
each i in site.data.link |
||||
- let className = i.class_name ? markdown(`## ${i.class_name}`) : "" |
||||
- let classDesc = i.class_desc ? `<div class="flink-desc">${i.class_desc}</div>` : "" |
||||
|
||||
- let listResult = "" |
||||
|
||||
each j in i.link_list |
||||
- |
||||
listResult += ` |
||||
<div class="flink-list-item"> |
||||
<a href="${j.link}" title="${j.name}" target="_blank"> |
||||
<div class="flink-item-icon"> |
||||
<img class="no-lightbox" src="${j.avatar}" onerror='this.onerror=null;this.src="${url_for(theme.error_img.flink)}"' alt="${j.name}" /> |
||||
</div> |
||||
<div class="flink-item-name">${j.name}</div> |
||||
<div class="flink-item-desc" title="${j.descr}">${j.descr}</div> |
||||
</a> |
||||
</div>` |
||||
- |
||||
|
||||
- result += `${className}${classDesc} <div class="flink-list">${listResult}</div>` |
||||
|
||||
- pageContent = result + pageContent |
||||
!= pageContent |
@ -0,0 +1,2 @@ |
||||
.tag-cloud-list.is-center |
||||
!=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 2.1, limit: 0, unit: 'em'}) |
@ -0,0 +1,41 @@ |
||||
- |
||||
var options = { |
||||
prev_text: '<i class="fas fa-chevron-left fa-fw"></i>', |
||||
next_text: '<i class="fas fa-chevron-right fa-fw"></i>', |
||||
mid_size: 1, |
||||
escape: false |
||||
} |
||||
|
||||
if is_post() |
||||
- let prev = theme.post_pagination === 1 ? page.prev : page.next |
||||
- let next = theme.post_pagination === 1 ? page.next : page.prev |
||||
nav#pagination.pagination-post |
||||
if(prev) |
||||
- var hasPageNext = next ? 'pull-left' : 'pull-full' |
||||
.prev-post(class=hasPageNext) |
||||
a(href=url_for(prev.path) title=prev.title) |
||||
if prev.cover_type === 'img' |
||||
img.cover(src=url_for(prev.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt='cover of previous post') |
||||
else |
||||
.cover(style=`background: ${prev.cover || 'var(--default-bg-color)'}`) |
||||
.pagination-info |
||||
.label=_p('pagination.prev') |
||||
.prev_info=prev.title |
||||
|
||||
if(next) |
||||
- var hasPagePrev = prev ? 'pull-right' : 'pull-full' |
||||
.next-post(class=hasPagePrev) |
||||
a(href=url_for(next.path) title=next.title) |
||||
if next.cover_type === 'img' |
||||
img.cover(src=url_for(next.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt='cover of next post') |
||||
else |
||||
.cover(style=`background: ${next.cover || 'var(--default-bg-color)'}`) |
||||
.pagination-info |
||||
.label=_p('pagination.next') |
||||
.next_info=next.title |
||||
else |
||||
nav#pagination |
||||
.pagination |
||||
if is_home() |
||||
- options.format = 'page/%d/#content-inner' |
||||
!=paginator(options) |
@ -0,0 +1,23 @@ |
||||
if theme.post_copyright.enable && page.copyright !== false |
||||
- let author = page.copyright_author || config.author |
||||
- let authorHref = page.copyright_author_href || theme.post_copyright.author_href || config.url |
||||
- let url = page.copyright_url || page.permalink |
||||
- let info = page.copyright_info || _p('post.copyright.copyright_content', theme.post_copyright.license_url, theme.post_copyright.license, config.url, config.title) |
||||
.post-copyright |
||||
.post-copyright__author |
||||
span.post-copyright-meta |
||||
i.fas.fa-circle-user.fa-fw |
||||
= _p('post.copyright.author') + ": " |
||||
span.post-copyright-info |
||||
a(href=authorHref)=author |
||||
.post-copyright__type |
||||
span.post-copyright-meta |
||||
i.fas.fa-square-arrow-up-right.fa-fw |
||||
= _p('post.copyright.link') + ": " |
||||
span.post-copyright-info |
||||
a(href=url_for(url))= theme.post_copyright.decode ? decodeURI(url) : url |
||||
.post-copyright__notice |
||||
span.post-copyright-meta |
||||
i.fas.fa-circle-exclamation.fa-fw |
||||
= _p('post.copyright.copyright_notice') + ": " |
||||
span.post-copyright-info!= info |
@ -0,0 +1,13 @@ |
||||
.post-reward |
||||
.reward-button |
||||
i.fas.fa-qrcode |
||||
= theme.reward.text || _p('donate') |
||||
.reward-main |
||||
ul.reward-all |
||||
each item in theme.reward.QR_code |
||||
- var clickTo = item.link ? item.link : item.img |
||||
li.reward-item |
||||
a(href=url_for(clickTo) target='_blank') |
||||
img.post-qr-code-img(src=url_for(item.img) alt=item.text) |
||||
.post-qr-code-desc=item.text |
||||
|
@ -0,0 +1,61 @@ |
||||
- const { readmode, translate, darkmode, aside, chat_btn } = theme |
||||
mixin rightsideItem(array) |
||||
each item in array |
||||
case item |
||||
when 'readmode' |
||||
if is_post() && readmode |
||||
button#readmode(type="button" title=_p('rightside.readmode_title')) |
||||
i.fas.fa-book-open |
||||
when 'translate' |
||||
if translate.enable |
||||
button#translateLink(type="button" title=_p('rightside.translate_title'))= translate.default |
||||
when 'darkmode' |
||||
if darkmode.enable && darkmode.button |
||||
button#darkmode(type="button" title=_p('rightside.night_mode_title')) |
||||
i.fas.fa-adjust |
||||
when 'hideAside' |
||||
if aside.enable && aside.button && page.aside !== false |
||||
button#hide-aside-btn(type="button" title=_p('rightside.aside')) |
||||
i.fas.fa-arrows-alt-h |
||||
when 'toc' |
||||
if showToc |
||||
button#mobile-toc-button.close(type="button" title=_p("rightside.toc")) |
||||
i.fas.fa-list-ul |
||||
when 'chat' |
||||
if chat_btn |
||||
button#chat-btn(type="button" title=_p("rightside.chat")) |
||||
i.fas.fa-sms |
||||
when 'comment' |
||||
if commentsJsLoad |
||||
a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment")) |
||||
i.fas.fa-comments |
||||
|
||||
#rightside |
||||
- const { enable, hide, show } = theme.rightside_item_order |
||||
- const hideArray = enable ? hide && hide.split(',') : ['readmode','translate','darkmode','hideAside'] |
||||
- const showArray = enable ? show && show.split(',') : ['toc','chat','comment'] |
||||
|
||||
|
||||
#rightside-config-hide |
||||
if hideArray |
||||
+rightsideItem(hideArray) |
||||
#rightside-config-show |
||||
if enable |
||||
if hide |
||||
button#rightside-config(type="button" title=_p("rightside.setting")) |
||||
i.fas.fa-cog.fa-spin |
||||
else |
||||
if is_post() |
||||
if (readmode || translate.enable || (darkmode.enable && darkmode.button)) |
||||
button#rightside-config(type="button" title=_p("rightside.setting")) |
||||
i.fas.fa-cog.fa-spin |
||||
else if translate.enable || (darkmode.enable && darkmode.button) |
||||
button#rightside-config(type="button" title=_p("rightside.setting")) |
||||
i.fas.fa-cog.fa-spin |
||||
|
||||
if showArray |
||||
+rightsideItem(showArray) |
||||
|
||||
button#go-up(type="button" title=_p("rightside.back_to_top")) |
||||
span.scroll-percent |
||||
i.fas.fa-arrow-up |
@ -0,0 +1,18 @@ |
||||
#sidebar |
||||
#menu-mask |
||||
#sidebar-menus |
||||
.avatar-img.is-center |
||||
img(src=url_for(theme.avatar.img) onerror=`onerror=null;src='${theme.error_img.flink}'` alt="avatar") |
||||
.sidebar-site-data.site-data.is-center |
||||
a(href=url_for(config.archive_dir) + '/') |
||||
.headline= _p('aside.articles') |
||||
.length-num= site.posts.length |
||||
a(href=url_for(config.tag_dir) + '/' ) |
||||
.headline= _p('aside.tags') |
||||
.length-num= site.tags.length |
||||
a(href=url_for(config.category_dir) + '/') |
||||
.headline= _p('aside.categories') |
||||
.length-num= site.categories.length |
||||
|
||||
hr.custom-hr |
||||
!=partial('includes/header/menu_item', {}, {cache: true}) |
@ -0,0 +1,15 @@ |
||||
script. |
||||
(() => { |
||||
const abcjsInit = () => { |
||||
const abcjsFn = () => { |
||||
document.querySelectorAll(".abc-music-sheet").forEach(ele => { |
||||
ABCJS.renderAbc(ele, ele.innerHTML, {responsive: 'resize'}) |
||||
}) |
||||
} |
||||
|
||||
typeof ABCJS === 'object' ? abcjsFn() |
||||
: getScript('!{url_for(theme.asset.abcjs_basic_js)}').then(abcjsFn) |
||||
} |
||||
|
||||
window.pjax ? abcjsInit() : window.addEventListener('load', abcjsInit) |
||||
})() |
@ -0,0 +1,6 @@ |
||||
if theme.abcjs && theme.abcjs.enable |
||||
if theme.abcjs.per_page |
||||
if is_post() || is_page() |
||||
include ./abcjs.pug |
||||
else if page.abcjs |
||||
include ./abcjs.pug |
@ -0,0 +1,3 @@ |
||||
link(rel='stylesheet' href=url_for(theme.asset.aplayer_css) media="print" onload="this.media='all'") |
||||
script(src=url_for(theme.asset.aplayer_js)) |
||||
script(src=url_for(theme.asset.meting_js)) |
@ -0,0 +1,35 @@ |
||||
- const { server, site } = theme.artalk |
||||
|
||||
script. |
||||
(() => { |
||||
const getArtalkCount = async() => { |
||||
try { |
||||
const eleGroup = document.querySelectorAll('#recent-posts .artalk-count') |
||||
const keyArray = Array.from(eleGroup).map(i => i.getAttribute('data-page-key')) |
||||
|
||||
const headerList = { |
||||
method: 'POST', |
||||
headers: { |
||||
'Content-Type': 'application/x-www-form-urlencoded', |
||||
'Origin': window.location.origin |
||||
}, |
||||
body: new URLSearchParams({ |
||||
'site_name': '!{site}', |
||||
'type':'page_comment', |
||||
'page_keys': keyArray |
||||
}) |
||||
} |
||||
|
||||
const res = await fetch('!{server}/api/stat', headerList) |
||||
const result = await res.json() |
||||
|
||||
keyArray.forEach((key, index) => { |
||||
eleGroup[index].textContent = result.data[key] || 0 |
||||
}) |
||||
} catch (err) { |
||||
console.error(err) |
||||
} |
||||
} |
||||
|
||||
window.pjax ? getArtalkCount() : window.addEventListener('load', getArtalkCount) |
||||
})() |
@ -0,0 +1,25 @@ |
||||
- const { shortname, apikey } = theme.disqus |
||||
script. |
||||
(() => { |
||||
const getCount = async () => { |
||||
try { |
||||
const eleGroup = document.querySelectorAll('#recent-posts .disqus-count') |
||||
const cleanedLinks = Array.from(eleGroup).map(i => `thread:link=${i.href.replace(/#post-comment$/, '')}`); |
||||
|
||||
const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{shortname}&api_key=!{apikey}&${cleanedLinks.join('&')}`,{ |
||||
method: 'GET' |
||||
}) |
||||
const result = await res.json() |
||||
|
||||
eleGroup.forEach(i => { |
||||
const cleanedLink = i.href.replace(/#post-comment$/, '') |
||||
const urlData = result.response.find(data => data.link === cleanedLink) || { posts: 0 } |
||||
i.textContent = urlData.posts |
||||
}) |
||||
} catch (err) { |
||||
console.error(err) |
||||
} |
||||
} |
||||
|
||||
window.pjax ? getCount() : window.addEventListener('load', getCount) |
||||
})() |
@ -0,0 +1,18 @@ |
||||
- const fbSDKVer = 'v16.0' |
||||
- const fbSDK = theme.messenger.enable ? `https://connect.facebook.net/${theme.facebook_comments.lang}/sdk/xfbml.customerchat.js#xfbml=1&version=${fbSDKVer}` : `https://connect.facebook.net/${theme.facebook_comments.lang}/sdk.js#xfbml=1&version=${fbSDKVer}` |
||||
|
||||
script. |
||||
(()=>{ |
||||
function loadFBComment () { |
||||
if (typeof FB === 'object') FB.XFBML.parse(document.getElementById('recent-posts')) |
||||
else { |
||||
let ele = document.createElement('script') |
||||
ele.setAttribute('src','!{fbSDK}') |
||||
ele.setAttribute('async', 'true') |
||||
ele.setAttribute('defer', 'true') |
||||
ele.setAttribute('crossorigin', 'anonymous') |
||||
document.body.appendChild(ele) |
||||
} |
||||
} |
||||
window.pjax ? loadFBComment() : window.addEventListener('load', loadFBComment) |
||||
})() |
@ -0,0 +1,16 @@ |
||||
case theme.comments.use[0] |
||||
when 'Twikoo' |
||||
include ./twikoo.pug |
||||
when 'Disqus' |
||||
when 'Disqusjs' |
||||
include ./disqus.pug |
||||
when 'Valine' |
||||
include ./valine.pug |
||||
when 'Waline' |
||||
include ./waline.pug |
||||
when 'Facebook Comments' |
||||
include ./fb.pug |
||||
when 'Remark42' |
||||
include ./remark42.pug |
||||
when 'Artalk' |
||||
include ./artalk.pug |
@ -0,0 +1,18 @@ |
||||
- const { host, siteId, option } = theme.remark42 |
||||
|
||||
script. |
||||
(()=>{ |
||||
window.remark_config = Object.assign({ |
||||
host: '!{host}', |
||||
site_id: '!{siteId}', |
||||
},!{JSON.stringify(option)}) |
||||
|
||||
function getCount () { |
||||
const s = document.createElement('script') |
||||
s.src = remark_config.host + '/web/counter.js' |
||||
s.defer = true |
||||
document.head.appendChild(s) |
||||
} |
||||
|
||||
window.pjax ? getCount() : window.addEventListener('load', getCount) |
||||
})() |
@ -0,0 +1,37 @@ |
||||
script. |
||||
(() => { |
||||
const getCommentUrl = () => { |
||||
const eleGroup = document.querySelectorAll('#recent-posts .article-title') |
||||
let urlArray = [] |
||||
eleGroup.forEach(i=>{ |
||||
urlArray.push(i.getAttribute('href')) |
||||
}) |
||||
return urlArray |
||||
} |
||||
|
||||
const getCount = () => { |
||||
const runTwikoo = () => { |
||||
twikoo.getCommentsCount({ |
||||
envId: '!{theme.twikoo.envId}', |
||||
region: '!{theme.twikoo.region}', |
||||
urls: getCommentUrl(), |
||||
includeReply: false |
||||
}).then(function (res) { |
||||
document.querySelectorAll('#recent-posts .twikoo-count').forEach((item,index) => { |
||||
item.textContent = res[index].count |
||||
}) |
||||
}).catch(function (err) { |
||||
console.log(err) |
||||
}) |
||||
} |
||||
|
||||
if (typeof twikoo === 'object') { |
||||
runTwikoo() |
||||
} else { |
||||
getScript('!{url_for(theme.asset.twikoo)}').then(runTwikoo) |
||||
} |
||||
} |
||||
|
||||
window.pjax ? getCount() : window.addEventListener('load', getCount) |
||||
|
||||
})() |
@ -0,0 +1,20 @@ |
||||
script. |
||||
(() => { |
||||
function loadValine () { |
||||
function initValine () { |
||||
let initData = { |
||||
el: '#vcomment', |
||||
appId: '#{theme.valine.appId}', |
||||
appKey: '#{theme.valine.appKey}', |
||||
serverURLs: '#{theme.valine.serverURLs}' |
||||
} |
||||
|
||||
const valine = new Valine(initData) |
||||
} |
||||
|
||||
if (typeof Valine === 'function') initValine() |
||||
else getScript('!{url_for(theme.asset.valine)}').then(initValine) |
||||
} |
||||
|
||||
window.pjax ? loadValine() : window.addEventListener('load', loadValine) |
||||
})() |
@ -0,0 +1,21 @@ |
||||
- const serverURL = theme.waline.serverURL.replace(/\/$/, '') |
||||
script. |
||||
(() => { |
||||
async function loadWaline () { |
||||
try { |
||||
const eleGroup = document.querySelectorAll('#recent-posts .waline-comment-count') |
||||
const keyArray = Array.from(eleGroup).map(i => i.getAttribute('data-path')) |
||||
|
||||
const res = await fetch(`!{serverURL}/api/comment?type=count&url=${keyArray}`, { method: 'GET' }) |
||||
const result = await res.json() |
||||
|
||||
result.data.forEach((count, index) => { |
||||
eleGroup[index].textContent = count |
||||
}) |
||||
} catch (err) { |
||||
console.error(err) |
||||
} |
||||
} |
||||
|
||||
window.pjax ? loadWaline() : window.addEventListener('load', loadWaline) |
||||
})() |
@ -0,0 +1,50 @@ |
||||
//- https://chatra.io/help/api/ |
||||
script. |
||||
(() => { |
||||
const isChatBtn = !{theme.chat_btn} |
||||
const isChatHideShow = !{theme.chat_hide_show} |
||||
|
||||
if (isChatBtn) { |
||||
const close = () => { |
||||
Chatra('minimizeWidget') |
||||
Chatra('hide') |
||||
} |
||||
|
||||
const open = () => { |
||||
Chatra('openChat', true) |
||||
Chatra('show') |
||||
} |
||||
|
||||
window.ChatraSetup = { |
||||
startHidden: true |
||||
} |
||||
|
||||
window.chatBtnFn = () => { |
||||
const isShow = document.getElementById('chatra').classList.contains('chatra--expanded') |
||||
isShow ? close() : open() |
||||
} |
||||
} else if (isChatHideShow) { |
||||
window.chatBtn = { |
||||
hide: () => { |
||||
Chatra('hide') |
||||
}, |
||||
show: () => { |
||||
Chatra('show') |
||||
} |
||||
} |
||||
} |
||||
|
||||
(function(d, w, c) { |
||||
w.ChatraID = '#{theme.chatra.id}' |
||||
var s = d.createElement('script') |
||||
w[c] = w[c] || function() { |
||||
(w[c].q = w[c].q || []).push(arguments) |
||||
} |
||||
s.async = true |
||||
s.src = 'https://call.chatra.io/chatra.js' |
||||
if (d.head) d.head.appendChild(s) |
||||
})(document, window, 'Chatra') |
||||
|
||||
})() |
||||
|
||||
|
@ -0,0 +1,45 @@ |
||||
script. |
||||
(() => { |
||||
window.$crisp = []; |
||||
window.CRISP_WEBSITE_ID = "!{theme.crisp.website_id}"; |
||||
(function () { |
||||
d = document; |
||||
s = d.createElement("script"); |
||||
s.src = "https://client.crisp.chat/l.js"; |
||||
s.async = 1; |
||||
d.getElementsByTagName("head")[0].appendChild(s); |
||||
})(); |
||||
$crisp.push(["safe", true]) |
||||
|
||||
const isChatBtn = !{theme.chat_btn} |
||||
const isChatHideShow = !{theme.chat_hide_show} |
||||
|
||||
if (isChatBtn) { |
||||
const open = () => { |
||||
$crisp.push(["do", "chat:show"]) |
||||
$crisp.push(["do", "chat:open"]) |
||||
} |
||||
|
||||
const close = () => { |
||||
$crisp.push(["do", "chat:hide"]) |
||||
} |
||||
|
||||
close() |
||||
$crisp.push(["on", "chat:closed", function() { |
||||
close() |
||||
}]) |
||||
|
||||
window.chatBtnFn = () => { |
||||
$crisp.is("chat:visible") ? close() : open() |
||||
} |
||||
} else if (isChatHideShow) { |
||||
window.chatBtn = { |
||||
hide: () => { |
||||
$crisp.push(["do", "chat:hide"]) |
||||
}, |
||||
show: () => { |
||||
$crisp.push(["do", "chat:show"]) |
||||
} |
||||
} |
||||
} |
||||
})() |
@ -0,0 +1,40 @@ |
||||
//- https://guide.daocloud.io/daovoice/javascript-api-5869833.html |
||||
script. |
||||
(() => { |
||||
(function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/!{theme.daovoice.app_id}.js","daovoice") |
||||
|
||||
const isChatBtn = !{theme.chat_btn} |
||||
const isChatHideShow = !{theme.chat_hide_show} |
||||
|
||||
daovoice('init', { |
||||
app_id: '!{theme.daovoice.app_id}',},{ |
||||
launcher: { |
||||
disableLauncherIcon: isChatBtn |
||||
}, |
||||
}); |
||||
daovoice('update'); |
||||
|
||||
if (isChatBtn) { |
||||
window.chatBtnFn = () => { |
||||
const isShow = document.getElementById('daodream-messenger').classList.contains('daodream-messenger-active') |
||||
isShow ? daovoice('hide') : daovoice('show') |
||||
} |
||||
} else if (isChatHideShow) { |
||||
window.chatBtn = { |
||||
hide: () => { |
||||
daovoice('update', {},{ |
||||
launcher: { |
||||
disableLauncherIcon: true |
||||
} |
||||
}) |
||||
}, |
||||
show: () => { |
||||
daovoice('update', {}, { |
||||
launcher: { |
||||
disableLauncherIcon: false |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
})() |
@ -0,0 +1,10 @@ |
||||
if theme.chatra && theme.chatra.enable |
||||
include ./chatra.pug |
||||
else if theme.tidio && theme.tidio.enable |
||||
include ./tidio.pug |
||||
else if theme.daovoice && theme.daovoice.enable |
||||
include ./daovoice.pug |
||||
else if theme.crisp && theme.crisp.enable |
||||
include ./crisp.pug |
||||
else if theme.messenger && theme.messenger.enable |
||||
include ./messenger.pug |
@ -0,0 +1,44 @@ |
||||
- let { pageID, lang } = theme.messenger |
||||
- lang = theme.comments.use && theme.comments.use.includes('Facebook Comments') ? theme.facebook_comments.lang : lang |
||||
|
||||
#fb-customer-chat.fb-customerchat(page_id=pageID attribution='biz_inbox') |
||||
|
||||
script. |
||||
(() => { |
||||
document.getElementById('fb-root') ? '' : document.body.insertAdjacentHTML('afterend', '<div id="fb-root"></div>') |
||||
|
||||
window.fbAsyncInit = function() { |
||||
FB.init({ |
||||
xfbml: true, |
||||
version: 'v16.0' |
||||
}); |
||||
}; |
||||
|
||||
(function(d, s, id) { |
||||
var js, fjs = d.getElementsByTagName(s)[0]; |
||||
if (d.getElementById(id)) return; |
||||
js = d.createElement(s); js.id = id; |
||||
js.src = 'https://connect.facebook.net/!{lang}/sdk/xfbml.customerchat.js'; |
||||
fjs.parentNode.insertBefore(js, fjs); |
||||
}(document, 'script', 'facebook-jssdk')); |
||||
|
||||
const isChatBtn = !{theme.chat_btn} |
||||
const isChatHideShow = !{theme.chat_hide_show} |
||||
|
||||
if (isChatBtn) { |
||||
window.chatBtnFn = () => { |
||||
const isShow = document.querySelector('.fb_customer_chat_bounce_in_v2') |
||||
isShow ? FB.CustomerChat.hide() : FB.CustomerChat.show() |
||||
} |
||||
} else if (isChatHideShow) { |
||||
window.chatBtn = { |
||||
hide: () => { |
||||
FB.CustomerChat.hide() |
||||
}, |
||||
show: () => { |
||||
FB.CustomerChat.show(false) |
||||
} |
||||
} |
||||
} |
||||
})() |
||||
|
@ -0,0 +1,45 @@ |
||||
script(src=`//code.tidio.co/${theme.tidio.public_key}.js` async) |
||||
script. |
||||
(() => { |
||||
const isChatBtn = !{theme.chat_btn} |
||||
const isChatHideShow = !{theme.chat_hide_show} |
||||
|
||||
if (isChatBtn) { |
||||
let isShow = false |
||||
const close = () => { |
||||
window.tidioChatApi.hide() |
||||
isShow = false |
||||
} |
||||
|
||||
const open = () => { |
||||
window.tidioChatApi.open() |
||||
window.tidioChatApi.show() |
||||
isShow = true |
||||
} |
||||
|
||||
const onTidioChatApiReady = () => { |
||||
window.tidioChatApi.hide() |
||||
window.tidioChatApi.on("close", close) |
||||
} |
||||
if (window.tidioChatApi) { |
||||
window.tidioChatApi.on("ready", onTidioChatApiReady) |
||||
} else { |
||||
document.addEventListener("tidioChat-ready", onTidioChatApiReady) |
||||
} |
||||
|
||||
window.chatBtnFn = () => { |
||||
if (!window.tidioChatApi) return |
||||
isShow ? close() : open() |
||||
} |
||||
} else if (isChatHideShow) { |
||||
window.chatBtn = { |
||||
hide: () => { |
||||
window.tidioChatApi && window.tidioChatApi.hide() |
||||
}, |
||||
show: () => { |
||||
window.tidioChatApi && window.tidioChatApi.show() |
||||
} |
||||
} |
||||
} |
||||
})() |
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue