diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..89b4ec64 --- /dev/null +++ b/404.html @@ -0,0 +1,14 @@ +
个人主页 www.ohmykreee.top 涅槃重生啦 ,快去围观吧!
(怎么这文案一股营销号的味道)
本来按照惯例,整完一个项目是要写一篇在做这个项目中学到的东西。但是因为这次懒癌犯了(我不掩饰了,来咬我啊咬我啊~~),这次就只写写如果要将 Next.js 项目渲染为静态网页并发布在 GitHub Pages 时要注意的几点。
(其实是那些知识点有点杂,并且是涉及某个特定的库的问题,没啥太大的参考价值。如果想要一起学习的话可以考虑直接看项目源代码,反正整个项目体量也不是很大。)
使用命令 next build && next export
就可以将整个项目渲染为静态的 HTML 文件,并且输出到 out 文件夹。默认情况下不需要额外的配置。
如果想要进一步简化操作,也可以编辑 package.json
关于 build
的命令:
"scripts": {
+ "build": "next build && next export"
+}
+
然后运行 npm run build
就行了。
其实 GitHub Pages 并不是一个单纯的静态网页提供服务,而是一个 Jekyll 服务(用官方的话讲,就是:GitHub Pages is powered by Jekyll.)而 在 Jekyll 生成最终的网站前会忽略所有开头带有 _
的文件夹和文件。
但是好巧不巧:在 Next.js 生成的文件中,一些关键文件是存放在 _next
文件夹中的(我觉得是故意的),导致了如果直接把生成的文件 push 到 GitHub Pages 上时,整个网页将会崩掉。
解决方法嘛,很简单,就在根目录放一个名字为 .nojekyll
的空文件,这样 GitHub Pages 在生成最终的网页时会强制跳过 Jekyll 的处理。
如果使用 GitHub actions,可以在流程文件里这样写:
- name: Build static page
+ run: |
+ npm run build
+ touch ./out/.nojekyll
+
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
如果是全新安装(并且使用的是 Minimal 安装的话),是没有预装 Docker 的。如果有请先卸载。
查询是否有 Docker :
rpm -qa | grep Docker
+
卸载 Docker :
yum remove docker docker-common docker-selinux docker-engine
+
安装依赖:
yum install -y yum-utils device-mapper-persistent-data lvm2
+
下载 repo 文件:
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
+
替换软件仓库地址(此处为 TUNA )
(什么时候我们学校也搞一个开源软件镜像库啊?)
sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
+
安装 Docker :
yum makecache fast
+yum install -y docker-ce
+
启动 Docker :
systemctl start docker
+
列出所有容器:
docker ps -a
+
启动一个容器(使用容器 ID ):
docker start *containerID*
+
同理,停止 stop
,重启 restart
,清除 rm
,查看映射端口 port
,查看日志 logs
。
创建容器并后台运行与进入后台容器:
docker run -itd --name *yourname* *container*
+docker exec -it *containerID*
+
四舍五入安卓系统是 Linux,四舍五入可以在安卓系统上直接进行开发。
四舍五入又水了一篇
得益于强大的 Termux,我们可以在安卓系统上玩一些 奇奇怪怪 的东西。
一切都开始于那一天:当我刷着B站,主页刷到了一个关于 code-server 的视频,然后从评论区里得知原来 Termux 还可以安装 Linux 发行版。这我就来劲了啊:本来当时买这个板子的时候就想着能不能用它进行一些简单的开发,结果发现 GitHub Web Editor 这个官方的在线编辑器只能进行文本编辑。现在既然 Termux 里能安装(几乎)全功能的 Linux 发行版,那可玩性就不止一点了。而且操作下来也非常的简单,可以说 Termux 永远的神好吧(误)。
当我被告知 Termux 可以安装 Linux 发行版的时候,我就知道,它小命就不保了。
—— Kreee
(如果你还是嫌麻烦的话/不喜欢纯命令行的环境,我也发现了一个别人做的挺好的一个整合:AidLux ,开箱即用的那种,而且初始就配备了图形化界面,并且(好像)配置好了 GPU 加速的 AI 开发环境。至于为啥我不用那个,其实当我知道那个软件的底层就是 Termux 的时候,我就有点没有兴致了:既然就是个整合,为啥我不自己整一个呢?)
proot-distro
安装 Termux 的话, F-Droid(推荐)和 Google Play Store 上都有,再不行酷安上也有得下,这里我就不贴链接了。
安装完之后,直接使用包安装管理器安装 proot-distro
就行了:
pkg install proot proot-distro
+
如果说下载慢的话,可以考虑一下换源为清华镜像源。
直接执行 proot-distro list
就可以查看可供安装的 Linux 发行版。
如果对储存空间和性能有一定要求的同学,可以考虑使用 Alpine Linux 发行版。这里因为要追求比较完整的 Linux 体验(以及自己对 Ubuntu 比较熟悉),就选择了 Ubuntu 发行版:
proot-distro install ubuntu
+
等它把必要文件下载好了之后,就可以使用 proot-distro login ubuntu
进入 Ubuntu 了!
code-server
这个 Ubuntu 环境拿到手了,第一步当然是换源为国内源了。由于这个环境还没有文本编辑器,还得先安装一个:
apt install vim
+
安装的时候按提示配置好地区后,就可以根据这个文章换清华源了。(注意几点:一是因为是 arm64 环境,要用 ubuntu-ports 的镜像源,二是默认安装的 Ubuntu 版本是最新的(截止写这篇博文的时候是 21.10),改镜像源的时候要注意一下)
之后就是常规的安装一些需要的软件:
apt install git curl
+
安装 code-server 也很傻瓜化,直接下载并运行安装脚本就行:
curl -fsSL https://code-server.dev/install.sh | sh
+
安装完了之后再顺便改一下 code-server
的配置文件:
cd ~/.config/code-server/
+cp config.yaml config.yaml.bak # 修改配置文件前备份原文件是个好习惯
+vim config.yaml
+
整个配置文件的结构是这样的:
bind-addr: 127.0.0.1:8080
+auth: password
+password: mew...22 # 随机生成的密码
+cert: false
+
我个人习惯是保留第一行不变,然后把第二行的 auth
方法改为 none
。
至于为啥第一行不改的原因是我第二行选择验证方式为不验证,而为了安全(在 VSCode 里是能够直接访问本机的命令行的),仅允许来自本机的连接;而如果我想临时允许局域网内的设备连接的话,我就在运行的时候加上 --bind-addr=0.0.0.0:8080
就行了。如果你有这样的需求的话,也可以直接在配置文件里把第一行改为 0.0.0.0:8080
。
保存完配置文件后,(可能)要先退出一下 Ubuntu 环境(直接输入 exit
就行),然后再用 proot-distro login ubuntu
进入,运行 code-server
命令,把 Termux 挂在后台,打开任意现代的浏览器访问 localhost:8080
,就能愉快玩耍了!
其实呢,安卓设备的性能毕竟有限,一些大型的项目我猜放这上面开发体验也不是很好。不过前端开发对设备性能要求不高,并且这也是我一开始就想实现的。
其实也不难,直接使用 NVM 的安装脚本 ,重开一下 Ubuntu 环境,安装个 Node.js,项目的话直接用 git 从 GitHub 上 Clone 到本地,就可以愉快玩耍了。
如果想要编辑安卓储存空间里的文件的话,可以使用 termux-setup-storage 来允许 Termux 访问安卓储存空间。具体操作的话因为我也没去搞,大家就看看官方文档就行。
Hugo 的话我没从 Snap Store 上下载,而是直接使用了 apt
里的 Hugo,虽然版本不是最新版的,但是 我懒啊 (而且 Snap 还要自己另外装,试了一遍不知道为啥没成功)
Python 的话我也整了一个。直接在 Ubuntu 环境下安装:
apt install python3
+apt install pip
+
然后在 VSCode 里安装 Python Debugger 插件就行了。首次使用要选择 Python 解释器。虽然我不知道为啥触屏模式下最下面那一栏不能拿手戳(包括设置里的好多下拉菜单),选不了 Python 解释器,最后还是外接了一个鼠标给解决了。运行效率还没有测试,但是谁知道呢,也就写着玩玩,大项目不还得在电脑上跑。
(应某人的需求)按照常理也是可以配置 C 开发环境的,只要安装一个 gcc
和一个 C Debugger 插件就行。但是由于不知为何写这篇博文的时候插件因为网络原因没办法安装,之后再试试。
至于其他玩法嘛,完整的 Linux 环境都摆在这里了,只要软件有 arm64 架构的包,都能安装。甚至你还可以安装一个 termux-api
插件访问安卓系统的一些 API,至于那些玩法就以后去探索吧。
(以及,这篇博文就是在这个开发环境下写的哦!又多了一个不开电脑的理由。)
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
废话不多说,上 /.github/workflows/publish-site.yml
:
name: Publish site to GitHub Pages
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repo
+ uses: actions/checkout@master
+ with:
+ submodules: true
+
+ - name: Setup Hugo
+ uses: peaceiris/actions-hugo@v2
+ with:
+ hugo-version: 'latest'
+ # extended: true
+
+ - name: Build
+ run: hugo
+
+ - name: Deploy to GitHub Pages
+ if: success()
+ uses: crazy-max/ghaction-github-pages@v2
+ with:
+ target_branch: gh-pages
+ build_dir: public
+ fqdn: blog.ohmykreee.top
+ dry_run: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
其实本人强烈推荐大家自己去 官方文档 学习相关语法 + 看示例文件。下面就作为本人的学习成果(半吊子程度),分析一下这个 GitHub Action 流程文件。
on:
+ push:
+ branches:
+ - main
+
一目了然:在 main
分支有新的 push 事件时触发。
- name: Checkout Repo
+ uses: actions/checkout@master
+ with:
+ submodules: true
+
这里有一个关键:submodules
参数一定要记得传递 true
, 因为如果你的 Hugo 项目用了模板,并且模板文件同样是托管在 GitHub 上,那么在创建项目和 push 项目时是要将模板设置为 SubModules 的。
然鹅默认情况下 git pull
是不会拉下 SubModules 内的文件(同时也需要自己手动更新 SubModules 的文件),也就导致了如果不声明 SubModules 的存在,到时候在 GitHub Action 的服务器上,准备被渲染的项目文件里就会缺少模板文件,然后就无了。
- name: Setup Hugo
+ uses: peaceiris/actions-hugo@v2
+ with:
+ hugo-version: 'latest'
+ # extended: true
+
一目了然,设置好 Hugo 环境。可以传递参数指定 Hugo 版本,以及是否使用 extended 版本。
- name: Build
+ run: hugo
+
也是一目了然,执行 hugo
命令。
gh-pages
分支并发布该包 Action marketplace 主页
官方文档:GitHub Actions 的上下文和表达式语法
官方文档:工作流程中的身份验证
- name: Deploy to GitHub Pages
+ if: success()
+ uses: crazy-max/ghaction-github-pages@v2
+ with:
+ target_branch: gh-pages
+ build_dir: public
+ fqdn: blog.ohmykreee.top
+ dry_run: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
还是一目了然(有完没完啊),如果上一步没有返回错误的话,push 生成的文件到 gh-pages
分支。
可以传递参数有:
target_branch
:目标分支;build_dir
:待上传文件夹;fqdn
:CNAME file 内容,用于自定义域名;dry_run
:测试用,运行但不 push 代码。接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
目录:
安装版本: Ubuntu server 20.04 LTS
跟着指示走就行。
+安装中要求输入的用户名与密码为之后需要登陆用的普通账号,如果需要提权操作需要 sudo
命令,且密码为自己账号的密码。
在安装阶段的时候就提示是否安装 OpenSSH。
同时,需要启用防火墙并设置端口:
sudo ufw enable
+sudo allow ssh
+sudo ufw reload
+
查看当前状态:
sudo ufw status
+
更改包安装管理器设置文件:
sudo vim /etc/apt/sources.list
+
然后根据提示添加 Tuna 源。
sudo apt update
+# 更新软件列表
+sudo apt dist-upgrade
+# 更新软件,单运行 upgrade 可能会导致更新系统
+
参考: https://help.ubuntu.com/community/AutomaticSecurityUpdates
sudo dpkg-reconfigure --priority=low unattended-upgrades
+
使用默认设置(一天检查一次)即可。
因为被网络桥接和 NAT 彻底整“破防”,一气之下决定:
在系统里虚拟出一个 OpenWRT !
(2022/2/7 更新)
找到了一个项目:lakinduakash / linux-wifi-hotspot,可以一步部署一个简单的无线路由器。
添加 ppa 包并安装:
sudo add-apt-repository ppa:lakinduakash/lwh
+sudo apt install linux-wifi-hotspot
+
手动安装 dnsmasq
(如果使用 NAT 方式):
apt install dnsmasq
+
编辑 /etc/create_ap.conf
(示例文件)
GATEWAY=192.168.6.1
+SHARE_METHOD=nat
+COUNTRY=CN
+WIFI_IFACE=wlp7s0
+INTERNET_IFACE=enp1s0
+SSID=MyAccessPoint
+PASSPHRASE=MyPassword
+
使用 systemctl start create_ap
启动 AP, systemctl enable create_ap
开机启动。
sudo cp /usr/lib/systemd/system/serial-getty@.service /etc/systemd/system/serial-getty@ttyS0.service
+sudo systemctl daemon-reload
+sudo systemctl start serial-getty@ttyS0.service
+sudo systemctl enable serial-getty@ttyS0.service
+
查看当前状态和开放端口:
sudo ufw status
+
开放端口:
sudo ufw allow 8000/tcp
+sudo ufw allow 7000
+sudo ufw allow from 192.168.6.0/24 to any port 25577
+
删除已经添加的规则:
# 列出已有规则的编号
+sudo ufw status numbered
+# 根据编号删除规则
+sudo ufw delete 3
+sudo ufw reload
+
sudo snap install core; sudo snap refresh core
+sudo snap install --classic certbot
+sudo ln -s /snap/bin/certbot /usr/bin/certbot
+
安装:
sudo add-apt-repository ppa:mumble/release
+sudo apt-get update
+sudo apt-get install mumble-server
+sudo dpkg-reconfigure mumble-server
+
开放端口64738
。
配置 /etc/mumble-server.ini
文件。
服务名为mumble-server
。
因为奇奇怪怪的连接问题(指墙 Github),所以使用离线安装模式。
curl -s https://my-netdata.io/kickstart.sh > kickstart.sh
+
+# Netdata tarball
+curl -s https://api.github.com/repos/netdata/netdata/releases/latest | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4 | wget -qi -
+
+# Netdata checksums
+curl -s https://api.github.com/repos/netdata/netdata/releases/latest | grep "browser_download_url.*txt" | cut -d '"' -f 4 | wget -qi -
+
+# Netdata dependency handling script
+# 奇奇怪怪的是经常失败,需要手动创建
+wget -q - https://raw.githubusercontent.com/netdata/netdata/master/packaging/installer/install-required-packages.sh
+
+# go.d plugin
+# For binaries for OS types and architectures not listed on [go.d releases](https://github.com/netdata/go.d.plugin/releases/latest), kindly open a github issue and we will do our best to serve your request
+export OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m | sed -e 's/i386/386/g' -e 's/i686/386/g' -e 's/x86_64/amd64/g' -e 's/aarch64/arm64/g' -e 's/armv64/arm64/g' -e 's/armv6l/arm/g' -e 's/armv7l/arm/g' -e 's/armv5tel/arm/g') && curl -s https://api.github.com/repos/netdata/go.d.plugin/releases/latest | grep "browser_download_url.*${OS}-${ARCH}.tar.gz" | cut -d '"' -f 4 | wget -qi -
+
+# go.d configuration
+curl -s https://api.github.com/repos/netdata/go.d.plugin/releases/latest | grep "browser_download_url.*config.tar.gz" | cut -d '"' -f 4 | wget -qi -
+
复制文件到服务器并给予运行权限:
# 不安全,本不应该使用0777权限
+sudo chmod -R 0777 /tmp/netdata
+
运行:
cd /tmp/netdata
+sudo bash ./kickstart.sh --local-files /tmp/netdata/netdata-(version-number-here).tar.gz /tmp/netdata/sha256sums.txt /tmp/netdata/go.d.plugin-(version-number-here).(OS)-(architecture).tar.gz /tmp/netdata/config.tar.gz /tmp/netdata/install-required-packages.sh --disable-telemetry
+
提示:安装时经常出问题的是 install-required-packages.sh
,需要特别关照。
然后就是修改配置文件 /etc/netdata/netdata.conf
。
在配置 SSL 的时候几率发生无法读取证书文件的问题(主要是 privkey.pem )需要参考 https://certbot.eff.org/docs/using.html#where-are-my-certificates 来配置文件的权限。
参考:https://ehang-io.github.io/
先将提前下好的 npc 文件复制到 /tmp/npc
下,并创建配置文件 /etc/np-client.conf
:
[common]
+server_addr=cloud.ip:8024
+conn_type=kcp
+vkey=you_vkey_here
+auto_reconnection=true
+crypt=false
+compress=false
+
使用命令安装:
cd /tmp/npc
+sudo ./npc install -config=/etc/np-client.conf
+
sudo npc start
+
安装 openjdk(最新):
sudo apt install default-jdk
+
老版本的 Minecraft 需要 Java 8 ,需要自己去甲骨文官网下载二进制文件。
参考用的 /etc/systemd/system/*.service
文件:
Java 8 :
[Unit]
+Description=Minecraft Server with Java 8
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=minecraft
+WorkingDirectory=/usr/local/mc_1_7_10/
+ExecStart=/usr/local/jre1.8.0_271/bin/java -jar /usr/local/mc_1_7_10/forge-1.7.10-10.13.4.1558-1.7.10-universal.jar nogui
+
+[Install]
+WantedBy=multi-user.target
+
OpenJDK :
[Unit]
+Description=Minecraft Server
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=minecraft
+WorkingDirectory=/usr/local/mc_1_17/
+ExecStart=/usr/bin/java -jar /usr/local/mc_1_17/server.jar nogui
+
+[Install]
+WantedBy=multi-user.target
+
记得开放端口:
# Minecraft 主端口
+sudo ufw allow 25565
+# Minecraft Rcon 控制端口
+sudo ufw allow 25577
+
懒,没人用,先不整。
安装配置程序:
sudo apt install pppoeconf
+
进行配置:
sudo pppoeconf
+
启动与断连:
# 连接
+pon dsl-provider
+# 断开
+poff dsl-provider
+
就是置顶状态的那个音乐播放器。
想要吗?只需要短短三步哦!
虽然只有短短四个字,但是对于许多萌新(包括我)来说这一步就是个噩梦(误)。
首先,这个东西是因所使用的模板而异。有的模板做的比较好(比如我这个,不得不说德国人是真的严谨),会在配置文件中预留可追加 .css
和 .js
的设置项;而有的模板只会提供 .css
,或者甚至没有。
所以这问题就大了,对于某些人来说添加依赖就是在设置文件里添加三句话的功夫,对于某些人来说就是遥不可及的彼岸(误)。
对于那些模板的配置文件里有位置预留追加 .css
和 .js
的,只需要追加 css 依赖 https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css
和 js 依赖 https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js
https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js
即可。
而对于无法在配置文件中直接添加依赖的,就需要 Overwrite 模板文件了:
找到文件 /themes/<你的主题名>/layouts/_default/baseof.html
,并将该文件复制到 /layouts/_default/baseof.html
,并对后者进行修改:
在 <head>
和 </head>
区域间复制粘贴以下代码:
<!-- require APlayer -->
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
+<script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script>
+<!-- require MetingJS -->
+<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>
+
注意,如果你的模板是通过 submodule 方式导入的,并且在非本地环境进行渲染和发布(如 GitHub Action),在每次模板更新后,最好重复以上步骤确保不会出啥奇奇怪怪的问题。
完事后,如何检测依赖成功被加载呢?
只需要启动 Hugo 内置服务器,打开网页,按下高贵的F12
按钮打开开发者工具。如果一切顺利,可以在控制台里看到两个 outputs :
APlayer v1.10.1 af84efb http://aplayer.js.org
+
+ MetingJS v2.0.1 https://github.com/metowolf/MetingJS
+
如果没有呢?那就是轮到你头痛的时候了😏
也不需要太着急,我也是头痛了一下午才搞定的。毕竟整这东西玩的这就是折腾,不是吗?
当然你这步不想做完全可以,只需要在想添加音乐播放器的地方插入这一行就行:
<meting-js server="netease" type="playlist" id="769332917"></meting-js>
+
相关参数的意义可以在 官方文档 查看详细介绍,这里我就不赘述。
然后我们来讲讲我们的 shortcodes 。为啥要有 shortcodes 呢?其实就是方便我们写文章的时候以最快的速度插入音乐。
老样子,我也是强烈推荐各位能够直接去 官方文档:Shortcodes 学习如何写 shortcodes 。下面就来说说我的 shortcodes 是怎么写的。
首先,新建文件 /layouts/shrotcodes/aplayer.html
,文件名就是你的 shortcodes 的名字。
然后,文件里写:
<meting-js server="{{ .Get "server" }}" type="{{ .Get "type" }}" id="{{ .Get "id" }}"></meting-js>
+
很简单,不是吗?其中 .Get
就是一个能够得到被传递参数的一个方法,这个想必大家一眼就能看出来。其实大家还可以通过一个判断语句来完成即使不声明参数名也能完成参数传递的 shortcodes ,对着官方的示例代码写就能写出来。我由于没这个需求就简单写了这么一行。
完事了之后,只需要在文章想要插入的地方使用刚刚定义的 shortcodes :
# 前面记得补上两个 {{ ,放代码块里也会触发 shortcodes 我也是没想到
+<aplayer server="netease" type="playlist" id="769332917">}}
+
就能达到之前那行 HTML 语句一样的效果。
你以为到这里就完事了?但是我有说需要三步啊。最后一步,也是 Hugo 版本更新遗留问题。
如果你头铁,直接去部署网页;等你部署完了,你就会发现,本来应该出现音乐播放器的地方,竟然一片空白。
不要慌张,和我一起,按下高贵的F12
按钮打开开发者工具,使用元素检查选中本应该出现播放器的地方。然后,你就会在代码查看器里看到一行字:
<!-- raw HTML omitted -->
+
好了不卖关子了,问题就在于新的渲染器 Goldmark 不默认渲染 HTML 代码,甚至用 shortcodes 也不行。
所以,解决方法有两个:
[markup]
部分添加以下内容(以 config.toml
为例,其他格式请参考对应文件的语法): [markup.goldmark]
+ [markup.goldmark.renderer]
+ unsafe = true
+
[markup]
部分添加以下内容(以 config.toml
为例,其他格式请参考对应文件的语法): defaultMarkdownHandler = "blackfriday"
+
So,导致播放器组件不起作用的可能原因分这三种:
如果一切顺利的话,你也可以让自己的博客里充满音乐了!Enjoy!
+
OS: CentOS 7 Minimal
Summary:
kserver.localdomain
hostnamectl set-hostname AnythingYouLike.localdomain
+
When installing:
yum -y update
+yum -y install vim
+yum -y install net-tools
+yum -y install git
+yum -y install unzip
+yum -y install wget
+
yum install -y yum-cron
+
Make it receive security update automatically:
vim /etc/yum/yum-cron.conf
+
Edit the file:
update_cmd = security
+apply_updates = yes
+
Start and auto-run:
systemctl start yum-cron
+systemctl enable yum-cron
+
vim /etc/ssh/sshd_config
+
Remove #
in the following line:
Port 22
+ListenAddress 0.0.0.0
+ListenAddress ::
+PermitRootLogin yes
+
Start and auto-run the SSH service.
systemctl enable sshd
+systemctl start sshd
+
View the local address:
ifconfig -a
+
Check if it is supported
dmesg |grep tty
+
Edit /etc/default/grub
and add:
GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,9600"
+
Update the grub file:
grub2-mkconfig -o /boot/grub2/grub.cfg
+
Reboot the machine.
Next, enable serial-getty
cp /usr/lib/systemd/system/serial-getty@.service /etc/systemd/system/serial-getty@ttyS0.service
+systemctl daemon-reload
+systemctl start serial-getty@ttyS0.service
+systemctl enable serial-getty@ttyS0.service
+
ref: https://www.osradar.com/building-your-own-wireless-access-point-on-top-of-centos7/
+
Install the wireless-tools and hostapd.
yum -y install iw
+yum -y install epel-release
+yum -y install hostapd
+
Config hostapd.
vim /etc/hostapd/hostapd.conf
+
Edit the conf file.
interface=wlp7s0
+hw_mode=g
+channel=6
+ssid=K_server
+utf8_ssid=1
+country_code=CN
+bridge=br-AP
+
Remove the #
in the following lines:
wpa=3
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=TKIP
+rsn_pairwise=CCMP
+wpa_passphrase=YouPassHere
+
Start and auto-run the hostapd
systemctl start hostapd
+systemctl enable hostapd
+
Change zones.
firewall-cmd --permanent -zone=external --change-interface=enp1s0
+firewall-cmd --permanent -zone=internal --change-interface=???
+firewall-cmd --zone=external --add-masquerade --permanent
+firewall-cmd --set-default-zone=internal
+firewall-cmd --zone=internal --add-service=dns --permanent
+firewall-cmd --complete-reload
+
Set up bridge.
nmcli con add con-name br-AP type bridge ifname br-AP autoconnect yes stp no ip4 192.168.6.1/24
+
Set up dhcp.
yum install -y dhcp
+vim /etc/dhcp/dhcpd.conf
+
Edit the conf file.
subnet 192.168.6.0 netmask 255.255.255.0 {
+ range dynamic-bootp 192.168.6.200 192.168.6.250;
+ option broadcast-address 192.168.6.255;
+ option domain-name-server 223.5.5.5, 223.6.6.6;
+ option routers 192.168.6.1;
+}
+
Start and auto-run the dhcp
systemctl start dhcpd
+systemctl enable dhcpd
+
Post-setup: make it more stable.
yum -y install haveged
+systemctl start havaged
+systemctl enable havaged
+
cd /usr/local/
+mkdir websocketd
+git clone https://github.com/joewalnes/web-vmstats
+
Copy websocketd.zip
to the folder /usr/local/websocketd/
and unzip it:
unzip websocketd.zip
+
Then create webvmstats
.
vim /etc/systemd/system/webvmstats.service
+
Add the following content to the file:
[Unit]
+Description=Web-vmstats
+
+[Service]
+ExecStart=/usr/local/websocketd/websocketd --port=8000 --staticdir=/usr/local/web-vmstats/web/ /usr/bin/vmstat -n 1
+
+[Install]
+WantedBy=multi-user.target
+
Reload, enable and start.
systemctl daemon-reload
+systemctl start webvmstats
+systemctl enable webvmstats
+
Add port 8000
to the firewall.
firewall-cmd --add-port=8000/tcp --zone=external --permanent
+firewall-cmd --add-port=8000/tcp --zone=internal --permanent
+
View status:
systemctl status webvmstats -l
+
Or:
journalctl -e -u webvmstats
+
Use the script to install Zerotier:
curl -s https://install.zerotier.com | sudo bash
+
Join network:
zerotier-cli join XXXXXXX
+
Start and auto-run ZeroTier.
systemctl start zerotier-one
+systemctl enable zerotier-one
+
mkdir /usr/local/frp
+
copy the frp.zip to the new folder. Then unzip it.
Edit the frpc.ini
in the client-side. (Stupid error in [ftp]?)
[common]
+server_addr = ?.?.?.?
+server_port = 7000
+pool_count = 2
+authenticate_new_work_conns = true
+authentication_method = token
+token = balabalabalabalabalabala
+
+[minecraft01]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 25565
+remote_port = 25565
+
+[minecraft02]
+type = udp
+local_ip = 127.0.0.1
+local_port = 25565
+remote_port = 25565
+
+[murmur01]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 64738
+remote_port = 64738
+
+[murmur02]
+type = udp
+local_ip = 127.0.0.1
+local_port = 64738
+remote_port = 64738
+
+[ftp01]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 20
+remote_port = 20
+
+[ftp02]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 21
+remote_port = 21
+
+[ftppasv]
+type = udp
+local_ip = 127.0.0.1
+local_port = 20000-23333
+remote_port = 20000-23333
+
+[webvmstat]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 8000
+remote_port = 8000
+
Copy origin .service
file and edit it.
cp /usr/local/frp/systemd/frpc.service /etc/systemd/system/
+vim /etc/systemd/system/frpc.service
+
User=nobody
On the server-side, edit the frps.ini
.
[common]
+bind_addr = 0.0.0.0
+bind_port = 7000
+dashboard_addr = 0.0.0.0
+dashboard_port = 7500
+authentication_method = token
+authenticate_new_work_conns = true
+token = balabalabalabalabalabala
+dashboard_user = admin
+dashboard_pwd = admin
+
Give permission:
chmod -R 700 /usr/local/frp/
+
ref: https://wiki.mumble.info/wiki/Install_CentOS7
+
Install java:
yum install -y java-latest-openjdk.x86_64
+
Upload the server.zip to the /usr/local/mc_1_16_4
or /opt/mc_1_16_4
mkdir /usr/local/mc_1_16_4/mc_1_16_4
+unzip server.zip
+
Create user:
groupadd -r minecraft
+useradd -r -g minecraft -m -d /var/lib/minecraft -s /sbin/nologin minecraft
+chown -R minecraft:minecraft /usr/local/mc_1_16_4
+chmod -R 0770 /usr/local/mc_1_16_4
+
Create mcserver.service
by vim /etc/systemd/system/mcserver.service
[Unit]
+Description=Minecraft Server
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=minecraft
+ExecStartPre=/bin/sleep 10
+ExecStart=/usr/bin/java -jar /usr/local/mc_1_16_4/fabric-server-launch.jar nogui
+WorkingDirectory=/usr/local/mc_1_16_4/
+
+[Install]
+WantedBy=multi-user.target
+
Add port:
vim /etc/firewalld/services/minecraft.xml
+
<?xml version="1.0" encoding="utf-8"?>
+<service>
+ <short>Minecraft</short>
+ <description>Minecraft Server</description>
+ <port protocol="tcp" port="25565" />
+ <port protocol="udp" port="25565" />
+</service>
+
vim /etc/firewalld/services/rcon.xml
+
<?xml version="1.0" encoding="utf-8"?>
+<service>
+ <short>RCON</short>
+ <description>Minecraft RCON</description>
+ <port protocol="tcp" port="25577" />
+ <port protocol="udp" port="25577" />
+</service>
+
firewall-cmd --permanent --add-service=minecraft --zone=internal
+firewall-cmd --permanent --add-service=minecraft --zone=external
+firewall-cmd --permanent --add-service=rcon --zone=internal
+firewall-cmd --complete-reload
+
Install the ftp-daemon
yum -y install vsftpd
+
Edit the conf file:
vim /etc/vsftpd/vsftpd.conf
+
local_enable=NO
+anonymous_enable=YES
+write_enable=YES
+anon_upload_enable=YES
+listen=YES
+
Create a folder for uploading and change the permission:
mkdir /var/ftp/UploadArea
+chown -R ftp:ftp /var/ftp/UploadArea
+chmod -R 777 /var/ftp/UploadArea
+
Can ban users from logging in by adding name in /etc/vsftpd/user_list
Change Selinux settings:
getsebool -a | grep ftp
+setsebool -P ftpd_anon_write on
+setsebool -P ftpd_full_access on
+
Specify the Pasv-port by adding in /etc/vsftpd/vsftpd.conf
pasv_min_port=20000
+pasv_max_port=23333
+
Open port.
firewall-cmd --permanent --add-service=ftp --zone=internal
+firewall-cmd --permanent --add-service=ftp --zone=external
+
Start and auto-run the service.
systemctl start vsftpd
+systemctl enable vsftpd
+
Install the ntp-daemon.
yum install -y ntp
+
Edit the conf file.
vim /etc/ntp.conf
+
Add the following line:
server ntp.ntsc.ac.cn
+server cn.ntp.org.cn
+SYNC_HWCLOCK=yes
+
Start and auto-run the servie.
systemctl start ntpd
+systemctl enable ntpd
+
View stats:
ntpstat
+ntpq -p
+
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
因为某些大家都懂的原因,整个开发都需要在代理环境下进行。
但是问题来了:原先用 Python 写的时候执行 GET 命令用的是第三方库,只需要传递环境变量就能完成代理的设置。然鹅在开发 JavaScript actions 包时,有一个原则就是尽量不要引入第三方包(原因之后会提及),所以 GET 方法只能使用内置库完成。结果写的时候死活都无法从代码层面上让程序走本地建立的代理:要么就是 hostname not resolved
或是 connect reject from 127.0.0.1:443
;也尝试过引入第三方库来解决,结果也是无功而返。
最后忍无可忍,还是使用了 Clash 的 TAP 模式: +Clash TUN 和 TAP 模式 - EdNovas 的小站 讲的挺详细的,再搭配上 官方文档,就能轻松搭建全局的代理。大致流程在这里也提一提:
General
页面的 TAP Device
旁边的 Manage
, 点击 Install
;Settings
页面 Profile Mixin
部分的 YAML
旁边的 Edit
,把官方文档里的配置文件复制进去;General
,关掉 System Proxy
,打开 Mixin
。@actions/core
包说是说官方推荐的工具包,但是整个项目开发完后我只想说一句话:对它是真的无语。
首先, core.setFailed()
方法看名字应该是那种抛出错误同时结束程序的功能对吧?结果本地测试的时候发现程序并没有按预期结束。去翻了翻源码,大无语事件发生了:整个 core.setFailed()
方法就是一行 core.error()
再加上一行设置退出码为 1 ,没了。根本不涉及退出程序的操作。那意思是说我用完 core.setFailed()
之后还要手动 exit()
?那为啥我不直接用 core.error()
加 exit(1)
呢,还能自定义退出码?
其次,所有 core.notice()
,core.warning()
,core.error()
方法在本地测试的时候都不会输出内容到调试工作台,就导致本地测试的时候贼痛苦。最后还是开发了 local-run 功能给本地测试用,顺便也给程序添加一个新功能。
最后,最令人无语的事件发生了。你作为官方推荐的工具包,你就应该提前在环境里安装好对吧?结果呢,并没有。再加上 actions 包在运行前是不会执行 npm install --production
,也就导致了要么不要引入第三方包,要么就提前在 node_modules 文件夹里准备好第三方包。最终的解决方法是在 .gitignore
里加了两行:
/node_modules/*/
+!/node_modules/@actions/
+
提醒一下,第一行不能换成 node_modules/
,不然整个 node_modules 文件夹都不会被跟踪,第二行排除规则自然也不会生效。使用原文这行就会跟踪 node_modules 文件夹的同时不跟踪其下所有文件(夹),第二行规则才会生效。
(更加无语的是,因为最后一个大无语事件,我的 main 分支多了一串 dirty commits。而且如果要真正测试 actions 包的话一定需要创建 Release 而不能直接在当前项目仓库里测试,所以这些 dirty commits 又是不可避免的,就非常的气)
直接用的是这里的实现方法:HTTP GET Request in Node.js Express - Stack Overflow
其实本来复制粘贴就没事了,但是在 JavaScript 中,涉及网络和 I/O 等长时间占用操作默认是异步的。而对于从来没有接触过异步的新手来说,是一个比较麻烦的问题。
(据说 Axios 库挺好的,下次一定试试)
久仰异步大名,听说异步是萌新杀手,今天可是真正见到其真面目。
异步是写 JavaScript 程序必须要过的一个坎,因为很多方法都是异步进行的。而异步问题最关键的一点就是如何判断一个异步方法结束运行了,并得到返回值。主流的话是两种处理方法:Old-style 的 callbacks 方法和最近才被引入的 async/await 方法:
参考文档:What are callbacks? - Node.js
function processData (callback) {
+ fetchData(function (err, data) {
+ if (err) {
+ console.log("An error has occurred. Abort everything!");
+ return callback(err);
+ }
+ data += 1;
+ callback(data);
+ });
+}
+
在这段示例代码中,callback
变量以及传递给 fetchData()
方法的那个函数都是使用了 callbacks 方法。而理解 callbacks 的精髓在于把一个方法作为变量传递给异步的方法,而在异步的方法结束并得到数据后传递回原先传递给它的方法。听起来很绕,不是吗?但是如果你真正理解了你就会感觉这个解决方法绝妙极了:
let data
+processData(function beCalled (result) {
+ data = result
+})
+console.log(data)
+
首先,上面的代码中调用了 processData()
方法,并且把名字为 beCalled()
(实际应用中可以不用命名,直接省略) 并写有 data = result
的方法传递给 processData()
;beCalled()
在 processData()
里有了一个新名字: callback()
。在 processData()
中运行完成后并调用 callback()
方法,会把变量 data
中的数据重新传回到 beCalled()
并传递给 beCalled()
方法的 result
变量中。这样就完成了一次 callbacks。
async/await 功能是最近才被引入的,目的就是让你写异步的时候有一种“回到家的感觉”。
首先,在你想使用这个功能的方法前声明 async
。没有声明 async 的方法是无法使用 await 功能的。
然后你就可以快乐地使用 await
啦!await
的功能其实非常简单:在 await
后跟的 Promise 只有运行完成并返回值后,整个程序才会进行下去,否则就会停在那一行代码上:
async function fun_with_async () {
+ ...
+
+ let a = await this_is_a_promise()
+
+ ...
+}
+
而 Promise 则是一种特殊的方法,会返回方法得到的值以及运行结果,有三种运行状态:pending、fulfilled、rejected。关于怎么写 Promise 可以 直接对着文档抄就行了 :Promise - MDN Web Docs
以及关于 async/await 的高阶用法以及如何更高效地让程序跑得更快也可以在这里学到:Making asynchronous programming easier with async and await - MDN Web Docs
综上,可以看出真正在 JavaScript 编程中写一些复杂的方法是很少用 return
的,更多的是使用 callbacks(毕竟异步这种东西好是好,就是处理起来有点麻烦)。在实际运用中,也可以考虑 callbacks 方法和 async/await 方法同时使用(比如我,我愿称之为把毕生所学都给整上(误))。
主要是用来 debug 用,如果用在 local-run 功能上相比直接传递参数还是麻烦了一点。
GitHub actions 转换输入为环境变量的方法(直接从源代码里截的):INPUT_${name.replace(/ /g, '_').toUpperCase()}
比如 bearer-token
就会被转换为 INPUT_BEARER-TOKEN
。
How to parse command line arguments - Node.js
原文档我觉得已经讲得很清楚了,我这里就简单提一下:
process.argv
会直接返回由跟在后面的参数组成的 array;用反单引号框起来后,用 ${变量名}
来代替变量值,空格都会被保留。
用起来很像 Python 里的 format()
方法。麻麻再也不用担心我用加号痛苦地连接字符串啦。
for in
是给 object 用的;for of
是给 array 用的。
用错了会直接不执行哦。
How do I write files in Node.js? - Node.js
原文档也已经讲得很详细了。就是异步要稍微注意一下。如果之前异步整明白的话,读这个文档也没啥太大压力。
从10月13日开坑,到11月2日 wolf-bites-tweets v2.0.0 推出标志着填坑正式告一段落,这之间长达20多天。20天说长也不长说短也不短,虽然这些天来自学业的压力和部门安排的任务让我并没有大把的时间去做这个项目,在写这个项目的时候也遇到了很多困难和烦心事,但是经历下来我还是挺享受整个过程的。
不知道为啥,我还是挺喜欢解决问题这一过程:从运用搜索引擎寻找资料,到查找一个 bug 背后产生的原因,再到根据已有的资料和别人的解决方法来想出一个适合自己的解决方法。这个过程的确很累,在外人也许看来我在整一些“没用的东西”,但是谁知道呢?或是说,这是因为我在为了实现自己一个小小的需求而“费劲”,是在真正为我自己做一些东西,而不是为了他人或是生计。这何尝不是一种乐趣呢?
20天从入门 JavaScript 到写一个简单的项目,的确有一定的挑战。我也承认我写的项目也有很多不完善的地方,我也有很多东西需要去学习。学习之旅还很长,与君共勉。
到头来,还是 Python 爱我。
—— Kreee
又为:论如何嫖秃 GitHub 服务器
一切都应该从那时说起:在一个月黑风高的夜晚,我翻译博客的 404 页面时,望着空荡荡的页面,心里突发奇想:如果这里能有一个随机展示我喜欢过的推特的一个小组件,是不是很 nice(顺便展示一下我奇怪的 xp )。
说挖坑就挖坑。要实现这个小功能,我打算做两个项目: wolf-bites-tweets 和 wolf-chews-tweets(别问我这个名字咋想出来的)。前者是利用 白嫖 一下 GitHub action 的服务器,每周通过推特官方 API 获取我的推特喜欢列表(顺便也把获取发推列表这个功能也做了,万一我以后也产粮了呢),准备用我最熟悉的 Python 实现;后者就是利用前者获取的数据,把随机抽到的推特展示在网页上。
由于这是我第一次涉及前端的开发,JavaScript 之前也没有涉及过,所以这次填坑之旅最主要也是最难的是 wolf-chews-tweets 这个项目,这篇博文主要的也是这个前端项目。咋说呢,自己开的坑哭着也要自己填完,不是吗😂?
声明变量时没有类型标注是个坏习俗。
—— 某位不知名暴论者
遭了,写习惯了咋办?
—— 某位痛苦的初学者
是的,很痛苦的教训,非常非常痛苦的教训。
在 wolf-bites-tweets 项目来到 1.0.5 的时候,我顺手就开了一个从 main <– dev-1.0.5 的 PR,很快啊,开完了马上就 merge 了。等一切都完成后,突然发现:我 ** README 忘记更新了,就把 PR 给 merge 了。回想起来,其实只要在 main 那里再加一个更新 README 的 commit 就行了,结果我一顿窒息操作,直接把 main 整到了一个很奇怪的状态。
最后,无奈之下,采取了“核弹级”操作(好孩子不要学):Hard reset 加 Force push:
git reset --hard commit_id
+git push origin HEAD --force
+
后果就是后面想要撤销的 commit 的记录都会被丢失。
但是正确操作也是要学会的是吧,事后还是去谷歌上找了这篇讨论:How to revert multiple git commits? - Stack overflow
如果有一个 branch 长这样,其中想要撤销 commits 直到 A 的状态:
Z <-- A <-- B <-- C <--D <-- main <-- HEAD
+
如果这些 commits 中没有 merge 操作时,可以使用 git revert
:
git revert --no-commit A..HEAD
+# 或者使用:
+# git revert --no-commit B^..HEAD
+# 或者怕出错也可以一步一步 revert:
+# git revert --no-commit D
+# git revert --no-commit C
+# git revert --no-commit B
+git commit -m "the commit message for all of them"
+
最后会变成:
Z <-- A <-- B <-- C <-- D <-- [(BCD)^-1] <-- main <-- HEAD
+
另外一种就是使用 git checkout
来恢复,适用于含有 merge 操作的 commits:
git stash #暂存本地修改,如果有的话
+git checkout -f A -- . # 在本地文件上 checkout 到指定 commit 上
+git commit -a
+
最后会变成:
Z <-- A <-- B <-- C <-- D <-- A' <-- main <-- HEAD
+
项目仓库:https://github.com/ohmykreee/wolf-bites-tweets
其实就是个比较简单的 Python 程序,写的也很快,花了两个晚上左右就把第一个版本 1.0.0 给写了出来,又花了几天肝 水 了两个版本。
也没啥技术含量(指很多东西都是对着其他项目照葫芦画瓢),其中一个坑就是在 1.0.0 版本中涉及到了一个关于 list/dict 变量复制的问题:
在 Python 中,如果想要把一个 list 的内容给另外一个变量,想整一些花活时,比如这样:
this_is_a_list = another_list
+
其实这样操作完后在 Python 那里只有一个 list 有着两个不同的名字 this_is_a_list
、another_list
。如果你对其中一个变量进行一些更改时,另外一个变量也会相应发生更改。就很傻逼,不是吗?
正确操作应该是使用 Deep copy:
import copy
+
+this_is_a_list = copy.deepcopy(another_list)
+
这样就会有两个独立互不干扰的 list。
这个操作在版本 1.0.5 以及之后已经弃用了,因为 get_tweets.py
从 pop
删除元素的操作(而且回想起来那种方法好像还存在 bug,有兴趣的可以回看那个版本)改成了 append
添加元素的操作。
项目仓库:https://github.com/ohmykreee/wolf-chews-tweets
准备写两个实现方式:一个是插入推特官方的小部件,一个是用别人造好的轮子 nolimits4web +/ +swiper 来写一个直接展示图片的组件。
对,很痛苦,也绕了一点弯路。
谁叫你用 Windows 做开发呢?
—— 不知名暴论者
mvn 用的是 nvm-windows 这个项目,注意点有这几个:
Program Files
文件夹下,推荐放在 C:\nvm\
或者 D:\nvm\
,不然会得到蜜汁乱码报错(估计是软件不适配中文环境的原因)。Node 的链接文件目录最好也放在这种盘根目录文件夹里。nvm node_mirror <node_mirror_url>
和 nvm npm_mirror <npm_mirror_url>
命令来添加镜像,加速下载。nvm use <node_version>
时,会遭遇报错 exit code 1 一堆乱码
,解决方法就是用管理员权限开一个终端,再执行命令。node
和 npm
命令,暂时没有搞明白原因(可能是系统环境变量没有同步?)。重启解。VS Code 那里又有一个大坑:
如果想在 VS Code 用微软亲儿子 Edge 浏览器调试 Javascript 代码时,里面其实是已经内置了 debugger for Microsoft Edge 的。我本以为这个 debugger 是直接“开箱即用”的:开始调试时,它会启动一个内置本地服务器,然后再打开浏览器开始调试。但事实是它并没有本地服务器这个功能,还得自己整一个本地服务器,不然你就会得到一个第一眼看上去不知所以然的报错:
crbug/1173575, non-JS module files deprecated.
+
还好 VS Code 插件里就有现成的 Live Server 插件。启动了之后在 .vscode/launch.json
里把 url
后面的端口改为开放的端口就行。
只能说这波啊,这波微软欺骗了我单纯的感情(雾)。
准备使用自定义元素来调用生成我想要的内容。
用法大致是用 window.customElements.define
命令来声明一个自定义元素,然后定义一个类来定义这个自定义元素的行为。
不过想说一下,在参考文章里示例代码用的是 constructor()
方法,我认为在某些情况下并不是个好方法:
class WolfChewsElement extends HTMLElement {
+ constructor() {
+ // 必须首先调用 super 方法
+ super();
+
+ // 元素的功能代码写在这里
+ ...
+ }
+}
+
在此之前,我想提醒一下:浏览器渲染 HTML 文件时也是按照从头到尾的顺序读取文件;也就是说,HTML 文件内代码的读取和执行是是受位置先后顺序影响的。
回到这个问题上,constructor()
这个方法是用方法来定义一个类没错,并且是在这个类被创建的时候 立即 执行,也就是说当浏览器读取到这一行的代码时,会立即执行 constructor()
内的代码。于是问题来了:如果你的 constructor()
方法里有读取你自定义元素内属性的代码,并且这个代码执行的时候 DOM 还没有建立,就会无法读取到属性内容并且返回 undifined。
于此同时,在Chrome版本76.0.3809.132(正式版本)(64 位)中,如果你在 constructor()
方法内有读取自定义元素内属性的行为,并且在 HTML 引用 js 文件时在 script
标签上没有添加 defer
属性,浏览器会直接返回 undifined。那这个 defer
又是什么东西呢?这个属性是告诉浏览器这个代码要等整个页面加载完成后再执行。(参考文章: Why do I have to defer customElement definitions? - Stack Overflow)
较好的方法应该是使用 connectedCallback()
,即在自定义元素首次被插入到文档 DOM 节点上时被调用:
class WolfChewsElement extends HTMLElement {
+ connectedCallback() {
+ // 代码写在这里
+ ...
+ }
+
+ this_is_a_function() {
+ ...
+ }
+}
+
关于其他方法可以参考这里:Web Components - MDN Web Docs
生命周期回调
定义在自定义元素的类定义中的特殊回调函数,影响其行为:
- connectedCallback: 当自定义元素第一次被连接到文档DOM时被调用。
- disconnectedCallback: 当自定义元素与文档DOM断开连接时被调用。
- adoptedCallback: 当自定义元素被移动到新文档时被调用。
- attributeChangedCallback: 当自定义元素的一个属性被增加、移除或更改时被调用。
(在这里顺便附上我读取自定义元素的属性的方法,虽然方法不是我自己原创的,但是这个方法可以说是非常好,对于可选参数传递也能很好的读取:)
// fetch value
+const config = {}
+const keys = ['url', 'method', 'index', 'container_id']
+for (let i = 0; i < this.attributes.length; i = i + 1) {
+ if (keys.includes(this.attributes[i].name)) {
+ config[this.attributes[i].name] = this.attributes[i].value
+ }
+}
+
本来说是想直接用推特的 API :statuses/oembed
来直接获取 embeded twitter 的 HTML 代码,结果遇到了跨域错误。
简单来说,跨域(跨域资源共享)就是在一个域名下想要用 XMLHttpRequest
访问另外一个域名下的资源时,出现的情况。
默认情况下,浏览器是默认拒绝跨域的,因为会有跨域攻击的可能存在(除非在发出请求的时候加上请求头: Access-Control-Allow-Origin: *
允许所有来源)。于此同时,对方服务器也需要允许跨域,否则这个跨域访问就无法完成。
但现状是,所有的推特 API 都不支持跨域访问,官方的说法是建议推特 API 只用于后端中。无可奈何,只能使用推特的 widgets.js
来渲染推特,缺点就是加入推特自己的分析框架,而且好像关不掉的样子。
更多信息可以去读读这篇文章:跨源资源共享(CORS) - MDN Web Docs
/**
+* Returns a random integer between min and max
+* Using Math.round() will give you a non-uniform distribution!
+* Both min and max can be randomed
+*/
+_getRandomInt (min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
+
直接从 Stack Overflow 上抄的:Javascript Random Integer Between two Numbers -Stack Overflow,至于原理我还没弄懂(谁叫我数学那么拉呢)。
Ctrl + C, Ctrl + V, work done!
—— 某人的暴言
_httpGet (theUrl) {
+ try{
+ var xmlHttp = new XMLHttpRequest()
+ xmlHttp.open( "GET", theUrl, false ) // false for synchronous request
+ xmlHttp.send( null )
+ return xmlHttp.responseText
+ } catch (e) {
+ throw this._throwException('http request', e)
+ }
+}
+
也是直接从 Stack Overflow 上抄的:HTTP GET request in JavaScript? - Stack Overflow,自己加了个 try{} 和 脱裤子放屁行为(指 catch 了一个错误又顺手丢出去) 。
三个方法,我都觉得不是很优雅。(参考文章: Remove all child elements of a DOM node in JavaScript - Stack Overflow)
parent.textContent = ''
项目中使用的是这个方法。因为原来的父 node 里只有一个 text node,我估计这个方法能行,还没试过父 node 里套了一些奇奇怪怪的东西的情况。
parent.innerHTML = ''
杀伤力极大,直接父 node 里啥都没有了,就是执行速度上要比上面一个要慢一点。在某些情况下不是很好用。
循环执行 parentNode.removeChild()
虽然这个看上去最优雅,但是总觉得这种 暴力 循环哪里看着怪怪的。
while (parentNode.firstChild) {
+ parentNode.removeChild(parentNode).lastChild);
+}
+
可以说是一个很不错的一个 trick ,具体的话也不好说,直接放链接:Make a div into a link - Stack Overflow
目前只想到自动化来缩小项目文件,所以整个配置都会变得很简单。
在此之前要安装全局的 grunt-cli
,作用是调起项目中的 grunt 并运行,这样运行相应任务只要运行 grunt <任务名>
就行了:
npm install -g grunt-cli
+
然后在项目中安装 grunt
、 grunt-contrib-uglify
和 grunt-contrib-cssmin
:
npm install grunt --save-dev
+npm install grunt-contrib-uglify --save-dev
+npm install grunt-contrib-cssmin --save-dev
+
安装完后可以考虑把 node_modules
添加到 .gitignore
里,如果不对包作直接的修改的话。
直接上官方的示例 Gruntfile.js
,然后自己改改:
module.exports = function(grunt) {
+
+ // Project configuration.
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ uglify: {
+ options: {
+ banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+ },
+ build: {
+ src: 'wolf-chews.js',
+ dest: 'wolf-chews.min.js'
+ }
+ },
+ cssmin: {
+ build: {
+ src: 'wolf-chews.css',
+ dest: 'wolf-chews.min.css',
+ }
+ }
+ });
+
+ // Load the plugins.
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-cssmin');
+
+ // Default task(s).
+ grunt.registerTask('build', ['uglify', 'cssmin']);
+
+ };
+
如果之后想要用 GitHub Action 自动执行 grunt
命令,可以在 package.json
里设置测试用命令:
"scripts": {
+ "test": "node -e \"var g = require('grunt'); g.cli.tasks = ['build']; g.cli()\""
+},
+
这样就不用在 Action 流程里安装 grunt-cli
而直接执行 npm test
就行。
编写 publish-to-npm.yml
用 GitHub Action 帮我完成这些任务并发布在 npm 上:
name: Publish to NPM
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: actions/setup-node@v1
+ with:
+ node-version: 10
+ - run: npm install
+ - run: npm test
+ - run: rm -rf .vscode .github
+ - uses: JS-DevTools/npm-publish@v1
+ with:
+ token: ${{ secrets.NPM_TOKEN }}
+ dry-run: false
+
于是,这个大坑就这样先告一段落了。
各位看官可以在 404页面 看到这个功能的实装了!
你以为这就结束了吗?
在另外一个夜黑风高的夜晚,当我躺在床上时,脑海里突然涌现一个想法:既然你都会 Javasript 了,要不你把 Wolf-bites-tweets 用 Javascript 重写一遍吧。
于是,下期预告:Wolf-Bites-Tweets v2.0 的开发(Node.js)
开坑不止,填坑不息。
—— Kreee
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
(据说)Python 期末考的编程题会从这里面抽。
有些没有答案的题目是临时写的,如果写的很烂欢迎反馈。
愿人间没有挂科人。
# by Kreee
+
+num1 = eval(input('请输入第一个数字:'))
+num2 = eval(input('请输入第二个数字:'))
+num3 = eval(input('请输入第三个数字:'))
+
+averNum = (num1 + num2 + num3) / 3
+print('这三个数的平均数为:{}'.format(averNum))
+
# by Kreee
+
+getInput = int(input('请输入一个三位整数:'))
+
+numBit1 = getInput % 10
+numBit2 = getInput // 10 % 10
+numBit3 = getInput // 100 % 10
+outNum = str(numBit1) + str(numBit2) + str(numBit3)
+
+print('该三位数的逆序数为:{}'.format(outNum))
+
# by Kreee
+
+TempStr = input('请输入华氏度:')
+if TempStr[-1] in ['F', 'f', '℉']:
+ C = (eval(TempStr[0: -1]) - 32) / 1.8
+ print('转换后的温度是{:.2f}℃'.format(C))
+else:
+ C = (eval(TempStr) - 32) / 1.8
+ print('转换后的温度是{:.2f}℃'.format(C))
+
# by Kreee
+
+getInput = input('请输入一个字符:')
+outOrd = ord(getInput)
+print('字符 {} 的 Unicode 编码值为:{}'.format(getInput, outOrd))
+
# by 张忆文(文忆天下)
+
+offset = int(input("输入的偏移量为:"))
+inputChar = input("输入单个大写英文字母为:")
+outputCharValue = (ord(inputChar) - ord("A") + offset) % 26
+outputChar = chr(ord("A") + outputCharValue)
+print("经过凯撒加密之后,输出的字符为:" + outputChar)
+
# by Kreee
+
+getShift = input('请输入偏移量(整数):')
+getInput = input('请输入待加密内容(单个英文字母):')
+
+# 检查用户输入
+if getShift.isdigit() == False:
+ print('输入不符合规范:偏移量不是整数!')
+ exit(1)
+if len(getInput) != 1:
+ print('输入不符合规范:未输入单个字符!')
+ exit(1)
+
+# 计算密码
+getShift = int(getShift)
+inOrd = int(ord(getInput))
+if 65 <= inOrd <= 90:
+ outOrd = inOrd + getShift
+ if outOrd > 90:
+ outChr = chr(outOrd - 90 + 64)
+ else:
+ outChr = chr(outOrd)
+elif 97 <= inOrd <= 122:
+ outOrd = inOrd + getShift
+ if outOrd > 122:
+ outChr = chr(outOrd - 122 + 96)
+ else:
+ outChr = chr(outOrd)
+else:
+ print('输入不符合规范:非英文字母!')
+ exit(1)
+print('转换后的字符为: {}'.format(outChr))
+
random.randint(a, b)
方法,随机生成三个100以内的自然数,求三个数的和。# by Kreee
+
+import random
+
+num1 = random.randint(0, 100)
+num2 = random.randint(0, 100)
+num3 = random.randint(0, 100)
+averNum = (num1 + num2 + num3) / 3
+
+print('随机产生的三个数为 {}, {}, {}, 它们的平均数为{}'.format(num1, num2, num3, averNum))
+
round(x, y)
函数,可以将浮点数x保留y位小数。使用键盘,输入两个非零数,求这两个数的商,结果保留两位小数。输出的时候,请注意使用“➗”(十进制Unicode编码:10135)作为连接符表示除号。# by Kreee
+
+num1 = eval(input("请输入第一个数:"))
+num2 = eval(input("请输入第二个数:"))
+
+outNum = round(num1 / num2, 2)
+
+print(str(num1) + chr(10135) + str(num2) + '=' + str(outNum))
+
time.time()
方法能够得到目前的时间点,距离1970年1月1日0时0点0分0秒已经过去了多少秒。已知,1970年1月1日星期四,使用计算机计算得出:# by Kreee
+
+import time
+
+currTime = time.time()
+dayPassed = int(currTime // (60 * 60 * 24))
+weekPassed = int(dayPassed % 7)
+if weekPassed > 3:
+ currWeek = weekPassed - 3
+else:
+ currWeek = weekPassed + 4
+
+print('今天距离1970年1月1日,过了 {} 天,今天是星期{}'.format(dayPassed, currWeek))
+
# by 张忆文(文忆天下)
+
+a, b, c = eval(input("输入一元二次方程的 a,b,c 的值以逗号隔开:"))
+if a == 0:
+ if b == 0:
+ if c == 0:
+ print("该方程有任意解")
+ else:
+ print("该方程无解")
+ else:
+ print("该方程有唯一解且解为x1={}".format(-c/b))
+else:
+ delta = b ** 2 - 4 * a * c
+ if delta < 0:
+ print("ax**2+bx+c=0 这个方程无实数解")
+ elif delta == 0:
+ root = (-b) / (2 * a)
+ print("ax**2+bx+c=0 这个方程有两个相等的根,其值为x1=x2={:.2f}".format(root))
+ else:
+ root1 = ((-b) + delta ** 0.5) / (2 * a)
+ root2 = ((-b) - delta ** 0.5) / (2 * a)
+ print("ax**2+bx+c=0 这个方程有两个不同的根: x1 = {:.2f},其值为x2={:.2f}".format(root1, root2))
+
# by Kreee
+
+getApiNum = int(input('请输入空气污染指数:'))
+
+if getApiNum <= 50:
+ outResult = '优'
+elif 51 <= getApiNum <= 99:
+ outResult = '良'
+elif 100 <= getApiNum <= 199:
+ outResult = '轻度污染'
+elif 200 <= getApiNum <= 299:
+ outResult = '中度污染'
+elif 300 <= getApiNum:
+ outResult = '重度污染'
+
+print('空气质量为:{}'.format(outResult))
+
w = (y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1) % 7
其中:
w:代表星期几;w对7取模得:0-星期日,1-星期一,2-星期二,3-星期三,4-星期四,5-星期五,6-星期六
c:世纪数(注:一般情况下,在公式中取值为已经过的世纪数,也就是年份除以一百的结果,c应该等于所在世纪的编号,如公元2021年,c就等于20)
y:世纪的年数(一般情况下是年份的后两位数,如公元2021年,y就等于21)
m:月份(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算)
d:日
[ ]代表取整,即只要整数部分。
请使用计算机编写程序,输入年、月、日,输出对应星期几。
# by 张忆文(文忆天下)
+
+year = int(input("输入年份:"))
+month = int(input("输入月份:"))
+day = int(input("输入日:"))
+m = month
+
+if month < 3:
+ m = month + 12
+ year = year - 1
+
+c = year // 100
+y = year % 100
+d = day
+w = (y + (y // 4) + (c // 4) - 2 * c + (26 * (m + 1) // 10) + d - 1) % 7
+
+if(w == 1):
+ message = "星期一"
+elif (w == 2):
+ message = "星期二"
+elif (w == 3):
+ message = "星期三"
+elif (w == 4):
+ message = "星期四"
+elif (w == 5):
+ message = "星期五"
+elif (w == 6):
+ message = "星期六"
+else:
+ message = "星期日"
+print("输入{}年{}月{}日,输出{}".format(year, month, day, message))
+
# by 张忆文(文忆天下)
+
+x1, y1 = eval(input("输入一个圆的圆心坐标:"))
+r1 = eval(input("输入圆的半径:"))
+x2, y2 = eval(input("输入另一个圆的圆心坐标:"))
+r2 = eval(input("输入圆的半径:"))
+
+# distance 代表两个圆心坐标的距离
+distance = ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
+
+# 对圆的关系进行判断
+if distance < abs(r1 - r2):
+ print("这两个圆的关系是:内含")
+elif distance == abs(r1 - r2):
+ print("这两个圆的关系是:内切")
+elif distance < r1 + r2:
+ print("这两个圆的关系是:相交")
+elif distance == r1 + r2:
+ print("这两个圆的关系是:外切")
+else:
+ print("这两个圆的关系是:分离")
+
# by 张忆文(文忆天下)
+
+import random
+
+computer = random.randint(0,2)
+you = int(input("石头(0), 剪刀(1), 布(2):"))
+message = "电脑出的是:"
+
+if computer == 0:
+ message = message + "石头(0),你出的是:"
+ if you == 0:
+ message = message + "石头(0),是平局!"
+ elif you == 1:
+ message = message + "剪刀(1),你输了!"
+ else:
+ message = message + "布(2),你赢了!"
+elif computer == 1:
+ message = message + "剪刀(1),你出的是:"
+ if you == 0:
+ message = message + "石头(0),你赢了!"
+ elif you == 1:
+ message = message + "剪刀(1),是平局!"
+ else:
+ message = message + "布(2),你输了!"
+else:
+ message = message + "布(2),你出的是:"
+ if you == 0:
+ message = message + "石头(0),你输了!"
+ elif you == 1:
+ message = message + "剪刀(1),你赢了!!"
+ else:
+ message = message + "布(2),是平局!"
+
+print(message)
+
# by Kreee
+
+import random
+
+getInput = int(input('划拳游戏:石头(整数0)剪刀(整数1)布(整数2):'))
+getRandom = random.randint(0, 2)
+flag = ''
+outUser = ''
+outCom = ''
+
+if getInput == 0:
+ outUser = '石头(0)'
+ if getRandom == 0:
+ outCom = '石头(0)'
+ flag = 'tie'
+ elif getRandom == 1:
+ outCom = '剪刀(1)'
+ flag = 'user'
+ elif getRandom == 2:
+ outCom = '布(2)'
+ flag = 'com'
+elif getInput == 1:
+ outUser = '剪刀(1)'
+ if getRandom == 0:
+ outCom = '石头(0)'
+ flag = 'com'
+ elif getRandom == 1:
+ outCom = '剪刀(1)'
+ flag = 'tie'
+ elif getRandom == 2:
+ outCom = '布(2)'
+ flag = 'user'
+elif getInput == 2:
+ outUser = '布(3)'
+ if getRandom == 0:
+ outCom = '石头(0)'
+ flag = 'user'
+ elif getRandom == 1:
+ outCom = '剪刀(1)'
+ flag = 'com'
+ elif getRandom == 2:
+ outCom = '布(2)'
+ flag = 'tie'
+else:
+ print('非法输入!')
+
+if flag == 'user':
+ print('电脑出的是:{},你出的是:{},你赢了!'.format(outCom, outUser))
+elif flag == 'com':
+ print('电脑出的是:{},你出的是:{},你输了!'.format(outCom, outUser))
+elif flag == 'tie':
+ print('电脑出的是:{},你出的是:{},平局!'.format(outCom, outUser))
+
# by 张忆文(文忆天下)
+
+position = input("输入职位,经理或普通员工:")
+order = eval(input("输入订单总额(单位:万元):"))
+
+if position == "经理":
+ if order > 500:
+ bonus = order * 0.01
+ else:
+ bonus = order * 0.005
+else:
+ if order > 50:
+ bonus = order * 0.02
+ else:
+ bonus = order * 0.01
+
+print("该员工的职位为{},绩效奖金为{}万元".format(position, bonus))
+
# by 张忆文(文忆天下)
+
+import random
+
+card = random.randint(0, 51)
+message = "你抽到的牌是:"
+
+# suit 代表花色,黑桃(0)、红桃(1)、梅花(2)、方片(3),输出花色
+suit = card % 4
+if suit == 0:
+ message = message + "黑桃"
+elif suit == 1:
+ message = message + "红桃"
+elif suit == 2:
+ message = message + "梅花"
+else:
+ message = message + "方片"
+
+# 接下来输出牌面
+cardnumber = (card % 13 + 1)
+if cardnumber == 1:
+ message = message + "A"
+elif cardnumber == 11:
+ message = message + "J"
+elif cardnumber == 12:
+ message = message + "Q"
+elif cardnumber == 13:
+ message = message + "K"
+else:
+ message = message + str(cardnumber)
+
+print(message)
+
# by Kreee
+
+import random
+getRandom = random.randint(0, 51)
+
+pokerList = []
+for suit in ['黑桃', '红桃', '梅花', '方片']:
+ for num in ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']:
+ pokerList.append(suit + num)
+
+print('你抽到的牌是:{}'.format(pokerList[getRandom]))
+
电费 = 第一档用电量(210度)* 第一档电价+第二档用电量(400 - 210度)* 第二档电价+第三档用电量(800 - 400度)* 第三档电价
使用键盘输入某用户的一个月用电量,求该用户需要缴纳多少电费。
# by Kreee
+
+getInput = eval(input('请输入这个月所用电度数:'))
+
+if getInput <= 210:
+ outMoney = getInput * 0.5469
+elif 210 < getInput <= 400:
+ outMoney = 210 * 0.5469 + (getInput - 210) * 0.5969
+elif 400 < getInput:
+ outMoney = 210 * 0.5469 + (400 - 210) * 0.5969 + (getInput - 400) * 0.8469
+
+print('这个月的电费为:{}'.format(outMoney))
+
# by Kreee
+
+outNum = 7
+i = 1
+while outNum < 100:
+ print(outNum, end=' ')
+ i = i + 1
+ outNum = 7 * i
+
(1)
*
+**
+***
+****
+*****
+
(2)
*
+ **
+ ***
+ ****
+*****
+
(3)
*
+ ***
+ *****
+ *******
+*********
+
# by Kreee
+# ps:想改又不愿意改,累了,就这样
+
+print('(1)')
+for i in range(1, 6):
+ printLine = '*' * i
+ print(printLine)
+
+print('(2)')
+for j in range(1, 6):
+ printLine = ' ' * (5 - j) + '*' * j
+ print(printLine)
+
+print('(3)')
+for k in range(1, 6):
+ printLine = ' ' * (5 - k) + '*' * (2 * k - 1)
+ print(printLine)
+
# by 张忆文(文忆天下)
+
+number = 10
+count, sum = 0, 0
+
+while count < number:
+ num = eval(input("输入一个数: "))
+ sum += num
+ count += 1
+
+print("十个数的平均数是:" + str(sum / number))
+
# by Kreee
+
+def CalAverage(Inputnum):
+ getResult = 0.0
+ for i in Inputnum:
+ getResult = getResult + i
+ return getResult / len(Inputnum)
+
+
+print(CalAverage(eval(input('输入一组数,用逗号隔开:'))))
+
# by 张忆文(文忆天下)
+
+rental, year = 100000, 1
+income, month, deposit = 10000, 1, 300000
+
+while year <= 10:
+ print("第{}年的租金为¥{:.2f}".format(year, rental))
+ rental *= 1.05
+ year += 1
+
+while deposit > 0:
+ deposit = deposit - income
+ month += 1
+ income *= 1.07
+
+print("第{}月后能够收回投资".format(month))
+
# by Kreee
+
+# 计算门面租金
+for i in range(0, 10):
+ rent = 100000.00 * pow(1.05, i)
+ print('第{}年的租金为:{:.2f}'.format(i + 1, rent))
+
+# 计算劳动月数
+getMonth = 0
+addMoney = 0
+while addMoney < 300000:
+ addMoney = addMoney + 10000 * pow(1.07, getMonth)
+ getMonth = getMonth + 1
+print('第{}个月能够收回投资。'.format(getMonth + 1))
+
(1)
1
+12
+123
+1234
+12345
+123456
+
(2)
654321
+ 54321
+ 4321
+ 321
+ 21
+ 1
+
(3)
1
+ 12
+ 123
+ 12345
+123456
+
# by Kreee
+print('(1)')
+result1 = ''
+for i in range(1, 7):
+ for num in range(1, i + 1):
+ if num == i:
+ result1 = result1 + str(num) + '\n'
+ else:
+ result1 = result1 + str(num)
+print(result1)
+
+# 方法很屎,没力气想其他更好的方法了,就这样
+print('(2)')
+result2 = ''
+for j in range(6, 0, -1):
+ result2 = result2 + ' ' * (6 - j)
+ for num in range(j, 0, -1):
+ if num == 1:
+ result2 = result2 + str(num) + '\n'
+ else:
+ result2 = result2 + str(num)
+print(result2)
+
+print('(3)')
+result3 = ''
+for k in range(1, 7):
+ result3 = result3 + ' ' * (6 - k)
+ for num in range(1, k + 1):
+ if num == k:
+ result3 = result3 + str(num) + '\n'
+ else:
+ result3 = result3 + str(num)
+print(result3)
+
# by Kreee
+
+def zeller(year, month, date):
+ if month <= 2:
+ month = month + 12
+ year = year - 1
+ week = (date + 26 * (month + 1) // 10 + year % 100 + year % 100 // 4 + year // 100 // 4 + year // 100 * 5 - 1) % 7
+ if week == 1:
+ weekDay = "星期一"
+ elif week == 2:
+ weekDay = "星期二"
+ elif week == 3:
+ weekDay = "星期三"
+ elif week == 4:
+ weekDay = "星期四"
+ elif week == 5:
+ weekDay = "星期五"
+ elif week == 6:
+ weekDay = "星期六"
+ elif week == 0:
+ weekDay = "星期日"
+ return weekDay
+
+
+getYear = int(input('请输入一个年份:'))
+for i in range(1, 13):
+ outWeek = zeller(getYear, i, 1)
+ print('{} 年的 {} 月 1 日是{}'.format(getYear, i, outWeek))
+
# by Kreee
+
+getInput = eval(input('请输入需要转换为二进制的整数:'))
+outBinary = ''
+
+if getInput == 0:
+ outBinary = '0'
+else:
+ divResult = getInput
+ while divResult != 1:
+ if divResult % 2 == 0:
+ outBinary = '0' + outBinary
+ divResult = divResult / 2
+ continue
+ else:
+ outBinary = '1' + outBinary
+ divResult = divResult // 2
+ continue
+ outBinary = '1' + outBinary
+
+print(outBinary)
+
# by 张忆文(文忆天下)
+
+for i in range(1, 10000):
+ sum = 0
+ for j in range(1, i // 2 + 1):
+ if i % j == 0:
+ sum = sum + j
+ if sum == i:
+ print(i, end=" ")
+
# by Kreee
+
+perfectNum = []
+getInput = 10000
+
+for num in range(1, getInput + 1):
+ divisor = []
+ for i in range(1, num):
+ if (num / i) % 1 == 0:
+ divisor.append(i)
+ addDivisor = 0
+ for j in divisor:
+ addDivisor = addDivisor + j
+ if addDivisor == num:
+ perfectNum.append(num)
+
+for k in perfectNum:
+ print(k, end=" ")
+
isLeapYear(year)
的函数,参数为一个年份,如果该年份是闰年,则返回值为True,否则,返回值为False。在同一源文件中,使用键盘输入年份,验证该函数是否能够正确返回该年份是否为闰年。# by 张忆文(文忆天下)
+
+def isLeapYear(year):
+ if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
+ return True
+ else:
+ return False
+
+
+year = int(input("输入一个年份:"))
+print(isLeapYear(year))
+
zeller(year, month, date)
的函数,参数为年、月、日。通过这个函数计算并返回该日期是星期几。在同一源程序中,使用键盘输入年、月、日,验证该函数是否能正确计算出输入的日期为星期几。# by 张忆文(文忆天下)
+
+def zeller(year, month, date):
+ if month <= 2:
+ month += 12
+ year -= 1
+ weekDay = (date + 26 * (month + 1) // 10 + year % 100 + year % 100 // 4 + year // 100 // 4 + year // 100 * 5 - 1) % 7
+ return weekDay
+
+
+year, month, date = eval(input("一次输入年,月,日:"))
+weekDay = zeller(year, month, date)
+print(weekDay)
+
isPrime(number)
的函数,参数为一个正整数。通过使用这个函数,能够判断一个正整数,是否为素数,是素数则返回True,不是素数则返回False。在同一源程序中,使用键盘输入一个正整数,验证该函数是否能够正确判断输入数为素数。# by Kreee
+
+def isPrime(number):
+ # 质数判断方法见下一题。
+ for factor in range(2, number // 2 + 1):
+ if number % factor == 0:
+ return False
+ else:
+ return True
+
+
+getInput = int(input('请输入一个正整数:'))
+ifPrime = isPrime(getInput)
+if ifPrime:
+ print('正整数 {} 是质数。'.format(getInput))
+else:
+ print('正整数 {} 不是质数。'.format(getInput))
+
primeNumbers(number)
的函数,参数为一个正整数。通过使用这个函数,能够输出小于number的所有素数,输出的时候,每行10个素数。在同一源文件中,使用键盘输入一个正整数,验证该函数的输出结果。# by 张忆文(文忆天下)
+
+def primeNumbers(number):
+ message = str(number) + "以内的质数有:\n"
+ count = 0
+ # 外层循环,从2开始,到number结束
+ for num in range(2, number):
+ # 使用一个标志,这个标志为True,假设number是质数
+ flag = True
+ # 内层循环从2开始,到num//2结束,寻找有没有num的因数
+ for factor in range(2, num // 2 + 1):
+ # 如果num可以被2到num//2之间的某个自然数整除
+ if num % factor == 0:
+ # 将标志设为False,意义为:number不是质数
+ flag = False
+ # 如果内层循环结束后,没有找到任何因数,标志保持True状态
+ # 说明num的确是一个质数
+ if flag:
+ count = count + 1
+ # 将这个质数记录到message中。
+ if count % 10 == 0:
+ message = message + str(num) + "\n"
+ else:
+ message = message + str(num) + "\t"
+ print(message)
+
+
+number = int(input("请输入一个正整数:"))
+primeNumbers(number)
+
一个自用的备忘录表,记录一下要用但是又容易忘记的命令。
不断更新中…
目录
git init
+
git remote add origin <remote address>
+git -M main # 切换默认分支到main(master 不再用)
+git pull <local> <remote>
+
git add <file>
+git commit -m 'descriptions'
+git push origin main
+
git branch <branch name> # 创建<branch name>分支
+git checkout <branch name> # 切换至<branch name>分支
+git switch <branch name> # 切换至<branch name>分支
+git checkout -b <branch name> # 创建并切换至<branch name>分支
+git switch -c <branch name> # 创建并切换至<branch name>分支
+git branch # 查看已有分支(* 表示当前分支)
+git merge <branch name> # 合并<branch name>到当前分支(通常在master分支下操作)
+git branch -d <branch name> # 删除分支(本地)
+git push <local> --delete <remote> # 删除分支(远程)
+
git submodule add <submodule address> <local folder> # 添加子库,使用 --force 强制使用本地已有文件
+git submodule foreach git pull #子模块更新
+
sudo update-alternatives --config java
+
根据 nobody - Ubuntu Wiki,不建议将程序的运行权限设置为 nobody:nogroup
,而是额外创建一个新的用户组。
(没办法偷懒了呜呜呜)
(方法来自 How can I create a non-login user? - superuser)
sudo useradd -r <username>
+
将会创建一个组名和用户名都为 <username>
的用户,且无用户目录。
certbot certonly --manual --preferred-challenges dns --email xxxxxx.xxxxx@outlook.com --agree-tos -d *.ohmykreee.top
+
pppoe-setup # 开始交互式操作
+
ifup ppp0
+ifdown ppp0
+
ifconfig ppp0
+pppoe-status
+
docker ps -a
+
docker start/stop/restart/rm/port/logs <container>
+
docker run -itd --name <name> <container> # -it: 使用交互模式并分配一个伪终端
+docker exec -it <container> # -d: 后台运行容器,并返回容器ID
+
注意! 这篇文章仅仅是作为自己边鼓捣边摸索出来的产物,并非为一篇教程,并不能保证所有的内容全部正确,如有错误也欢迎指出。
目录:
直接在官网上下载 ISO 镜像,然后按照提示安装上去就行。
安装中记得记录一下 Summary 与 Install Successful 中的信息,之后可能会用到。
首次安装完成后如果想要访问 ssh、WebGUI 等,需要先连接到安装时指定的管理网口(这里是 enp6s0),再设置电脑网卡将 IP 设置为安装时设置的同子网但是与 pve 主机不同的 IP。如果还是访问不了的话可以试试关闭其他网络连接(比如 WiFi 等)。
由于想要将部分网卡直通给 OPNsense,所以要修改一下设置让 pve 支持硬件直通。
在 WebGUI 的 node 处登录 Shell,修改 grub 文件 /etc/default/grub
:GRUB_CMDLINE_LINUX_DEFAULT=quite
一栏的值为 quite intel_iommu=on
(AMD 的就是amd_iommu=on
)。保存后使用 update-grub
更新。
修改 /etc/modules
文件,添加以下内容:
vfio
+vfio_iommu_type1
+vfio_pci
+vfio_virqfd
+
保存后,重启机器。
运行以下命令行来列出所有 IOMMU 组:
#!/bin/bash
+shopt -s nullglob
+for g in `find /sys/kernel/iommu_groups/* -maxdepth 0 -type d | sort -V`; do
+ echo "IOMMU Group ${g##*/}:"
+ for d in $g/devices/*; do
+ echo -e "\t$(lspci -nns ${d##*/})"
+ done;
+done;
+
由于我有一张无线网卡,想要直通到 OPNsense 所在的虚拟机中。而直接设置直通的话 qemu 会报错:failed to add PCI capability 0x11[0x70]@0x90: table & pba overlap, or they don't fit in BARs, or don't align
。
这里就需要多做一步:修改 pve 的 /etc/pve/qemu-server/[虚拟机ID].conf
,在文件一行加上:
# 这里无线网卡在虚拟机中分配到的是 hostpci4,按需修改
+args: -set device.hostpci4.x-msix-relocation=bar2
+
更多信息可以参考:Failed to PCI passthrough SSD with SMI SM2262 controller. - Kernel.org Bugzilla、PCIe Passthrough of Atheros AR9280 - Promox Forums
进入 pve WebGUI,上传 OPNsense 的 ISO 安装包。
在 pve 节点处新建一个网卡桥接,选择一个与管理网卡不同的网卡,且只填写 Bridge Ports
字段。这里名字为 vmbr1。
新建一个虚拟机,设置参数(记得在 CPU 设置里把 aes 功能打开),添加网络设备 vmbr1 ,在 Hardware
里添加 PCI 设备,先只添加 enp1s0。
注意! 建议在新建虚拟机的时候先不要设置自动启动,以便如果出现了 OPNsense 配置错误导致的 pve WebGUI 访问不了,可以通过强制重启机器来恢复。等全部配置完成并确认没有问题后再设置 OPNsense 虚拟机自动启动。
启动虚拟机,首先进入的是 live mode(演示模式),其中在进入演示模式前会配置网络信息,这里建议手动配置设置好WAN口与LAN口,这里是把连接到光猫的 enp1s0 端口设置为 WAN,桥接网卡 vtnet0 设置为 LAN 口。
在成功进入演示模式后,使用用户名 installer
与密码 opnsense
登录,就能进入安装模式,完成接下来的安装,与设置管理员密码。
安装完成后,重启虚拟机,移除安装介质。
将电脑连接到 enp5s0 对应的网口上,并将电脑的手动地址改回为 DHCP 自动获取地址。使用默认地址 192.168.1.1
登录上 OPNsense 的 WebGUI 后,完成初始设置向导。在设置向导里可以更改 LAN 口地址,防止与光猫的 192.168.1.1
冲突(建议与 pve 的子网相同,原因后面会提及)。应用设置后,等待一段时间(比较长),重新用新的地址访问 WebGUI 界面。
将网线重新插回 enp6s0 对应网口,重新设置电脑地址,将 OPNsense 虚拟机关闭,并添加 enp2s0、enp3s0、enp4s0 网卡。重新启动虚拟机。
按上面的方法回到 OPNsense 的 WebGUI。在 Interfaces ‣ Assignments 把刚刚添加的所有端口都新建一遍,保存设置。再在 Interfaces ‣ [刚刚添加的各网口],把刚刚添加的网口都启用,并应用更改。
在 Interfaces ‣ Other Types ‣ Bridge 里,新建一个 br-LAN
网桥,然后把除最开始添加的 LAN 口外的其他网口全部添加进去。回到 Interface ‣ Assignments,把 LAN 口(即在标识名字后面是 lan 字样的)换到 br-LAN
,保存并应用。此时将断开与 OPNsense 的连接,将电脑连接到其他的 LAN 口上可以重新连接。
连接成功后,按照以上的操作方式把最开始的网口添加进 br-LAN
,保存并测试是否可以访问 OPNsense WebGUI。
在 System ‣ Settings ‣ Tunables 里,将 net.link.bridge.pfil_member
改为 0,net.link.bridge.pfil_bridge
改为 1,修改防火墙行为。
没找到什么其他的好办法。
目前的解决方法是将 OPNsense 与 pve 的 IP 范围设置为同一个子网(比如 192.168.3.x/24
),然后将 pve 的管理端口所在的网桥 vmbr0 添加进 OPNsense VM,在 OPNsense 中将该网口启用并加入到 br-LAN
中。因为 OPNSesne 的 DHCP 服务器默认从 192.168.3.10/24
开始分配 IP 地址,所以给予 pve 静态 IP 地址 192.168.3.2/24
。这样就能从内网通过访问 https://192.168.3.2:8006
来访问 pve WebGUI 了。
当 OPNsense 挂了后,就可以连接管理端口,手动配置 IP 地址在同一子网,来应急连接。
在 Interfaces ‣ Wireless 里创建一个无线网卡的克隆后,再到 Interfaces ‣ Assignments 里添加无线网卡的网口,保存应用。
添加网口成功后,在对应网口设置里启用,并设置以下内容:
Setting | Value |
---|---|
Standard | 802.11na |
Mode | Access Point |
SSID | WiFi名字 |
Allow intra-BSS communication | True |
WPA | Enable WPA |
WPA Pre-Shared Key/EAP Password | WiFi密码 |
WPA Mode | WPA2 |
WPA Key Management Mode | Pre-Shared Keys |
WPA Pairwise | AES |
最后将该无线网口添加到 br-LAN
里就完成了。
首先开启 OPNsense 的 ssh 连接方式:在 System ‣ Settings ‣ Administration 里 Enable Secure Shell,并允许 root 登录与密码登录,保存并应用设置。
使用 ssh 登录 OPNsense,新建一个文件夹 /usr/local/clash
,将 freebsd 版的二进制文件、配置文件、yacd面板文件都放进去。
使用 pw user add clash -c "Clash" -s /usr/sbin/nologin
创建一个无登录的账号,并给予文件所有者为刚刚创建的用户 clash:clash
。完成后就地运行一次进行初始化。
新建文件 /usr/local/etc/rc.d/clash
#!/bin/sh
+# $FreeBSD$
+
+# PROVIDE: clash
+# REQUIRE: LOGIN cleanvar
+# KEYWORD: shutdown
+
+# Add the following lines to /etc/rc.conf to enable clash:
+# clash_enable (bool): Set to "NO" by default.
+# Set to "YES" to enable clash.
+# clash_config (path): Clash config dir.
+# Defaults to "/usr/local/etc/clash"
+
+
+. /etc/rc.subr
+
+name="clash"
+rcvar=clash_enable
+
+
+load_rc_config $name
+
+: ${clash_enable:="NO"}
+: ${clash_config="/usr/local/clash"}
+
+command="/usr/local/clash/clash"
+#pidfile="/var/run/clash.pid"
+required_files="${clash_config}"
+clash_group="clash"
+clash_user="clash"
+
+command_args="-d $clash_config"
+
+run_rc_command "$1"
+
并给予运行权限 chmod +x /usr/local/etc/rc.d/clash
新建文件 /usr/local/opnsense/service/conf/actions.d/actions_clash.conf
[start]
+command:/usr/local/etc/rc.d/clash onestart
+type:script
+message:starting clash
+
+[stop]
+command:/usr/local/etc/rc.d/clash stop
+type:script
+message:stoping clash
+
+[status]
+command:/usr/local/etc/rc.d/clash statusexit 0
+type:script_output
+message:get clash status
+
+[restart]
+command:/usr/local/etc/rc.d/clash onerestart
+type:script
+message:restarting clash
+
并启用 service configd restart
最后,前往 Services ‣ Monit ‣ Settings 里启用 Monit,并在 Service Test Settings 里添加两个:
Setting | Value |
---|---|
Name | Clash |
Condition | failed host 127.0.0.1 port 7890 type tcp |
Action | Restart |
第二个,避免重启死循环
Setting | Value |
---|---|
Name | RestartLimit4 |
Condition | 5 restarts within 5 cycles |
Action | Unmonitor |
在 Service Settings 里添加:
Setting | Value |
---|---|
Name | Clash |
Match | clash |
Start | /usr/local/sbin/configctl clash start |
Stop | /usr/local/sbin/configctl clash stop |
Tests | Clash,RestartLimit4 |
最后等待一段时间,在 Monit ‣ Status 里查看是否运行。
在 Services ‣ Web Proxy ‣ Administration 的 General Proxy Settings 里启用代理,在 Forward Proxy 里启用 Enable Transparent HTTP proxy
、Enable SSL inspection
、Log SNI information only
,并点击每一栏 (i) 按钮中提示文字的 Add a new firewall rule(注意!添加完 NAT 项目后记得应用!)。
再前往 System ‣ Trust ‣ Authorities 处新建一个证书,使用下面的设置:
Setting | Value |
---|---|
Descriptive name | OPNsense-SSL |
Method | Create an internal Certificate Authority |
Key length (bits) | 2048 |
Digest Algorithm | SHA256 |
Lifetime (days) | 356 |
Country Code | NL (Netherlands) |
State or Province | Zuid Holland |
City | Middelharnis |
Organization | OPNsense |
Email Address | spam (at) opnsense.org |
Common Name | opnsense-ssl-ca |
创建证书完成后,回到 Services ‣ Web Proxy ‣ Administration 的 Forward Proxy 里的 CA to use 选择刚刚创建的证书。
设置完成后先不设置上游代理,随便访问一下网页,然后在 Web Proxy ‣ Access Log 里看有没有访问日志。(提醒一点:如果半天发现没有反应,可能是 NAT 创建了项目但是没有应用)
最后,设置上游代理。在 Web Proxy ‣ General Proxy Settings ‣ Parent Proxy Settings 里,启用,并设置为 127.0.0.1:7890
。
偷了一个小懒,请 ChatGPT (3.5) 帮我写了一个这个脚本 update.sh
。只需要把这个脚本放置于任意位置,并配置好,就可以方便快捷更新核心和配置文件。
以下是这个脚本需要修改的地方:
current_directory
为需要执行更新的文件夹,如果按照之前步骤操作则该部分不需要改动;download_config_url
则为配置文件的下载地址,按需求更新;update_core_proxy
和 update_config_proxy
分别是更新内核和配置时所用的 http/socks5 代理,为空则不使用;download_ui_url
为下载 ui.zip
的链接,文件中的链接为 MetaCubeX/metacubexd 面板的下载链接。下载完成后需要自己解压 ui.zip
到相应目录。遵循 update_core_proxy
的设定;repo_owner
,仓库名 repo_name
,文件中提供的是 Clash.Meta 的 Stable 版;repo_filename
中 <version>
会替换为实时获取的最新版本。#!/bin/sh
+
+# 设置当前工作目录的变量
+current_directory="/usr/local/clash"
+
+# 定义下载config.yaml的链接
+download_config_url="http://openmediavault:25500/getprofile?name=profiles/default.ini&token=xxx"
+
+# 定义下载ui.zip的链接
+download_ui_url="https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
+
+# 定义Clash core GitHub仓库信息
+repo_owner="MetaCubeX"
+repo_name="Clash.Meta"
+repo_filename="clash.meta-freebsd-amd64-<version>.gz"
+
+# 添加代理变量
+update_core_proxy="socks5://opnsense:7891"
+update_config_proxy=""
+
+# 定义当前所要执行的更新命令
+current_command=$1
+
+# 帮助文档函数
+print_help() {
+ echo "Usage: $0 [config|core|stop|help]"
+ echo "Commands:"
+ echo " config 更新config.yaml文件"
+ echo " core 下载最新的clash可执行文件并替换"
+ echo " ui 下载最新的ui.zip文件"
+ echo " stop 停止clash进程"
+ echo " help 显示帮助文档"
+}
+
+# 备份并替换文件函数
+backup_file() {
+ if [ -f "$1" ]; then
+ mv -f "$1" "$1.bak"
+ fi
+}
+
+# 停止进程并设置权限
+stop_and_cleanup() {
+ echo "停止 Clash 进程:"
+ chown -R clash:clash $current_directory
+ /usr/local/sbin/configctl clash stop
+}
+
+# 定义信号处理函数
+interrupt_handler() {
+ echo "下载被中止。"
+
+ # 根据当前执行的命令来选择是否替换文件
+ if [ "$current_command" = "core" ]; then
+ # 如果是下载 core,还原备份的clash(如果存在)
+ if [ -f "$current_directory/clash.bak" ]; then
+ cp -f "$current_directory/clash.bak" "$current_directory/clash"
+ echo "已还原备份的Clash。"
+ fi
+ elif [ "$current_command" = "config" ]; then
+ # 如果是下载 config,还原备份的config.yaml.bak(如果存在)
+ if [ -f "$current_directory/config.yaml.bak" ]; then
+ cp -f "$current_directory/config.yaml.bak" "$current_directory/config.yaml"
+ echo "已还原备份的config.yaml.bak。"
+ fi
+ fi
+
+ stop_and_cleanup
+ exit 1
+}
+
+# 设置信号处理程序
+trap interrupt_handler SIGINT
+
+# 获取最新版本的clash可执行文件下载链接函数
+get_core_latest_version() {
+ local proxy_option="$1" # 接受传入的core_proxy作为参数
+
+ # 获取最新发布版本信息
+ release_url="https://api.github.com/repos/$repo_owner/$repo_name/releases/latest"
+ latest_release_info=$(curl $proxy_option -s "$release_url")
+
+ # 从版本信息中提取最新版本号
+ latest_version=$(echo "$latest_release_info" | grep -oE '"tag_name": "[^"]+"' | head -n 1 | cut -d '"' -f 4)
+
+ # 返回最新版本号
+ echo "$latest_version"
+}
+
+# 处理config命令
+if [ "$current_command" = "config" ]; then
+ # 备份并覆盖config.yaml.bak
+ backup_file "$current_directory/config.yaml"
+
+ # 设置代理,如果有的话
+ config_proxy=""
+ [ -n "$update_config_proxy" ] && config_proxy="-x $update_config_proxy"
+
+ # 使用curl下载文件并重命名为config.yaml
+ curl $config_proxy -# -fSL -o "$current_directory/config.yaml" "$download_config_url"
+ download_result=$? # 保存curl命令的退出码
+
+ # 检查下载是否成功
+ if [ $download_result -eq 0 ]; then
+ echo "config.yaml 更新成功!"
+ else
+ echo "下载失败。请检查URL是否正确或网络连接是否正常。"
+ # 如果下载失败,还原备份的config.yaml.bak(如果存在)
+ if [ -f "$current_directory/config.yaml.bak" ]; then
+ cp -f "$current_directory/config.yaml.bak" "$current_directory/config.yaml"
+ echo "已还原备份的config.yaml.bak。"
+ fi
+ fi
+ stop_and_cleanup
+
+# 处理core命令
+elif [ "$current_command" = "core" ]; then
+ # 备份并替换clash文件
+ backup_file "$current_directory/clash"
+
+ # 设置代理,如果有的话
+ core_proxy=""
+ [ -n "$update_core_proxy" ] && core_proxy="-x $update_core_proxy"
+
+ # 获取最新版本的clash可执行文件下载链接
+ latest_version=$(get_core_latest_version "$core_proxy")
+ # 更新 repo_filename,将 <version> 替换为实际的版本号
+ repo_filename=$(echo "$repo_filename" | sed "s/<version>/$latest_version/")
+ # 构建下载链接
+ download_core_url="https://github.com/$repo_owner/$repo_name/releases/download/$latest_version/$repo_filename"
+ echo "下载链接:$download_core_url"
+
+ # 下载最新的clash可执行文件并解压
+ curl $core_proxy -# -fSL "$download_core_url" | gunzip > "$current_directory/clash"
+ download_result=$? # 保存curl命令的退出码
+ chmod +x "$current_directory/clash"
+
+ # 检查是否更新成功
+ if [ $download_result -eq 0 ]; then
+ echo "Clash core 更新成功!"
+ echo "最新版本:$latest_version"
+ else
+ echo "Clash 更新失败。"
+ # 如果更新失败,还原备份的clash(如果存在)
+ if [ -f "$current_directory/clash" ]; then
+ cp -f "$current_directory/clash.bak" "$current_directory/clash"
+ echo "已还原备份的clash。"
+ fi
+ fi
+ stop_and_cleanup
+
+# 处理ui命令
+elif [ "$current_command" = "ui" ]; then
+ # 备份并覆盖ui.zip.bak
+ backup_file "$current_directory/ui.zip"
+
+ # 设置代理,如果有的话
+ ui_proxy=""
+ [ -n "$update_core_proxy" ] && ui_proxy="-x $update_core_proxy"
+
+ # 使用curl下载ui.zip文件到当前目录
+ curl $ui_proxy -# -fSL -o "$current_directory/ui.zip" "$download_ui_url"
+ download_result=$? # 保存curl命令的退出码
+
+ # 检查下载是否成功
+ if [ $download_result -eq 0 ]; then
+ echo "ui.zip 更新成功!请自行解压到指定文件夹中。"
+ else
+ echo "下载 ui.zip 失败。请检查URL是否正确或网络连接是否正常。"
+ [ -f "$current_directory/ui.zip" ] && rm "$current_directory/ui.zip" # 如果下载失败并且文件存在,则删除它
+ fi
+
+# 处理stop命令
+elif [ "$current_command" = "stop" ]; then
+ stop_and_cleanup
+
+# 处理help命令或未知命令
+else
+ print_help
+fi
+
(说实话在和 ChatGPT 协作这个脚本的时候,前期还没太复杂的情况下一切进展得非常顺利,也非常舒心。可等到需求越来越多,代码越来越复杂,ChatGPT 就开始犯各种各样的小问题,最后还是我自己完成了最后代码的修改和整合,可能是我用的模型不太强大吧,等一手某人让我白嫖一个 4.0 模型使用权(误))
有些服务很奇葩,即使在 Clash 规则里设置了直连,也不能用(说的就是你学习xx),估计是拥有某种方法检测透明代理。这里选择创建 NAT 规则将该域名绕过代理。
首先在 Firewall ‣ Aliases 里创建一个条目 NoRedirect1
,类型选择 Hosts,内容为域名或 IP 地址,保存并应用。可以在 Firewall ‣ Diagnostics ‣ Aliases 里选择对应规则集查看域名是否成功解析为IP地址。
其次在 Firewall ‣ NAT 里,在透明代理那两个规则之前再创建一个规则:
Setting | Value |
---|---|
No RDR (NOT) | True |
Interface | LAN |
Protocol | TCP/UDP |
Source | LAN net |
Source port range | any to any |
Destination | NoRedirect1 |
Destination port range | any to any |
与上一步类似,在 Firewall ‣ Aliases 里创建一个条目 ProxyMAC1
,类型选择 MAC address,内容为需要走代理设备的 MAC 地址(设备记得关闭随机 MAC 地址功能),保存并应用。
然后在前几步创建的两个 NAT 规则中,Source 条目从 LAN net
改为 ProxyMAC1
,就可以保存应用了。
没有什么特别的注意点,只是在新建虚拟机的时候选择 vmbr1 作为网络设备,就可以访问互联网。
在 LuCI 的 Network ‣ Interfaces 里删除所有的 WAN 端口。
配置 LAN 端口:
General Settings:
Setting | Value |
---|---|
Protocol | Static address |
Device | br-lan |
IPv4 address | 192.168.3.3/24 |
IPv4 gateway | 192.168.3.1 |
Advanced Settings:
Setting | Value |
---|---|
Use custom DNS servers | 192.168.3.1 |
Delegate IPv6 prefixes | False |
Firewall Settings:
Setting | Value |
---|---|
Create / Assign firewall-zone | unspecified |
DHCP Server:
Setting | Value |
---|---|
Ignore interface | True |
然后把从软路由出来的网线插进无线路由器的 LAN 口就行。
最后常规设置配置 WiFi 就完成了。
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
最近的单曲循环:
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
最近的单曲循环:
懂的都懂这是干啥用的。
requirements.txt
:
pynput==1.7.3
+six==1.16.0
+
大佬建议直接用 requirements.txt
安装依赖。
安装方法(建议使用Pycharm):
新建项目,将代码复制粘贴。
然后在下面的 Terminal 标签页里运行:
pip install pynput
+
话不多说,直接上代码:
import pynput
+import time
+
+# Setting are here:
+timeOfSleep = 5
+timeOfKeyStroke = 0.01
+
+f = open('input.txt', 'w+')
+f.close()
+input('Please edit the input.txt file. When ready, press ENTER...')
+
+inputFile = open('input.txt', 'r', encoding='utf-8')
+inputContents = inputFile.read()
+inputFile.close()
+simKeys = list(inputContents)
+
+print('Will execute key stroke after {} secs...'.format(timeOfSleep))
+time.sleep(timeOfSleep)
+ctr = pynput.keyboard.Controller()
+for i in simKeys:
+ ctr.press(i)
+ time.sleep(timeOfKeyStroke)
+ ctr.release(i)
+
+input('Finished!' + '\n' + 'Warning: text in input.txt will be deleted! Press ENTER to continue...')
+
+f = open('input.txt', 'r+')
+f.truncate()
+f.close()
+
最近的单曲循环:
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
决定建立一个独立的个人博客,撒花🎉🎉
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
最近的单曲循环:
最近的单曲循环:
决定建立一个独立的个人博客,撒花🎉🎉
决定建立一个独立的个人博客,撒花🎉🎉
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
最近的单曲循环:
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
接上文: wolf-bites-tweets 和 wolf-chews-tweets 开发小记
项目地址: https://github.com/ohmykreee/wolf-bites-tweets
为啥在已经有 wolf-bites-tweets v1 的情况下,还要重写并开发 v2.0.0 呢?原因有俩:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
根据 GitHub Pages 的政策,一个 GitHub 账号只能拥有一个个人主页和多个项目主页。我的个人主页名额给了服务器的 Landing Page,所以这个博客只能以项目主页的名义发布了。
然鹅有一个问题是,不同于个人主页,项目主页的网页是要托管在 gh-pages
分支的,所以如果完全手动的话需要我自己在本地渲染好网页后,手动 push 到 gh-pages
分支上。
所以秉承着人类科技进步的本质是懒这一原则,顺便学学 GitHub Action 做到网页渲染和发布一条龙吧。
So, let’s begin!
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!
最近的单曲循环:
最近的单曲循环:
本来用 CentOS 7 用得开开心心的,结果了解到 Redhat 公司要整治一下我们这群白嫖怪(感觉被强行喂了一口💩)。
So, 为了服务器的可持续发展(其实是放假闲得无聊),顺便重装一下机器的系统,以及更新一下远古的备忘指南,Let’s begin!
本来一开始不打算使用 Docker 来配置服务:虽然的确 Docker 能省下配置环境的麻烦,但是总觉得哪里不舒服(误)。
其实是因为服务器不是长时间运行的,会时不时关机/重启,还要重新到 Docker 里启动。
以及据说有安全隐患(其实其他软件配置不当也会有安全隐患233)
但是现在是真的闲的无聊 + 还是有很多软件是基于 Docker 的(还是只提供了 Docker 的安装教程,吐了),故打算核心服务直接部署在服务器里,整一些不重要的东西放在 Docker 里。
Now, let’s begin!