分类目录归档:技术

text

如何用 SSH Config 来优化你的 SSH 连接?

在 Windows 的时候,我一度非常喜欢使用诸如 XShell 之类的软件,主要原因是我可以将不同的服务器信息保持在同一个软件当中,不用每次都手动保存(Putty往往就无法保存)。

而到了 macOS 之后,我不再使用 XShell 这样的软件来完成我的 SSH 工作流(事实上我也找不到类似 XShell 的软件,后来在 Setapp 软件包中发现了 Core Shell,但我已经有了下面的技巧了,就没再用)。

经过一番研究,我发现对于我的诉求来说,其实完全没必要使用一款第三方软件来完成,SSH 自带的功能即可完成。

再次 Review 一下我的诉求:

  1. 我希望不要输入 IP 地址,而是输入一个短语,或者是域名来连接我的服务器。
  2. 我希望能够支持自定义端口,因为我一般会把我的服务器端口从 22 改为一个随机的数字。
  3. 我希望可以指定密钥文件,因为我有多个密钥,不通的场景可能使用不同的密钥。

这些诉求如今我的新方法都可以实现。

SSH 支持自定义 Config 文件,而默认的 Config 文件位于 ~/.ssh/config 这个文件中,你可以在其中添加自己的配置来实现自定义 SSH。

比如这里用到的就是 SSH Config 中的 Host 定义,你可以以如下的规则来定义一个新的配置。

Host 主机名
    User 登录用的用户名
    HostName 登录用的主机名,可以是域名或者IP
    Port 登录用的端口号
    IdentityFile 需要使用的密钥文件

比如下图就是一个配置的例子

d2b5ca33bd970f64a6301fa75ae2eb22 3

通过在你的 Config 文件当中添加对应的定义,你就可以用 ssh 主机名 的方式来连接你的服务器了。

比如,我的树莓派的配置的主机名是 pi 那我就可以使用 ssh pi 的方式来连接到我的树莓派当中。

d2b5ca33bd970f64a6301fa75ae2eb22 5
text

在油猴脚本中实现新增按钮和按钮的点击效果

在油猴脚本中,有些时候,我们需要在界面当中添加一个新的按钮。这个时候我们可以使用 document.getElementById("id").innerHTML=xxx 来指定某个元素中的内容是特定的 HTML,从而实现添加一个新的按钮。

但在这个按钮上绑定事件则不是通过简单的指定 button 的 onclick 来完成的。核心原因是默认情况下,你在油猴脚本中所写的函数只运行在油猴脚本中的 Scope ,而 button 则是运行在 Document 的 Scope 下。直接绑定事件在触发时会无法找到对应的函数。

一个好的办法是为你新增的 Button 带上 ID,并通过 ID 找到对应的 Element 并添加事件绑定来实现。

参考代码如下:

// ==UserScript==
// @name         示例代码
// @namespace    https://www.ixiqin.com
// @version      0.0.1
// @description  示例代码
// @author       bestony
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';
  window.addEventListener('load', function () {
    function viewAuthor() {
      // button click event
    }

    var targetElement =document.getElementById("xxx")

    targetElement[0].innerHTML = targetElement[0].innerHTML + ` &nbsp;<a id="view-author">查看作者</a>`

    var link = document.getElementById("view-author");
    if (link) {
      link.addEventListener("click", viewAuthor, false);
    }
  }, false);
})();
Code language: JavaScript (javascript)
text

油猴脚本不支持 Modules Javascript 导致空白脚本报错

最近在写一个油猴脚本时,因为懒得写 DOM 处理函数,我试图在油猴脚本当中引入 Zepto.js, 用于实现 DOM 操作。

d2b5ca33bd970f64a6301fa75ae2eb22

但在引入 Zepto 之后,发现即使是空白的脚本文件,在网页加载时依然会报错。经过排查发现,是我引入的 Zepto 文件导致的,进一步研究后发现,之所以有这个问题,是目前油猴脚本仍不支持 Modules ,因此,我引入 Zepto 就会报错。

想要解决这个问题倒是也简单,将 Zepto 替换为 JQuery 即可。本来想着用 Zepto 会轻量一点, 最后发现还是要上 JQuery。好在是目前 JQuery 提供了不含 Ajax 和 Effect 的 Slim 版本,倒是也不用担心引入的依赖文件特别的大。

5e54199359bbafe0ef692365a9bcffb6

三个有用的 Golang 辅助网站

在我数十年的编程生涯里,我大部分时间写的都是动态类型语言,所以我对于类型并不太感冒。不过,当我在写 Golang 的时候,确实遇到了一些过去写 PHP、 Node.js、前端不同的问题。

一个最令我困扰的点便是:对于 JSON 的处理,需要先定义结构体来做转换。对于一些简单的 JSON 结构,我还可以简单的完成定义。但如果是一些比较复杂的结构体,我就放弃了手动定义,而是选择借助一些辅助工具来完成

d2b5ca33bd970f64a6301fa75ae2eb22

比如我最常用的便是各种 JSON to Go 的网站,贴一个 JSON 进去,然后自动生成对应的 Golang 的 Struct,接下来我只需要复制右侧生成的 Struct 到我的代码里,就可以完成 JSON 的定义,使用 Golang 自带的 JSON 库解析即可。

截图的网站是 https://mholt.github.io/json-to-go/

另外一个可以实现 JSON 转换成 Go Struct 的网站是 https://transform.tools/json-to-go,它提供了更加全面的转换路径。不过有些时候选择太多也会让我有点困惑。

d2b5ca33bd970f64a6301fa75ae2eb22 1

另外一个令我困扰的也是类似的格式转换,不过是 YAML 的。思路相同,手写要配置的 YAML 文件,并生成对应的 Struct: https://zhwt.github.io/yaml-to-go/。在设计风格上,和上面的 JSON2Struct 类似,是基于上面的项目改造过来的。

d2b5ca33bd970f64a6301fa75ae2eb22 3

除此之外,如果你在定义 YAML 的时候, 对于部分数据如何定义不太明确,可以考虑使用 JSON2Yaml 的工具来实现将你需要的数据转换为 YAML 定义。

5e54199359bbafe0ef692365a9bcffb6

在 Go 当中嵌入父目录中的文件

自 Go 1.16 版本开始,Go 提供了将二进制文件打包进入到 Binary 文件当中的机制:`//go:embed。不过,我看到的示例大多数都是嵌入当前文件夹下的子文件夹的示例。并没有嵌入父一级文件夹的示例。于是,我便开始研究起来。

为什么会需要嵌入父目录中的文件?

这是因为不同的项目的构建规则不同。一些小型项目可能只有一个 main.go 或同级目录下几个其他的 go source 文件即可,但对于更大型的项目,合理的项目拆分是有助于帮助提升项目的可维护性的。

以我为例,我的目录结构如下:biz 目录下是我的核心逻辑,也是我日常写代码的地方;conf 则放置了各种配置,比如各种 API Key,ral 则是网络访问层,比如我比较喜欢用 gin 来做底层的网络访问层;script 放置了开发所需要的各种脚本文件;static 则放置了前端所需要的 JS / CSS 文件。在开发一些偏内容向的页面时,我会习惯使用 Server Side Render,所以我在 biz 目录下还有一个 template 目录。而主要的逻辑则放在 handler。

├── biz
│   ├── dal
│   ├── handler
│   ├── logic
│   ├── service
│   └── template
├── conf
├── ral
│   └── gin
├── script
└── static
    └── public
Code language: PHP (php)

在日常开发时,一个非常常见的操作是在 handler 里处理基本的逻辑,并将 template 中的模板渲染出来,并返回给用户,这个时候就需要在 handler 里使用上一级目录的文件了。

错误的尝试1:使用 .. 来引入

作为文件系统的常规配置,我自然知道可以通过 .. 来代表上一级目录,因此我试着使用如下的语法来引入文件

//go:embed ../template/*
Code language: JSON / JSON with Comments (json)

结果报了个错误 invalid pattern syntax。看得出来,是因为我的语法出现了问题。

通过搜索,我找到了这个 Github issue,才得知,由于避免跨模块的访问,go embed 阻止了 .. 语法的使用

Because embed.Files implements fs.FS, it cannot provide access to files with names beginning with .., so files in parent directories are also disallowed entirely, even when the parent directory named by .. does happen to be in the same module.

https://go.googlesource.com/proposal/+/master/design/draft-embed.md

错误的尝试2: 使用 go:generate 来执行复制

在上面的 issue 当中,我注意到还有另外一个方式,可以实现类似的效果。可以借助 go:generate来实现在执行 go generate 方法时,复制文件到本地,这样就可以实现在 Build 二进制文件之前,把文件复制到本地,即规避了跨模块的问题,也避免了将 template 文件放在本地,污染代码目录的问题。

//go:generate cp -r ../../assets ./local-asset-dir
//go:embed local-asset-dir
var assetFs embed.FS
Code language: JavaScript (javascript)

这个方法在运行的时候没有效果,不过在其他人可能是有效果的(我猜是因为我开发时很少直接用 go generate,都是直接 go run 了)。不过,这个方法也有个问题,因为是写在代码本身当中的,很有可能后续因为某些原因,这些代码被误删除或修改了,导致整个系统的运行不正常。而散落在各文件的描述也会使得修复十分复杂。

正确的做法

在看这个 issue 的最后,我注意到了正确的用法,在另外一个 issue 当中,开发者 tomasf 提到了,我们应该在 asset 目录下创建一个 embed.go 来完成 embedFs 的建立,并在其他文件直接引入这个文件,完成定义。

You can create a embed.go file in your assets directory and make the assets available as it's own package to the rest of your program.

tomasf

这个说法一出,豁然开朗!尝试一下,果然有效。我在 template 目录下创建了一个 embed.go 文件,并添加了如下代码。

package template

import "embed"

//go:embed *
var TemplateFs embed.FS

并在另外一个文件当中使用template.TemplateFs.ReadFile("index.tmpl") 来完成模板文件的引用。这样既不违背 golang 的跨模块,也不会使得代码不可维护,非常好。

参考阅读

  • https://go.googlesource.com/proposal/+/master/design/draft-embed.md
  • https://github.com/golang/go/issues/46056
  • https://github.com/golang/go/issues/41191#issuecomment-686621090
  • https://blog.carlmjohnson.net/post/2016-11-27-how-to-use-go-generate/
  • https://blog.carlmjohnson.net/post/2021/how-to-use-go-embed/
dfe5624d0afe5e8220b907e92766dd4b

在 Dokuwiki 当中接入 draw.io 的绘图

作为一个工程师,我难免会在自己的博客 / Wiki 当中加入流程图、时序图之类的。因此,需要更加简单的绘图的方式。

过去往往是采用本地绘制好图片,然后再复制上传到 Wiki 当中。不过,随着 tldraw、draw.io 等一类在线绘图工具出现以后,大家开始习惯于在线绘制图片。

在使用飞书文档的时候,我觉得其中内嵌 Diagram.net 的服务体验很不错。

现在,在 Dokuwiki 当中你也可以实现类似的功能。安装 Dokuwiki 上的 drawio 插件,随后,在你们的编辑器当中就可以看到一个 Drawio 的图标,点击这个图片,就可以插入一个 Drawio 的图片引用

d2b5ca33bd970f64a6301fa75ae2eb22 17
Drawio 的图标

插入完成后,你可以在下方的输入框当中看到插入新的引用,此时你可以修改具体的文件名和对应的 namespace。

插入完成后,点击保存(也可以在预览中修改),保存成功后,你会看到一个 Start drawing by clicking here,点击这个图标,就可以在渲染出的新的 UI 当中绘制你想要的流程图。

d2b5ca33bd970f64a6301fa75ae2eb22 18

绘制完成后,点击右上角的保存,即可保存你绘制的流程图。

d2b5ca33bd970f64a6301fa75ae2eb22 19

此时,你的流程图就绘制好了,其他人看的时候也是你画好的流程图。

后续如果你需要编辑这个流程图,只需要在登录的状态下,点击绘制好的图片,就会自动进入到编辑的模式,允许你修改你的流程图。

以下是一个简短的视频介绍:

dfe5624d0afe5e8220b907e92766dd4b

在 Dokuwiki 当中实现拖拽上传

在 Dokuwiki 当中,如果你想要上传一个文件,需要先将文件通过媒体管理器上传到wiki当中,再将对应的媒体插入到对应的页面中,流程繁琐不说,还容易把文件上传到意料之外的地方。

不过,你可以通过安装第三方插件,来实现 Drag & Drop 拖拽上传。

d2b5ca33bd970f64a6301fa75ae2eb22 16

安装 Dokuwiki 的 Dropfiles 插件,你就可以开启在编辑 Dokuwiki 的同时,直接拖拽上传文件的体验。

同时,因为你上传时已经在文本编辑的状态,因此,你上传的文件也会自动按照 wiki 所在路径来上传,对于不支持分页管理的 dokuwiki 来说,是一个非常有用的 Feature。

此外,如果你安装了这个插件,我强烈你开启 insertFileLink 这个选项,开启后,你拖拽上传的文件链接将会自动插入到当前文件中,十分方便。

dfe5624d0afe5e8220b907e92766dd4b

在 Dokuwiki 上实现反向链接的展示

Wiki 的一般用法是正向的链接到某个特定的页面,即所谓的正向链接。

但反向链接可以帮我们更好的对信息进行汇总和分析 —— 比如我们可以知道哪些地方引用了当前的 wiki 页面,从而实现更好的组织不同的信息。

想要在 Dokuwiki 当中实现这样的效果,需要使用一个第三方插件 —— Backlinks

d2b5ca33bd970f64a6301fa75ae2eb22 14

安装上这个插件后,你只需要在想要展示反向链接的地方插入{{backlinks>.}}就可以展示当前页面的反向链接。

不过,如果你想要达成比较好的效果,可以选择像我一样,建设一个 Side bar ,并在 Side bar 当中展示具体的反向链接,这样在具体看效果的时候,非常的简单和明确。

d2b5ca33bd970f64a6301fa75ae2eb22 15
dfe5624d0afe5e8220b907e92766dd4b

如何在 Dokuwiki 当中隐藏掉外部链接前的 Icon

Dokuwiki 在链接外部网站时,默认会在链接前展示一个地球的小图标。但如果你和我一样,觉得这个小图标有点碍眼,则可以尝试通过添加 CSS 来隐藏前面的小地球。

d2b5ca33bd970f64a6301fa75ae2eb22 12

你只需要在 /conf 文件夹下创建一个 userstyle.css 的文件,并在其中添加对应的 CSS

.page a.urlextern,
.page a.interwiki,
.page a.windows,
.page a.mail,
.page a.media {
  padding-left: 0 !important;
  background: none !important;
}
Code language: CSS (css)

添加完后,执行 Shift + Control + R 来强制刷新 CSS,刷新完成后,就可以看到没有小地球的连接了。

d2b5ca33bd970f64a6301fa75ae2eb22 13
dfe5624d0afe5e8220b907e92766dd4b

在 Dokuwiki 中配置安全规则,来保护配置和数据文件

由于 dokuwiki 的文件路径默认会放在 data/conf/bin 等几个目录当中,如果你不对相应的文件进行保护,如果某个人是了解 dokuwiki 的情况下,它可以越过你的 dokuwiki ,直接读取 wiki 的内容。

而你没有进行保护的时候,Dokuwiki也会在配置当中展示你的配置不安全。

d2b5ca33bd970f64a6301fa75ae2eb22 11

想要开启 Nginx 的保护,你需要在 Nginx 的配置文件当中,添加如下代码。

 location ~ /(data|conf|bin|inc|vendor)/ {
      deny all;
 }
Code language: JavaScript (javascript)

添加完成后,执行如下命令来重启 Nginx,使命令生效。

nginx -t 
nginx -s reload

生效成功后,再次刷新,就可以看到提示已经消失了。同时如果你直接访问 data 目录下的数据文件,也会直接报错。