普通视图

发现新文章,点击刷新页面。
昨天以前Ray's Blog

使用 ip6tables 在群晖 DiskStation 上开启 Docker Bridge 网络 IPV6 支持(不支持 SA6400)

2024年8月6日 13:18

注意:黑群晖、白群晖适用;SA6400 不适用于本教程。

前情提要

我自己组装了一台黑群晖,用于存放代码、运行一些服务和存储电影等。如果能拥有公网 IP,那 BT 客户端的可连接性就大大提高,同时也方便自己在外的时候观看电影。但家里用的是移动宽带,而移动宽带想要获取公网 IPV4 地址的难度想必大家都有所了解。不过运营商下发了 IPV6 地址,因此我可以使用公网 IPV6 地址访问这台 NAS。

但是,在默认情况下 Docker 的 Bridge 网络并没有支持 IPV6,尽管能够通过 NAS 的地址访问到 Docker 中的服务,容器本身是不能访问 IPV6 站点的。虽说 Host 网络可以简单粗暴地为容器开启 IPV6 访问,但是就不能够在同一个 Compose 中使用容器名访问其他容器,十分不便,也无法映射端口。因此,我希望能够为 Docker Bridge 网络开启 IPV6 支持。

开干

填上群晖的坑

众所周知群晖 DSM 系统使用的是魔改版 Linux,其中并没有内置本文所需的 ip6tables 模块。因此我们需要手动添加。

前往 syno-iptables 仓库,根据 README 指示查找对应你的群晖 CPU 架构及内核版本的文件夹(如我的型号是 DS918 内核版本为 4.4.302,那就选择 apollolake/kernel-4.4.302),并下载到本地,随后上传到群晖任意一个文件夹中并解压(我上传到 homes/ray)这里建议使用 Download GitHub directory

随后找到解压出的文件夹,其中会有 ko so 两个文件夹,右键 - 属性记录下 位置,我这里是 /volume1/homes/ray/sjtuross syno-iptables master apollolake-kernel-4.4.302

进入群晖 SSH,sudo -i 后进入 root 用户,执行下列代码,记得替换为你的路径

cp /volume1/homes/ray/sjtuross syno-iptables master apollolake-kernel-4.4.302/ko/* /lib/modules/
cp /volume1/homes/ray/sjtuross syno-iptables master apollolake-kernel-4.4.302/so/* /usr/lib/iptables/

接下来依旧需要根据你的内核版本执行对应命令,以我的 4.4.302 为例:

insmod /lib/modules/nfnetlink.ko &> /dev/null
insmod /lib/modules/ip_set.ko &> /dev/null
insmod /lib/modules/ip_set_hash_ip.ko &> /dev/null
insmod /lib/modules/xt_set.ko &> /dev/null
insmod /lib/modules/ip_set_hash_net.ko &> /dev/null
insmod /lib/modules/xt_mark.ko &> /dev/null
insmod /lib/modules/xt_connmark.ko &> /dev/null
insmod /lib/modules/xt_comment.ko &> /dev/null

insmod /lib/modules/nf_conntrack_ipv6.ko &> /dev/null
insmod /lib/modules/nf_defrag_ipv6.ko &> /dev/null

insmod /lib/modules/xt_TPROXY.ko &> /dev/null
insmod /lib/modules/xt_socket.ko &> /dev/null
insmod /lib/modules/iptable_mangle.ko &> /dev/null
insmod /lib/modules/textsearch.ko &> /dev/null
insmod /lib/modules/ts_bm.ko &> /dev/null
insmod /lib/modules/xt_string.ko &> /dev/null

insmod /lib/modules/ip6_tables.ko &> /dev/null
insmod /lib/modules/nf_nat.ko &> /dev/null
insmod /lib/modules/nf_nat_ipv6.ko &> /dev/null
insmod /lib/modules/nf_nat_masquerade_ipv6.ko &> /dev/null
insmod /lib/modules/ip6t_MASQUERADE.ko &> /dev/null
insmod /lib/modules/ip6table_nat.ko &> /dev/null
insmod /lib/modules/ip6table_raw.ko &> /dev/null
insmod /lib/modules/ip6table_mangle.ko &> /dev/null

其他版本的内核根据项目 README 走就行,这里不再贴了。

配置 Docker

在 DSM 7.2 之后群晖的 Docker 套件更名为 Container Manager,其存放配置文件的目录也进行了更名,因此命令稍有不同

# DSM 7.2+
sed -i 's/}/,"experimental":true,"ip6tables":true}/' /var/packages/ContainerManager/etc/dockerd.json

# 旧版
sed -i 's/}/,"experimental":true,"ip6tables":true}/' /var/packages/Docker/etc/dockerd.json

在 Docker Compose 中启用

只需要添加:

networks:
  default:
    enable_ipv6: true
    driver: bridge

迟来了一个月的 2023 年度总结 + 2024 新年快乐!

2024年2月2日 20:28

还没写,考完了就补!

终于考完期末考了,TuT

社交平台

  • +86 手机号收不到 Telegram 验证码,导致自己的电报账号被注销。于是在 chihuo2104 的推荐下申请了一张 Giffgaff 手机卡,成功注册 TG 🥺
  • 大部分情况都在 QQ 群里乱创,偶尔去微信看看群友发电)
  • 在 X 上获得了 1000+ followers

技术方面

  • 成为了 TS 体操运动员(x
  • 从纯正的前端切图仔变成了一个全栈,在这一年间学习了 Rust 和 Go 两门新语言 不过都还没做过项目
  • Vue Language Tools 贡献了几个 PR,并且混到了 Collaborator,然后在今年一月中旬进入了 Vue Team
  • 对自己一年前写的博客系统深感不满 屎山 考虑一下迁移到 Astro 或者 Nuxt Content
  • 加入了 Vue Vine 组织,负责 脚手架 CLI、 VSCode 插件和语言服务器的维护
  • 做了个插件化的 CLI 框架
  • 一个增强 Vue TypeScript 体验的插件
  • 在 GitHub 上获得了 3000+ contributions 和 500 followers

游戏方面

  • 国产之胱、裆红热油 越来越令人失望,作为一个从内测玩到现在的老玩家看着热油一步步走到今天也挺不容易的,但是死妈策划把一手好牌打得稀烂,圈钱都圈不明白,遂退坑
  • 在某群聊群友的推荐下入坑了 Valorant (国港都打),已经上头了🥵🥵
  • 同时学校的信竞给我推荐了摸鱼小游戏 Generals.io aka 将军棋

设备方面

  • 悲,手机被收了()只能用 WSA 替代
  • 购买了一个 YubiKey,可惜没有薅到 Cloudflare 的羊毛
  • 买了一块三星 PM9A1,作为移动硬盘 + Win To Go 使用
  • 游戏本坏了 售后说修不好,傻逼 ROG 品控再也不会买败家之眼😡😡 2W块打水漂

去年比较摆,基本没干什么事💦 那年度总结就先到此结束罢,最后,祝各位新年快乐啦🥰🥰


See Also

快使用 Dprint 换掉你的 Prettier 罢(迫切

2023年4月7日 13:23

前言:什么是代码格式化工具?

在写代码的时候,我们有时候会写出来这些丑陋的代码,没有分号,没有换行,奇怪的缩进和空格,不统一的引号:

// 手动造的(
const a=1;let b= 114514,c='1',d="homo"
function
    a
    (

    ){}
type A={ a:string}

可恶,太丑陋力!如果没有代码格式化工具,我们就必须一个一个一个修改,而且还没办法做到全部人员写的代码风格都统一。 如果我们使用了代码格式化工具(这里使用 Dprint),它会自动将以上代码格式化为:

// 手动造的(
const a = 1;
let b = 114514, c = "1", d = "homo";
function a() {}
type A = { a: string };

是不是好看多了?自动加上了分号,统一了引号,空格也适当地添加了。(代码写得好看的有奖励,写的难看的有惩罚
这就是代码格式化工具的作用:将代码统一为一致的格式。

Why not Prettier?

如果你曾经做过 JS 开发,那么你或许对 Prettier 有所耳闻。Prettier 在其官网将自己称为“一个固执己见的代码格式化工具”(An opinionated code formatter),同时“拥有极少配置项”(Has few options)。这也使得其开箱即用,不必要为配置而烦恼——它会自动统一你的代码风格(而且默认的并不丑!),可以说是ni prettiernlx prettier --write . 就能用。

那么,为什么这篇文章不向你推荐 Prettier 而要用 不知名 Dprint?原因有下:

  1. Prettier 的配置项很少。前面提到了,它是一个“固执己见”的代码格式化工具,也就意味着它并不能让你自定义代码风格。比如有些人喜欢把等号对齐到一行,不喜欢在花括号前面加空格(虽然我本人对这种风格不感冒),Prettier 就没法做到这一点。一些示例:

    function foo () {} // 这里,括号前面带有空格
    function bar<T>() {} // 泛型函数没有
    

    这是我个人比较喜欢的一种码风,Prettier 却并不支持它。而且你也无法申请添加这一功能,因为 Prettier 不再添加新的配置项

  2. 速度慢。虽然说 Prettier 相比 ESLint 快了不止一个量级(我知道 ESLint 不是一个专门的格式化工具),但是面对大型代码库时,Prettier 仍然需要数秒时间来解析代码然后格式化。这是脚本语言本身的限制,指不定 Node 在解析 Prettier 代码的时候原生语言写的工具都格式化完了(指Dprint

出于以上几点,我选择转投 Dprint 的怀抱(

Dprint

注意,Dprint 目前尚未达到 1.0 稳定版,但是 Bug 不多,可以试着用

反正我的 eslint-config 用的 Dprint 来格式化

Dprint 对自己的描述是“Rust 写的插件化、可配置的代码格式化平台”(虽然我看不懂这个平台platform是什么意思,但是 nb 就对了),注意奥,Rust 写的,那基本上就意味着高性能()同时,它使用多线程进行格式化,可配置化也是其一大亮点,像是上面提到的括号前加空格就可以实现。

Talk is cheap, show me the code

没有使用方法的介绍文章都是耍流氓,上代码(这里使用 @antfu/ni 进行依赖安装):

$ ni dprint

随后:

$ nlx dprint help

看到输出帮助信息就说明安装成功了。

然后初始化:

$ nlx dprint init

选择需要装的插件按回车(这里装了 typescript json markdown toml 插件),应该会在当前目录生成一个 dprint.json

{
  "typescript": {
  },
  "json": {
  },
  "markdown": {
  },
  "toml": {
  },
  "includes": ["**/*.{ts,tsx,js,jsx,cjs,mjs,json,md,toml}"],
  "excludes": [
    "**/node_modules",
    "**/*-lock.json"
  ],
  "plugins": [
    "https://plugins.dprint.dev/typescript-0.84.1.wasm",
    "https://plugins.dprint.dev/json-0.17.1.wasm",
    "https://plugins.dprint.dev/markdown-0.15.2.wasm",
    "https://plugins.dprint.dev/toml-0.5.4.wasm"
  ]
}

或者你也可以自己指定:

$ nlx dprint init --config .dprint.json

此时运行 nlx dprint check 就能够检测代码中存在的格式问题, nlx dprint fmt 就能自动格式化!

可以看到安装步骤也是非常简单()

配置

配置这里不多说,需要自定义请自行查看官网:https://dprint.dev/config

与编辑器集成

如果不按照编辑器插件,你每次写代码都必须要手动进行格式化,挺烦人的。如果装了编辑器插件,就可以在保存 / 输入时自动格式化!

打开你的用户设置 settings.json(不会打开的自己看文档),加入如下内容:

{
  "editor.defaultFormatter": "dprint.dprint",
  "editor.formatOnSave": true
}

或者如果你只想让 JS 使用 Dprint (TS 同理):

{
  "[javascript]": {
    "editor.defaultFormatter": "dprint.dprint",
    "editor.formatOnSave": true
  }
}

注意,以上配置只适用于 全局安装的 Dprint 。如果是在项目中装的 Dprint,请在工作区配置中添加:

{
  "dprint.path": "./node_modules/dprint/dprint.exe"
}

可以看到,上面这种方式是比较麻烦的。而且,它没法显示格式化更改了那些地方,没有 Diff,看着可难受了。有没有什么更好的办法呢?

有!如果你用了 ESLint,请看下一部分!

与 ESLint 集成

Prettier 有一个插件可以让 Prettier 作为一个 ESLint 规则来运行,它叫 eslint-plugin-prettier。我也做了个类似的插件,eslint-plugin-dprint-integration (eslint-plugin-dprint 被人抢了),内置了 typescript json markdown toml dockerfile 的格式化,以及 Vue 的 Script 部分。(以后会支持 CSS)。使用方法很简单:

$ ni eslint-plugin-dprint-integration -D

随后,在你的 ESLint 配置文件(这里是 .eslintrc.cjs)中添加:

module.exports = {
  extends: [
    "plugin:dprint-integration/recommended",
    // 关闭冲突 ESLint 规则
    "plugin:dprint-integration/disable-conflict",
  ]
};

齐活!打开你的 VSCode,装上 ESLint 插件,可以看到格式化的修改都可视化了:P

chi的小红包冒险 v.e.r. 2023

2023年1月20日 22:21

之前看到好多大佬都有在新年发红包的好习惯(x),可惜Ray穷穷又没有zfb没办法发红包(恼

于是就只能白嫖大佬们的力(

今年chihuo2104也做了一个小冒险并且让我发小软文推广,那么大家一起来玩罢(

以下为原文:

太可惜了!去年因为没有钱钱,所以红包活动就暂停力

今年红包活动正常进行!!!

活动时间:2023.1.21 10:00 - 2023.1.22 10:00

支付宝红包会在1.21 10:00准时发出,有24小时的时限的~~,所以抓紧速度哦~~

小提示:一共有三个quiz,有一些是受到Hackergame 2022的启发设计的题目qwq

活动网页:https://rb.chihuo2104.dev/

做出来的可以在本页评论(x

那时候chiblog的评论肯定能修好(立flag)(x

打造一个强大的 PowerShell 终端 =)

2023年1月13日 16:58

注意

请使用PowerShell 7。

安装所需要的软件

这里我使用Scoop进行安装,如果本地没有Scoop的话可以去看看这篇文章

$ scoop install pscolor posh-cargo posh-docker posh-git scoop-completion dockercompletion bottom starship zoxide git-aliases hub

我的PowerShell配置:

using namespace System.Management.Automation
using namespace System.Management.Automation.Language

#############################
# Encoding
#############################

# 编码
[console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding

#############################
# PSReadLine
#############################

Import-Module PSReadLine

# 补全括号和引号
Set-PSReadLineOption -HistorySearchCursorMovesToEnd
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward

Set-PSReadLineOption -Predictionsource History

# Smart Insertion

Set-PSReadLineKeyHandler -Key '"', "'" `
    -BriefDescription SmartInsertQuote `
    -LongDescription "Insert paired quotes if not already on a quote" `
    -ScriptBlock {
    param($key, $arg)

    $quote = $key.KeyChar

    $selectionStart = $null
    $selectionLength = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength)

    $line = $null
    $cursor = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    # If text is selected, just quote it without any smarts
    if ($selectionStart -ne -1) {
        [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, $quote + $line.SubString($selectionStart, $selectionLength) + $quote)
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2)
        return
    }

    $ast = $null
    $tokens = $null
    $parseErrors = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$parseErrors, [ref]$null)

    function FindToken {
        param($tokens, $cursor)

        foreach ($token in $tokens) {
            if ($cursor -lt $token.Extent.StartOffset) { continue }
            if ($cursor -lt $token.Extent.EndOffset) {
                $result = $token
                $token = $token -as [StringExpandableToken]
                if ($token) {
                    $nested = FindToken $token.NestedTokens $cursor
                    if ($nested) { $result = $nested }
                }

                return $result
            }
        }
        return $null
    }

    $token = FindToken $tokens $cursor

    # If we're on or inside a **quoted** string token (so not generic), we need to be smarter
    if ($token -is [StringToken] -and $token.Kind -ne [TokenKind]::Generic) {
        # If we're at the start of the string, assume we're inserting a new string
        if ($token.Extent.StartOffset -eq $cursor) {
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$quote$quote ")
            [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
            return
        }

        # If we're at the end of the string, move over the closing quote if present.
        if ($token.Extent.EndOffset -eq ($cursor + 1) -and $line[$cursor] -eq $quote) {
            [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
            return
        }
    }

    if ($null -eq $token -or
        $token.Kind -eq [TokenKind]::RParen -or $token.Kind -eq [TokenKind]::RCurly -or $token.Kind -eq [TokenKind]::RBracket) {
        if ($line[0..$cursor].Where{ $_ -eq $quote }.Count % 2 -eq 1) {
            # Odd number of quotes before the cursor, insert a single quote
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert($quote)
        } else {
            # Insert matching quotes, move cursor to be in between the quotes
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$quote$quote")
            [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
        }
        return
    }

    # If cursor is at the start of a token, enclose it in quotes.
    if ($token.Extent.StartOffset -eq $cursor) {
        if ($token.Kind -eq [TokenKind]::Generic -or $token.Kind -eq [TokenKind]::Identifier -or 
            $token.Kind -eq [TokenKind]::Variable -or $token.TokenFlags.hasFlag([TokenFlags]::Keyword)) {
            $end = $token.Extent.EndOffset
            $len = $end - $cursor
            [Microsoft.PowerShell.PSConsoleReadLine]::Replace($cursor, $len, $quote + $line.SubString($cursor, $len) + $quote)
            [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($end + 2)
            return
        }
    }

    # We failed to be smart, so just insert a single quote
    [Microsoft.PowerShell.PSConsoleReadLine]::Insert($quote)
}

Set-PSReadLineKeyHandler -Key '(', '{', '[' `
    -BriefDescription InsertPairedBraces `
    -LongDescription "Insert matching braces" `
    -ScriptBlock {
    param($key, $arg)

    $closeChar = switch ($key.KeyChar) {
        <#case#> '(' { [char]')'; break }
        <#case#> '{' { [char]'}'; break }
        <#case#> '[' { [char]']'; break }
    }

    $selectionStart = $null
    $selectionLength = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength)

    $line = $null
    $cursor = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
    
    if ($selectionStart -ne -1) {
        # Text is selected, wrap it in brackets
        [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, $key.KeyChar + $line.SubString($selectionStart, $selectionLength) + $closeChar)
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2)
    } else {
        # No text is selected, insert a pair
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)$closeChar")
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
    }
}

Set-PSReadLineKeyHandler -Key ')', ']', '}' `
    -BriefDescription SmartCloseBraces `
    -LongDescription "Insert closing brace or skip" `
    -ScriptBlock {
    param($key, $arg)

    $line = $null
    $cursor = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    if ($line[$cursor] -eq $key.KeyChar) {
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
    } else {
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)")
    }
}

Set-PSReadLineKeyHandler -Key Backspace `
    -BriefDescription SmartBackspace `
    -LongDescription "Delete previous character or matching quotes/parens/braces" `
    -ScriptBlock {
    param($key, $arg)

    $line = $null
    $cursor = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    if ($cursor -gt 0) {
        $toMatch = $null
        if ($cursor -lt $line.Length) {
            switch ($line[$cursor]) {
                <#case#> '"' { $toMatch = '"'; break }
                <#case#> "'" { $toMatch = "'"; break }
                <#case#> ')' { $toMatch = '('; break }
                <#case#> ']' { $toMatch = '['; break }
                <#case#> '}' { $toMatch = '{'; break }
            }
        }

        if ($toMatch -ne $null -and $line[$cursor - 1] -eq $toMatch) {
            [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor - 1, 2)
        } else {
            [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar($key, $arg)
        }
    }
}

#############################
# Highlighting
#############################

# 高亮
Import-Module PSColor
# Import-Module syntax-highlighting

#############################
# Completions
#############################

# 自动补全
Import-Module $Env:SCOOP\modules\posh-cargo
Import-Module $Env:SCOOP\modules\posh-docker
Import-Module $Env:SCOOP\modules\posh-git
Import-Module $Env:SCOOP\modules\scoop-completion
Import-Module $Env:SCOOP\modules\dockercompletion
Import-Module $Env:SCOOP\apps\bottom\current\completion\_btm.ps1
starship completions powershell | Out-String | Invoke-Expression
# 注意:以下补全为开发环境的补全,你的电脑上可能没有装,请自行删除 =v=
rustup completions powershell | Out-String | Invoke-Expression
fnm completions --shell powershell | Out-String | Invoke-Expression
dvm completions powershell | Out-String | Invoke-Expression
deno completions powershell --unstable | Out-String | Invoke-Expression
# (& conda 'shell.powershell' 'hook') | Out-String | Invoke-Expression
(& volta completions powershell) | Out-String | Invoke-Expression
Invoke-Expression (& { $hook = if ($PSVersionTable.PSVersion.Major -ge 6) { 'pwd' } else { 'prompt' } (zoxide init powershell --hook $hook | Out-String) })

#############################
# Starship
#############################

# 初始化Starship,这一块之后会讲
Invoke-Expression (&starship init powershell)

#############################
# Rust
#############################

# Rustup
# 设置Rustup的镜像
$Env:RUSTUP_DIST_SERVER = "https://mirrors.ustc.edu.cn/rust-static"
$Env:RUSTUP_UPDATE_ROOT = "https://mirrors.ustc.edu.cn/rust-static/rustup"

#############################
# fnm
#############################

# 设置FNM镜像和安装目录
$Env:FNM_DIR = "D:\.fnm"
$Env:FNM_NODE_DIST_MIRROR = "https://cdn.npmmirror.com/binaries/node"

fnm env --use-on-cd | Out-String | Invoke-Expression

#############################
# PyEnv
#############################

# $Env:PYTHON_BUILD_MIRROR_URL = "https://npm.taobao.org/mirrors/python"

#############################
# Zoxide alias
# Well, this should be here
#############################

# Remove-Alias -Name cd -Force
# Set-Alias cd z

#############################
# PNPM
#############################

# 设置PNPM目录
$Env:PNPM_HOME = "D:\.pnpm"
$Env:Path += ";$Env:PNPM_HOME"

#############################
# Path
#############################

# 添加deno install目录
$Env:Path += ";C:\Users\Hatsune_Miku\.deno\bin"

#############################
# Aliases
#############################

# Fix @antfu/ni
Remove-Alias -Name ni -Force
# Fix Scoop Install alias
Remove-Alias -Name si -Force

# Git
Import-Module git-aliases -DisableNameChecking
Set-Alias git hub
Set-Alias code code-insiders

# 一些缩写,同样你的电脑上可能没有装,请自行删除
# Node
# 这里需要你npm安装@antfu/ni
function nio { ni --prefer-offline @Args }
function nid { ni -D @Args }
function nd { nr dev @Args }
function ns { nr start @Args }
function nb { nr build @Args }
function nbw { nr build --watch @Args }
function nt { nr test @Args }
function ntu { nr test -u @Args }
function ntw { nr test --watch @Args }
function nw { nr watch @Args }
function np { pnpm publish --access public --no-git-checks @Args }
function nc { nr typecheck @Args }
function nl { nr lint @Args }
function nlf { nr lint --fix @Args }
function nrelease { nr release @Args }
function nre { nr release @Args }

# 这里需要你npm安装taze
function vc { nx vercel@latest @Args }
function taze { nx taze@latest @Args }
function tzm { taze major @Args }
function tz { taze major -wfri @Args }
function giget { nx giget@latest @Args }
function vcp { vc --prod @Args }

# Deno
# 这里需要你deno安装deployctl
function dctl { deployctl deploy @Args }
function dctlp { dctl --prod @Args }

# Go
# 这里需要你安装Go
function gg { go get @Args }
function gmt { go mod tidy @Args }
function gmi { go mod init @Args }
function gt { go test @Args }
function gta { go test ./... @Args }

# Python
# 这里需要你安装Python
function pi { pip install @Args }
function pu { pip install --upgrade @Args }
function pup { pu pip }

# Misc
# 这里需要你安装you-get和scoop
function yg { you-get -o="D:\.you-get" @Args } # You-get
function si { scoop install @Args } # Scoop Install
function sun { scoop uninstall @Args } # Scoop Uninstall
# 代理
function proxy {
    $Env:http_proxy = "http://127.0.0.1:7890"
    $Env:https_proxy = "http://127.0.0.1:7890"
}
function unproxy {
    $Env:http_proxy = ""
    $Env:https_proxy = ""
}
# 安装夜间版Node
function fnmn { fnm --node-dist-mirror https://nodejs.org/download/nightly/ @Args }
function Rename-Branch {
    git branch -m $Args[0] $Args[1]
    git fetch origin
    git branch -u "origin/$Args[1]" $Args[1]
    git remote set-head origin -a
}

# INIT Proxy

proxy

2023新年快乐! + 年度总结

2022年12月31日 18:11

今天事2022的最后一天,当你看到这篇文章的时候说不定已经是2023年啦(

Ray之前都没有写过年度总结,不过今年对于我来说意义重大,还是写罢(

博客

今年Ray的小博客系统Dolan正式启用,虽然还有很多大大小小的问题,不过这也算是建站历史上一个里程碑式的事件了……之后的打算是在新年前给博客加上新年主题(挂个灯笼啥的),然后找个时间把Admin用Vue + Element Plus重构一下,React的单向数据流用起来真TM膈应人……

文章

今年Ray中考结束如愿上了三线小城市的屑高中(),由于学业原因以及住校的缘故无法抽出时间来更新文章,就连文章也没有更新几篇,唯一的那三篇甚至有两篇没有写完(全恼

明年会努力的!

友链

今年加了不少大佬的友链,也清了一些死链)

新增的有YFunmzwing乐特伊緻幻以及CYF,但是CYF太高冷了并没有鸟我)

数据

有个毛的数据啊,都没加统计(),数据为N/A

学习

高中开始了,意味着又要三年苦读(恼),不过这次不是中考,而是更重要的高考😅

希望自己能够不负三年的努力吧,给自己一个满意的结果!

未来……

有人见尘埃,有人望星辰。告别2022,拥抱2023。

我们的每一天都在见证历史,欢迎2023!

无服务器动态博客系统Dolan:从设想到现实

2022年12月6日 13:40

起因

本站最早于 2020 年建立。当时正值网课时期,我就想着搭建一个博客记录一下自己的编程 / 学习过程。一路磕磕绊绊,尝试了多种不同的博客系统:

  1. 免费的 WordPress 部署平台(三蛋host),不过其国内访问速度实在不尽人意,承诺SLA为0%,同时还在网站中强制添加广告,这换谁不跑路啊;
  2. Hexo,一个静态博客生成器,目前使用人数十分庞大,同时有丰富的插件,是一个比较理想的博客系统。但是Hexo的坑实在是太多了,经常碰到因为Node版本过高 / 过低引起的问题虽然用一个fnm就能解决,同时生成速度在插件多了之后直线下滑,完全无法接受,遂弃用;
  3. Gridea,一个国人做的带GUI静态博客系统,好用,但是可拓展性不高,主题也不是很丰富,用了一段时间后也抛弃了;
  4. Hugo,一个用Go编写的静态网站生成器,生成速度很快,同时也具有插件系统与强大的自定义功能,是我使用时间最长的博客系统。

使用Hugo搭建的博客是存活时间最长的。我一共尝试了两个主题:MemE,以及自己移植的Tony。这套配置的易用性极高,同时高度可定制化,生成速度也十分令人满意。但是人心不足蛇吞象,有了一个能用的博客,没地方折腾了,怎么办呢?首先想到的一个优化点就是在线编辑文章。在本地 / 云端编辑固然好,还能实时预览,但难免受到开发环境的影响。

有一个轻量级的解决方案是以HexoPlusPlusWexagonalQexo等为代表的在线编辑器(不是开发环境)。这一类项目的特点是基于Github API无服务器,通过前端的富文本预览,然后再利用Github API上传博客源文件,从而模拟出一个在线编辑文章的环境,配合上集成部署实时生成网页,能够满足极大部分下的使用场景。不过,它们的适配对象都是Hexo,Hugo怎么办呢??理论上,在这些项目上添加支持也不难,但CYF和Abudu写的代码确实屎山质量不高,难不成自己再造一个轮子???

好吧,确实去造轮子了() 既然造了轮子,那我们就造个大的,直接造个博客系统罢!

选型

由于我们要做的是一个无服务器的博客系统,由于运行环境的限制(诸如性能、FS),我们无法做到上传主题、实时更改主题、从模板渲染等操作。同时,我们最好做到前后端分离,让每个部分各司其职,发挥最高的效能:比如后台就没必要和API捆绑在一起,可以独立部署;前端也如此。那我们的项目大体结构如下:

  1. API,提供数据支持。部署在Serverless上。
  2. 后台,提供管理页面。静态网页,部署在哪都行。
  3. 前端,用户访问界面。理论上可以是一个SPA,也可以进行服务端渲染,这个看自己有没有SEO需求了。

API最初的技术栈是Node(Koa),打算部署在Vercel上。但是Vercel的Node运行时速度实在感人,常常超时,遂放弃。

后面我了解到Deno背后的公司Deno Land Inc.推出了一个在线部署服务,叫做Deno Deploy,可以将Deno项目部署到云端,同时节点众多,访问速度理想;其又针对Deno进行了大量的优化,性能极高。于是,API的技术栈就定为了Deno(Oak)。

这里插一个题外话,Oak是一个受到Koa启发而创建的Deno HTTP框架,二者API基本相同,可以毫不费力地把Koa项目迁移过去。

随后是后台。后台的开发过程稍显曲折,最开始的技术栈是Vue3 + Quasar,但是Quasar的API稍显不足,许多功能都需要自己手动实现。后面看到抖音开源了一个SemiUI,说实话,挺漂亮的,于是捣鼓了很久,把完成了一半的后台迁移到了React。技术栈为:React + SemiUI + UnoCSS。

最后,是前端。写前端我的第一反应是使用一个SSR框架,即Nuxt / Next,但Next貌似支持的Serverless平台并不丰富,同时Nuxt3声称基于Nitro原生支持多平台(其中就包括Deno Deploy)。卧槽这不得给他冲爆,技术栈为Nuxt3,移植了一个hugo-theme-meme

当然,前端并非只能使用我做的这个dolan-client-meme。由于其本质只是从API获取数据并渲染(这也是为什么它叫做client而不是theme的原因),因此任何人都能够做自己的client。

再插个嘴:目前Nuxt3和Nitro对Deno Deploy的支持并没有在文档中体现出来,不过Nitro已经有了对它的初步支持。我发起了一个PR(目前已经合并)来修复一些bug,但目前包含这个Commit的版本还没发布,因此我自己先发布了一个包(@so1ve/nitropack)暂时用着,同时做了一些黑魔法,见dolan-client-meme/deno-fix.ts

开发

API

众所周知,开发初期搭建项目结构是最搞人心态的一件事了……不过好在Deno所需的配置并不多,最多就是import_map.jsondeno.jsonc两个配置文件。同时Oak也是一个很轻量级的框架,因此主要的目录结构如下:

│   .env
│   .gitignore
│   config.ts
│   deno.jsonc
│   deno.lock
│   import_map.json
│   LICENSE
│   README.md
├───.vscode
├───src
│   ├───protected_routes.ts
│   ├───server.ts
│   └───unprotected_routes.ts
│
└───────controller
    ├───lib
    │   └───init_values
    ├───middleware
    ├───types
    └───utils

其中两个*_routes.ts结尾的文件是路由文件,分为需要登录 / 不需要登录的两组路由。

后台

后台的UI前面已经说过选择了SemiUI,迎面而来的还有另一个问题:Markdown编辑器选什么呢?我考虑了以下几个编辑器:

后面两个首先出局,for-editor界面古早而且很久没更新,react-markdown-editor倒是挺活跃,不过它的功能不多而且不能通过插件扩展功能,于是放弃。

为什么最后选择了Milkdown而不是ByteMD呢?嗯我也不太清楚为什么,到后面想重构一下换成ByteMD发现工作量挺大的,于是搁置了() 主要原因是ByteMD默认会对HTML内容进行序列化,无法在文章中直接插入HTML,但是Milkdown@7则支持插入HTML。

然后是配置编辑器 / MD第二编辑器,Monaco Editor,这个没啥好说的

前端

TODO

如何使用呢?

目前文档在https://dolan.js.org,大家可以先看看

TODO

使用Scoop + 版本管理器科学地管理你的开发环境!

2022年12月5日 12:50

提示:这篇文章中有个小小的彩蛋()

管理开发环境是一门学问。你是否有过这样的经历:

$ node
node: The term 'node' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

很令人抓狂,对不对?这种问题一般是由不规范的PATH环境变量引起的,或是未添加,或是语法不对=v=

这种问题可以被称之为“开发环境污染”,这时候你可能会听从别人的建议重装应用(甚至重装电脑),但是无济于事,于是放弃折腾了()

不过,今天我会告诉你,重装不是唯一的方法,也不是最有效的。这种问题的最终解决办法都是使用包管理器,包括Linux上的apt和yum ,MacOS上的Brew,或者是Windows上的ChocolateyWinGet,还有今天我们要介绍的主角:Scoop

对比

上面写到,Win上的包管理器除了Scoop外还有Chocolatey和Winget,其中Chocolatey是老牌项目了,WinGet则是微软亲生的包管理器。

奇怪,为什么放着老牌包管理器和官方包管理器不用,而去用Scoop呢?我们来对比一下:

Winget Chocolatey Scoop
是否为中心化 是(Microsoft Store)
是否有付费功能
包数量(截止2022/12/19) 3966(通过winget search ""并统计行数得出) 9521 37793(官方 + 第三方源,可能有重复)
发布方式 编写Manifest并手动提交 编写Manifest并手动提交 编写Manifest并从bucket中获取,无需手动提交
如何更新自己的包 手动更新 有GitHub Actions自动更新 有GitHub Actions自动更新
能否自定义运行安装脚本 可以 可以 可以

当然,以上的结果是带有我个人偏见的(大嘘)。Scoop完全开源,发布自己包的方式也最为简单(加一个json文件提交到GitHub上就行),可拓展性也是三者中最高的(有诸如post_install等钩子函数可在安装的前后运行脚本)。因此,我选择了Scoop,而不是Chocolatey,或者Winget。

安装

好了BB了那么多我们也应该进入正题力()如何安装Scoop呢?官网给出了安装命令(先别急着运行):

> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
> irm get.scoop.sh | iex

Oh这里注意一下,安装脚本会从GitHub上拉取Scoop的最新版源代码,这就意味着我们可能会受到长城的制裁而无法正常安装()相信大家都有办法解决对吧

虽然这么安装Scoop很快也很省事,但是如果你的C盘并不充裕,我不推荐你使用上面的命令进行安装。为啥呢,因为默认的程序安装目录在C盘啊(悲)以下的脚本可以让你自定义安装目录:

> irm get.scoop.sh -outfile 'install.ps1'
> .\install.ps1 -ScoopDir 'D:/.scoop' -ScoopGlobalDir 'D:/.scoop-global'

其中ScoopDirScoopGlobalDir可以随便替换成两个不相同的目录。

等待脚本执行完后,在终端里打个scoop看看吧:

如果你看到了以上内容,那么恭喜你,成功力()先别急着装软件,我们再来了解些基本概念

基本概念

Scoop中程序的信息是怎么被获取的呢?这就涉及到Manifest和Bucket这两个重要概念。

其实理解起来也很简单,Bucket相当于是一个Manifest的合集,而Manifest则包含了一个软件的一些信息,比如安装包的URL啊,名称啊,版本、简介等等。默认情况下Scoop会把Main这个官方维护的Bucket拉取到本地(你也可以去拉一些别的Bucket,这个之后会说),其中包括了大量的开源软件。我们在运行scoop install foo的时候,Scoop都会去这些Bucket中寻找名称一致的Manifest,并从中获取软件安装包的链接,下载到本地进行安装。

使用

终于要到使用环节了吗,感动()

Node

我们先来搭一个Node环境吧,这里我们会使用版本管理器fnm。为什么不是n / nvm / nvs?fnm的一个重要特点就是模糊搜索,只需要给一个大版本号就能安装,同时其也是一个很新的项目,充满活力

> scoop install fnm

Installing 'fnm' (1.31.1) [64bit] from main bucket
fnm-x86_64-pc-windows-msvc.zip (4.5 MB) [==================================================================] 100%
Checking hash of fnm-x86_64-pc-windows-msvc.zip ... ok.
Extracting fnm-x86_64-pc-windows-msvc.zip ... done.
Linking D:.scoop\apps\fnm\current => D:.scoop\apps\fnm\1.31.1
Creating shim for 'fnm'.
'fnm' (1.31.1) was installed successfully!

> fnm install --lts # 安装LTS版本的Node
Installing Node v18.12.1 (x64)

> fnm default 18 # 使用最新版

注意啦,安装了之后还不能直接使用。打开Powershell,执行:

> notepad $PROFILE

在打开的记事本中添加:

fnm env --use-on-cd | Out-String | Invoke-Expression

这样子Node环境就搭好力()

Rust

Rust的环境搭建也很简单啊:

> scoop install rustup-msvc

就一行命令()

Go

不废话,上代码

> scoop install go

别的工具……

有时自己需要的工具并不在Main Bucket中,这时候你试图执行scoop install osulazer时就会提醒你Couldn't find manifest for 'osulazer'.。怎么办呢?好办,去搜索下:

可以看到osulazer在Games这个Bucket里头。

那么我们执行:

> scoop bucket add games
> scoop install osulazer

就可以啦~

搜索结果中给出的两条命令分别是添加Bucket以及安装软件的命令,先执行上面那条,后执行下面那条。

字体

没想到吧,Scoop还能装字体()

Scoop的字体都在nerd-fonts这个包中,执行scoop bucket add nerd-fonts即可。

注意:安装字体请务必全局安装,如scoop install FiraCode-NF -g,原因见此

实用工具合集

最后附上我用的软件:

>  scoop bucket list

Name       Source                                             Updated             Manifests
----       ------                                             -------             ---------
destiny    https://github.com/so1ve/destiny.git               2022/12/14 4:29:35          6
dorado     https://github.com/chawyehsu/dorado.git            2022/12/19 8:11:45        227
extras     https://github.com/ScoopInstaller/Extras           2022/12/19 16:27:47      1752
games      https://github.com/Calinou/scoop-games             2022/12/19 16:28:28       247
java       https://github.com/ScoopInstaller/Java             2022/12/17 16:18:12       253
main       https://github.com/ScoopInstaller/Main             2022/12/19 16:28:35      1129
nerd-fonts https://github.com/matthewjberger/scoop-nerd-fonts 2022/12/14 5:12:47        209
siku       https://github.com/amorphobia/siku.git             2022/12/17 0:39:11         57
versions   https://github.com/ScoopInstaller/Versions         2022/12/19 16:35:38       400

> scoop list

Installed apps:

Name               Version          Source     Updated             Info
----               -------          ------     -------             ----
7zip               22.01            main       2022-11-05 12:02:09
adb                33.0.3           main       2022-11-05 12:02:11
ag                 2.2.5            main       2022-11-05 12:02:16
aria2              1.36.0-1         main       2022-11-05 12:13:39
bat                0.22.1           main       2022-11-05 12:13:41
bazel              5.3.2            main       2022-11-05 12:17:02
bottom             0.6.8            main       2022-11-05 12:17:12
broot              1.16.2           main       2022-11-05 12:19:34
busybox            4784-g5507c8744  main       2022-11-14 10:04:28
cheat              4.4.0            main       2022-11-14 10:04:41
clink              1.4.0            main       2022-11-14 10:04:43
cloc               1.94             main       2022-11-05 12:22:03
cloudflared        2022.10.3        main       2022-11-05 12:41:57
cmake              3.24.3           main       2022-11-05 12:49:00
curlie             1.6.9            main       2022-11-05 12:49:38
dark               3.11.2           main       2022-11-05 12:53:24
delta              0.14.0           main       2022-11-05 12:53:27
deta               1.3.3-beta       main       2022-11-05 13:06:14
docker             20.10.21         main       2022-11-05 13:06:28
docker-compose     2.12.2           main       2022-11-05 13:12:24
dockercompletion   1.2010.1.211002  extras     2022-11-05 13:06:32
duf                0.8.1            main       2022-11-05 13:12:40
dust               0.8.3            main       2022-11-05 13:12:58
dvm                1.8.6            destiny    2022-11-05 12:53:39
fd                 8.5.2            main       2022-11-05 13:13:10
ffmpeg             5.1.2            main       2022-11-05 13:55:42
FiraCode-NF        2.2.2            nerd-fonts 2022-11-05 13:59:22
fnm                1.31.1           main       2022-11-05 14:00:09
fzf                0.35.0           main       2022-11-14 10:04:45
gh                 2.20.0           main       2022-11-14 10:04:49
gitui              0.21.0           main       2022-11-15 21:05:10
go                 1.19.3           main       2022-11-05 14:03:45
gow                0.8.0            main       2022-11-05 14:08:06
gpg                2.3.8            main       2022-11-05 14:10:58
gping              1.4.0            main       2022-11-05 14:11:04
gradle             7.5.1            main       2022-11-05 14:14:25
gsudo              2.0.2            main       2022-11-14 10:05:01
helix              22.08.1          main       2022-11-05 14:18:02
hexyl              0.10.0           main       2022-11-05 14:18:10
hub                2.14.2           main       2022-11-05 14:19:08
hugo-extended      0.105.0          main       2022-11-05 14:21:41
hyperfine          1.15.0           main       2022-11-05 14:22:08
jid                0.7.6            main       2022-11-05 14:22:30
jq                 1.6              main       2022-11-05 14:23:32
just               1.8.0            main       2022-11-05 14:23:46
lazygit            0.36.0           extras     2022-11-15 21:05:21
llvm               15.0.4           main       2022-11-05 16:31:03
lsd                0.23.1           main       2022-11-05 16:31:08
make               4.4              main       2022-11-05 16:31:19
mingit             2.38.1.windows.1 main       2022-11-05 11:46:47
mingw-nuwen        18.0             main       2022-11-05 16:36:36
msys2              2022-09-04       main       2022-11-05 16:37:24
neofetch           7.1.0            main       2022-11-05 16:37:26
neovide            0.10.3           extras     2022-11-14 10:05:06
neovim             0.8.0            main       2022-11-05 16:37:50
openssh            8.9.1.0p1        main       2022-11-05 11:48:09
oraclejdk17        17.0.3.1         destiny    2022-11-05 16:53:24
oraclejdk8         8u321            destiny    2022-11-05 16:54:46
posh-cargo         0.1.2.0          siku       2022-11-05 16:54:48
posh-docker        0.7.0            extras     2022-11-05 16:54:50
posh-git           1.1.0            extras     2022-11-05 16:54:52
powershell-preview 7.3.0-rc.1       destiny    2022-11-05 13:05:41
procs              0.13.3           main       2022-11-05 16:54:59
psreadline         2.2.6            extras     2022-11-05 16:55:00
refreshenv         2022.06.30       main       2022-11-05 16:55:39
ripgrep            13.0.0           main       2022-11-05 16:55:47
rustup-msvc        1.25.1           main       2022-11-05 17:14:00
scoop-completion   0.2.4            extras     2022-11-05 16:56:55
starship           1.11.0           main       2022-11-05 16:56:59
tdm-gcc            10.3.0           versions   2022-11-05 16:57:47
tinygo             0.26.0           main       2022-11-05 17:06:16
ttyd               1.7.2            main       2022-11-06 12:55:25
vcpkg              2022.10.19       main       2022-11-05 17:06:52
vcredist2010       10.0.40219.473   extras     2022-11-05 17:07:37
vcredist2022       14.34.31931.0    extras     2022-11-14 10:06:49
vhs                0.1.1            main       2022-11-06 12:55:30
vim                9.0              main       2022-11-05 17:10:32
vimtutor           0.2018.07.25     main       2022-11-05 17:10:33
wrangler           1.20.0           main       2022-11-05 17:10:38
youtube-dl         2021.12.17       main       2022-11-05 17:10:52
zig                0.10.0           main       2022-11-05 17:11:40
zoxide             0.8.3            main       2022-11-05 17:11:44
firacode-nf        2.2.2            nerd-fonts 2022-11-05 18:35:27 Global install
Noto-NF            2.2.2            nerd-fonts 2022-12-17 20:05:33 Global install
smiley-sans        1.0.0            nerd-fonts 2022-12-11 12:11:51 Global install

恢复更新

2022年11月16日 11:11

如你所见,本博客已经搬迁到了新博客系统 + 新主题(也就是外观啦)!

由于学业问题以及开发博客系统的进度所迫,我前前后后咕了一年半(好像?bushi

总之,本博客从今日起复活啦!

详解JWT

2022年11月15日 15:29

这可能是旧博客的最后一篇文章了,之后会使用新博客系统。

创客七户社征文。

阅读这篇文章前,我们假定你对HTTP稍有一些了解。

JWT……和为什么要用JWT

JWT,即JSON Web Tokens。顾名思义,其为用于Web授权认证的一种令牌。它最为显著的特征为安全和“无状态”。

我们先来看看过去的几种认证方式。

HTTP Basic Auth

在Web授权认证这一方面,我们最先使用的是HTTP Basic Auth。这种方式优缺点都很明显,优点是简单易用,基本上所有流行的网页浏览器都支持,开发者只需要在服务器的响应中加上一行形如WWW-Authenticate: Basic realm="Secure Area"的响应头即可。但这种认证方式只对用户密码进行了近乎裸奔的Base64编码,且易被获取造成信息泄露,甚至可以发起中间人攻击,因此极少在生产环境中使用。

Cookie/Session

Cookie/Session认证机制是一种较为安全的Web授权认证机制,其分为两大组成部分,即存储在客户端的Cookie和服务端的Session。它的运行方式也十分简单,客户端发出一个登录请求,服务端记录下用户信息(即Session),并在响应中要求客户端记录下Cookie。在用户发起请求时,Cookie会一同发出,服务端便可基于此进行用户认证。

这种方式看似是一种完美的解决方式,但其实仍有不足之处。

首先,Session存储于服务端,这就必然导致服务器资源的占用。倘若有大量用户或攻击者同一时间大量发起登录请求,服务器便要大量存储Session,容易导致内存不足,甚至崩溃。同时,如果在Session中置入了较大的对象,也会产生较大的性能负担。Session的服务端开发对于程序员来说也不是一件美事,过度使用Session会导致代码不可读而且不好维护。

JWT来啦

既然Basic不安全,Session又会给服务器造成大量负担,那该怎么办呢?

首先我们需要一个安全的认证方式,这个好办。我们只需要一个只存在于服务器的密钥对敏感信息进行加密就可以了。

其次是不能给服务器造成性能负担,那我们势必将登录信息存储于客户端。

由此,JWT就诞生了。

JWT的组成

部分内容来自https://javaguide.cn/system-design/security/jwt-intro.html

我们可以在jwt.io上直观感受JWT。JWT是一种形如xxxxx.yyyyy.zzzzz的由服务器返回的字符串,其中xxxxx为Header(描述JWT的元数据,定义了生成签名的算法以及 Token 的类型)、yyyyy为Payload(用来存放实际需要传递的数据),zzzzz为Signature(签名)(服务器通过Payload、Header和一个密钥(Secret)使用Header里面指定的签名算法(默认是HMAC SHA256)生成)。

可以看出,JWT本质上就是一组字符串,通过.切分成三个经过Base64编码的部分。

例如:

eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoic28xdmUiLCJwYXNzd29yZCI6bnVsbH0.zPEzTi2VzXpMTndG0k04QVddxaQ2Ermgw21skfyl1XWqdF5mdPoT6goQsk8XsAh40Twux-IHFl1RSYUeLuyCGQ

你可以在这里对它进行解码,可以得出其中包含的Header、Payload和Signature。

Header

Header 通常由两部分组成:

  • typ (type): 令牌类型,也就是JWT。
  • alg (algorithm): 签名算法,比如HS512。

示例:

{
  "alg": "HS256",
  "typ": "JWT"
}

JSON形式的Header被转换成Base64编码,成为JWT的第一部分。

Payload

随后是Payload。Payload也是JSON格式数据,包含了Claims(声明,包含JWT的相关信息)。在这里可以包含一些用户相关的信息,但请注意,默认情况下该部分并未进行加密,请不要存储隐私信息。

例如:

{
  "name": "so1ve",
  "password": null
}

这是一个很简单的例子,除了这些自定义的内容,我们还可以放置如下数据:

  • iss (issuer):JWT 签发方。
  • iat (issued at time):JWT 签发时间。
  • sub (subject):JWT 主题。
  • aud (audience):JWT 接收方。
  • exp (expiration time):JWT 的过期时间。
  • nbf (not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。
  • jti (JWT ID):JWT 唯一标识。

JSON形式的Payload被转换成Base64编码,成为JWT的第二部分。

Signature

Signature 部分是对前两部分的签名,作用是防止JWT(主要是payload)被篡改。

这个签名的生成需要用到:

  • Header + Payload。
  • 存放在服务端的密钥(一定不要泄露出去)。
  • 签名算法。

签名的计算公式如下:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

算出签名以后,把Header、Payload、Signature三个部分拼成一个字符串,每个部分之间用.分隔,这个字符串就是JWT

如何认证

使用JWT进行认证,大致可以简化为如下步骤:

  1. 客户端向服务器发起登录请求(用户名、密码,或许还有验证码?),服务器后返回一个包含用户信息(Payload中)的JWT令牌。
  2. 客户端将这个JWT存放在自己的localStorage(本地存储)中,并且在每次需要用户信息的请求中在请求头中携带上该令牌。Authorization: Bearer <JWT>
  3. 服务器接收到JWT,并进行解密,利用自己的密钥检验Signature,对用户权限进行判断。如果用户拥有访问该页面的权限,则返回相应内容。
  4. 客户端接收到响应内容。

可以看出,全程服务器乐的清闲,不需要本地存储用户信息,而是将工作交给浏览器。

关于安全性

前面我们提到,JWT的弊病之一是Payload只经过Base64加密(≈裸奔了),因此不能存储隐私信息。在一般情况下,客户端无需用户的隐私信息,如果必要,我们可以采用对JWT进行对称加密,用时再解密的方式来增加安全性。

另外,由于JWT被JavaScript存储在localStorage中,由于JavaScript的同源策略,网站下所有的JavaScript代码都可以访问同一个localStorage,从而获取到JWT,这就会带来注入的风险。

最后,网站一定要使用HTTPS。如果只使用HTTP,Token在网络中明文传输还是存在泄露的风险。

总结

此外,Web授权认证还有OAuth等方式,在此不再多说。总而言之,JWT是目前最流行也最优秀的Web授权认证方案。

题外话

开学就上高中了,之后更新的频率可能会降低不少,请见谅。

图床的千层套路

2020年9月13日 14:57

转载自CYF的博客

博客最近在细心打磨终于上95分了,其中我认为图片功劳不可没。

2020年8月9日Jsdelivr发布了一次使用政策:Create Acceptable Use Policy,其中第4条Prohibited Use引起了众多议论:

4. Prohibited Use

The following behavior is prohibited:

 1. Hosting or accessing content that:
     - contains malware or harmful code in any form,
     - violates proprietary rights of others,
     - is sexually explicit,
     - is potentially illegal in the EU or the USA.

 2. Abusing the service and its resources, or using jsDelivr as a general-purpose
    file or media hosting service. This includes, for example:
     - running an image hosting website and using jsDelivr as a storage for all
       uploaded images,
     - hosting videos, file backups, or other files in large quantities.

    We recognize that there are legitimate projects that consist of a large number
    of files, and these are not considered abuse. For example: icons packs, apps,
    or games with a large number of assets.

其中running an image hosting website and using jsDelivr as a storage for all uploaded images 这一句相当的有歧义,要多少的图片才能算是图站?博客里面图片放里面算吗?上传的图片怎样才不行?

反观网上流传的白嫖Github做图床,基本点进去都是https://cdn.jsdelivr.net/gh/ 这样子的图床,这种行为,我不敢妄加评论。但是,jsdelivr诞生的意义似乎并不是为了图床而生的,这种行为也很难判断成滥用。

使用政策发布之后,一时间,QQ群、v2ex、知乎上立刻就炸了锅。很多人猜测jsdelivr是不是滥用过度而禁止将其作为图床?免费图床的白嫖日子要结束了吗?更多的人,是在哭诉和询问那里还有像jsd一样优秀的图床可以白嫖,微博炸了,那里还有免费图床啊?

实际上,我一般采用的是BackBlaze+CloudFlare 但是自从八月底移动开始改道,从原先优秀的CMI绕路LAX后,国内CloudFlare访问质量再次暴跌,这不得不使我将博客迁至Vercel。好在八月份我有幸申请到了doegdoge图床使用权限,获得了国内较高速的图床.

但是,对于哪些没有没有图床的人来说,免费图床真的这么难以获得吗?

不好意思,免费图床非常多,只是你不会用而已,这篇文章,就是拯救面前陷入图床危机的你【当然是面向小白,大佬也可以在底下给我提意见鸭】。

公益图床

sm.ms

https://sm.ms

推荐程度:★

首先推荐的是这个图床,loli.net域名经典重现。三年前此图床域名还有备案采用的是国内CDN,可惜后来因为滥用吊销备案号而被迫迁移国外,用的是CloudFlare。实际使用效果面向国内确实不太好,建议备用。

你不需要注册,拖拽直接上传,只要不违反大陆和香港法律,他就能永久保留你的图片

可搭配PicGo

Imgur

https://imgur.com

推荐程度:★★

国外一家牛逼的图片托管服务商,你可以选择注册或不注册,同样的,拖拽上传,永久保留,其SLA有着相当高的保证。

然而很可惜的是,这种网站很早就在国内被DNS域名污染,也就意味着访客无法正常加载你的图片。这也就是被打为两颗星的原因。

当然,你也可以通过#图像缓存服务 从而实现国内访问。

可搭配PicGo【需注册】

去不图床

https://7bu.top/

推荐程度:★★★★

杜老师提供的个人公益图床,存储于阿里和腾讯的COS,官方保证SLA>=99%,是一个不错的选择,当然,7bu毕竟是个人维护的图床,能不能永久撑下去还是个问题,我也没有做过深度评测,无法表明其可用性。

可搭配PicGo。

接口地址:https://7bu.top/api/upload
post参数:image
回调json:data.url

更准确的API文档

而且,就在我上传测试图片的时候,明明已经表明图片已经上传,打开却发现COS提示404,这一点我不得陷入思考,个人维持的公益项目真的能保证SLA吗?

昨天上传的时候撞上服务器维修了,很抱歉做出了不够恰当的评价.7bu采用的是全国腾讯云CDN加速,国内访问速度十分优良。然而请注意,7bu刚开始建立的目的并不是面向全球【仅面对中国大陆游客】,这导致其大陆以外基本解析至国内西藏腾讯,访问效果并不好。并且,这是通过腾讯云的鉴黄,可能会存在误杀行为。具体使用请个人斟酌【不过作为开发环境还是可行的】。

白嫖的

阿里图床

推荐程度:★★★★

我个人搭建的API:https://picbed.cyfan.top 不保证上传SLA

由于小鸡联通国内网络不太好,很有可能无法正常上传,原项目已经开源 ,你完全可以通过在国内的机子或者是本地搭建以获得更佳体验。

如果上传成功了,图片将会托管于阿里云的CDN,无论是速度还是延迟都相当的优秀。

官方大厂,下载SLA有保障。

可搭配PicGo。

接口地址:https://picbed.cyfan.top/update.php
post参数:file
回调json:data.url

已失效,切勿使用

DogeDoge图床

推荐程度:★★★★★

TEST

其实很早就看到V2EX的那篇征文了

可是当时我不够优秀啊虽然现在同样不优秀,博客也没满一年啊,于是白嫖的心态搁浅了。

后来突然看到Jalen的博客也用了DogeDoge图床,这才突然意识到原来我已经满一年了。于是抱着试试看的心态向doge官方邮箱发送了邮件,结果真过了。。。

dogedoge拥有着国内相当不错的CDN,国内访问飞快,但是国外的访问质量的确不如人意。【反正此博客面向中国大陆】

而且,DogeDoge拥有着很良心的处理参数:

w:宽
h:高
mode:模式 - crop 裁剪、clip 缩略
fmt:格式 - jpg、png、webp(原图为 gif,且没有 frame 参数时,不做任何裁切、缩略处理)
frame:1 - EOF帧,默认为 1 (对动画有效)
q:压缩质量 - 1 - 100(默认 90 )
rect:指定位置裁剪 - top,left,w,h(若与 w / h 参数同时存在,则 会在 rect 裁剪过后,继续按照 w / h 的要求缩略)
pos:(配合 w / h )裁剪位置 - top-left、top、top-right、left、center、right、bottom-left、bottom、bottom-right,默认为center
pos 还有一个特殊的值 auto,该值目前为 alpha 状态,可以根据图片重点来进行 pos 的位置取舍。

话说回来,DogeDoge也可以搭配PicGo使用。

接口地址:https://www.dogedoge.com/tools/upload/{Your_Token}
post参数:file
回调json:data.o_url

当然,现在的Doge图床还是处于免费的试用期【Creater】,不过好在试用期过后价格也比较合理,一般的tester也足够使用,目前看来SLA还是不错的。

不过,申请不到dogedoge图床也没关系,看下去你就会发现,白嫖的路千千万万,何必执着于一条。

BackBlaze

推荐程度:★★★

具体可以看看这篇文章

千奇百怪的

Github+JSDelivr

正如我所说的,这种组合已经被广大博主所采纳,并且网上教程已经泛滥了,在这里不再阐述。

npm+JSDelivr&&Zhimg&&bdstatic&&自定义镜像

推荐程度:★★★★★

为什么很多文章都没有提到用npm做图床?我想其中很大的原因是,白嫖jsd做图床的,很多都是小白【或者不愿花时间在于此的大佬】,同样的,这些文章面向的都是这些人,毕竟,以拖拽方式上传的Github和命令行方式上传,我想,大都数人会选择前者吧。

可是,你们没有想到的是,github文件镜像【github.com.cnpmjs.org是站点镜像】只有jsd一个,npm镜像可远远不止这一个啊!

让我们看看分别镜像在jsd、zhimg、bdstatic的文件怎么样:

【unpkg镜像用的是CloudFlare,国内加速效果不好,暂时不写】

jsd就不必多说了,国内拥有强劲的网宿节点支撑【虽然以前出现过网宿下游投毒】,速度丝滑无比,国外也有强劲的CloudFlare上岗,可谓国内外两不误。而且,jsd对于npm的package单文件没有大小限制,也就是说泡个视频也不是问题。

zhimg是知乎的unpkg镜像,也是一个不错的选择【阿里CDN】,知乎官方也未对此做出限制,日常使用是可以的。

bdstatic是百度的内用npm镜像,速度也很好【百度CDN】,但是请注意,bdstatic作为内用cdn,其拉取频率较慢,经常出现无法及时更新。

啊哈?不会上传?
npm
官网注册个账号去,然后先:

npm login

接着:

npm init
npm publish

请注意,如果你之前用过淘宝镜像,那么请先手动切回源:

npm config set registry https://registry.npmjs.org

每一次发布图片后,你可以将原来的图片删除,更改package.json 版本号【向上增加】,然后npm publish即可

这个似乎可以搭配picgo,不过好像没这个插件,写起来也麻烦。。。

unpkg的国内镜像其实远远不止这些,包括七牛、饿了么、腾讯都有,不过这个就要自己找了。

一些推荐的npm【or unpkg镜像】:

【jsd出品,网宿国内节点】https://npm.elemecdn.com/
【知乎出品,网宿国内节点】https://npm.elemecdn.com/
【百度出品,网宿国内节点】https://code.bdstatic.com/npm/
【饿了么出品,网宿国内节点,回源是Unpkg,建议用这个】https://npm.elemecdn.com/
【饿了么出品,网宿国内节点,回源是JSdelivr,貌似可以用github,但是我用的时候大多无法正常回源,只能获取几个已缓存的热门库】https://shadow.elemecdn.com/npm/
【怎么都是网宿的】

或者说,你还可以自建unpkg镜像。

啊,你说你没有服务器反向代理unpkg?

其实,七牛的对象存储,腾讯的COS和阿里的OSS都是支持镜像回源的鸭!

七牛http流量每月免费10GB,腾讯的国内免费60GB6个月,作为自用完全足够了!

ipfs

我曾经写过关于ipfs的讲解 ,作为一个去中心化的存储系统拿来做公开图床其实挺不错的。

我个人搭建的ipfs镜像【托管于CloudFlareWorkers】:https://ipfs.cyfan.top

我个人搭建的ipfs上传API:https://ipfsupload.cyfan.top

基于Vercel+CloudFlare~~【我也不知道worker为什么死活上传不上去】~~

接口地址:https://ipfsupload.cyfan.top/api/v0/add?pin=true
post参数:file
回调json:Hash

此处Hash获得的是文件的Qmhash,你还要依托ipfs镜像,如https://ipfs.cyfan.top/ipfs/{QmHash}

顺便收录一些ipfs网关【可访问】:

【北京 阿里云】https://hashnews.k1ic.com/
【香港 阿里云】https://ipfs.jbb.one/
【美国 DigitalOcean】https://ipfs.telos.miami/
【Amazon】https://ipfs.oceanprotocol.com/

你可以在https://ipfs.github.io/public-gateway-checker/找到更多

图片缓存服务

正如##Imgur所说的,imgur在国内已经无法访问了,但是,图片缓存服务可以啊!

收集了一些图片缓存服务:

【国内网宿节点,只能加载特定图床图片如imgur】https://search.pstatic.net/common/?src=
【Akamai节点,没有使用限制】https://imageproxy.pimg.tw/resize?url=
【CloudFlare节点】https://images.weserv.nl/?url=
【CloudFlare节点】https://pic1.xuehuaimg.com/proxy/

PicGo的搭配使用

PicGo默认已经集成了部分图床,其拖拽上传、自动复制剪贴板实在赢得了无数人的心。但是,对于一些冷门的图床支持似乎就不太好,这时候你需要用自定义web图床实现这一切:

我在上方介绍的图床如果支持web端上传,基本上就会写一个post请求,你可以依葫芦画瓢填写进去

这样子你就可以实现较为丝滑的上传图片了:

【为了压缩方便删除了部分帧】

后言

实际上最保险的莫过于使用各大厂商的对象存储,当然这笔钱不大好使。
你也可以用自己的VPS搭建Chevereto,当然前提是你有VPS

❌
❌