#818 静态网站生成器

2022-11-17

https://github.com/topics/static-site-generator

GitHub 搜索了一下,知名的静态网站生成器,大部分是 JS/TS 写的。

社区上经常见到的一些项目有 JS 的 Hexo,Ruby 的 Jekyll,Go 的 Hugo

JavaScript

  1. gatsbyjs/gatsby shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    The fastest frontend for the headless web. Build modern websites with React.
  2. hexojs/hexo shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    A fast, simple & powerful blog framework, powered by Node.js.
  3. vuejs/vuepress shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    📝 Minimalistic Vue-powered static site generator
  4. netlify/netlify-cms shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    A Git-based CMS for Static Site Generators
  5. 11ty/eleventy shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    A simpler static site generator. An alternative to Jekyll. Transforms a directory of templates (of varying types) into HTML.
  6. react-static/react-static shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    ⚛️ 🚀 A progressive static site generator for React.
  7. gridsome/gridsome shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    ⚡️ The Jamstack framework for Vue.js
  8. metalsmith/metalsmith shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    An extremely simple, pluggable static site generator.
  9. getgridea/gridea shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    ✍️ A static blog writing client (一个静态博客写作客户端)
  10. GetPublii/Publii shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Publii is a desktop-based CMS for Windows, Mac and Linux that makes creating static websites fast and hassle-free, even for beginners.

Python

  1. mkdocs/mkdocs shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Project documentation with Markdown.
  2. getpelican/pelican shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Static site generator that supports Markdown and reST syntax. Powered by Python.

Ruby

  1. jekyll/jekyll shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    :globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby

Rust

  1. getzola/zola shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    A fast static site generator in a single binary with everything built-in. https://www.getzola.org

Go

  1. gohugoio/hugo shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    The world’s fastest framework for building websites.

#817 Pipenv:PkgResourcesDeprecationWarning

2022-11-13
~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py:123: PkgResourcesDeprecationWarning: 1.1build1 is an invalid version and will not be supported in a future release
  warnings.warn(
~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py:123: PkgResourcesDeprecationWarning: 0.1.43ubuntu1 is an invalid version and will not be supported in a future release
  warnings.warn(
~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py:123: PkgResourcesDeprecationWarning: 2.9.0.Odd.Olm is an invalid version and will not be supported in a future release
  warnings.warn(

Pipenv 最近执行命令的时候会有这些提示,看这个意思是检测了一些 Python 包的版本不被支持。
用 traceback 排查之后,确认是在 Python PATH 下检查所有包的版本。
出问题的三个包是:

  • distro-info <LegacyVersion('1.1build1')> /usr/lib/python3/dist-packages
  • python-debian <LegacyVersion('0.1.43ubuntu1')> /usr/lib/python3/dist-packages
  • Shredder <LegacyVersion('2.9.0.Odd.Olm')> /usr/lib/python3/dist-packages

查找路径:

~/.local/lib/python3.10/site-packages/pipenv/patched
~/.local/lib/python3.10/site-packages/pipenv/vendor
~/Projects/Mine/staticize
/usr/lib/python310.zip
/usr/lib/python3.10
/usr/lib/python3.10/lib-dynload
~/.local/lib/python3.10/site-packages
~/.local/lib/python3.10/site-packages/mackup-0.8.33-py3.10.egg
/usr/local/lib/python3.10/dist-packages
/usr/lib/python3/dist-packages

相关代码:

File "~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py", line 997, in __init__
  self.scan(search_path)
File "~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py", line 1030, in scan
  self.add(dist)
File "~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py", line 1050, in add
  dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)
File "~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py", line 2623, in hashcmp
  self.parsed_version,
File "~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py", line 2671, in parsed_version
  self._parsed_version = parse_version(self.version)
File "~/.local/lib/python3.10/site-packages/pkg_resources/__init__.py", line 122, in parse_version
  print(''.join(traceback.format_stack()))

#816 HTTPS 知识点

2022-11-11

HTTPS = HTTP + TLS
原来是 TCP -> HTTP
现在是 TCP -> TLS -> HTTP

提升了安全性的同时,降低了一些性能。

更重要的是,互联网基础设施层面开始淘汰 HTTP:

  1. 包括谷歌在内的浏览器厂商将使用 HTTP 访问的网站标记为不安全网站,Google 还会降低 HTTP 网站的排名。
  2. 其他平台,比如苹果 App Store,微信小程序等,都要求使用 HTTPS 协议。

1、怎么提升安全性

  1. 加密数据传输: TLS 数据加密,难以被窃听和解密。即使攻击者能够截获传输的数据包,他们也不能轻易读取其中的内容。
  2. 身份验证: 受信任的第三方机构颁发的 TLS 证书,可以用来验证服务器身份。
  3. 完整性验证: 使用消息摘要算法(如 SHA-256)来验证数据的完整性,防止数据被篡改(中间人攻击)。

2、降低了多少性能

相关通信过程在 2021/01/08,了解 HTTPS 背后的原理 有描述。

损耗主要在哪些环节呢?

  1. 握手
  2. 加密、解密
  3. 数据传输(加密之后数据增大)
  4. 证书验证 - 优化:浏览器缓存证书

3、如何优化

参考 HTTPS 的资源消耗,针对消耗点进行优化。

4、工具

5、清单

在 2012 年的 RFC 6797 中,HTTP 严格传输安全被定义为网络安全标准。 创建这个标准的主要目的,是为了。

  • 证书
    • OCSP(在线证书状态协议):检查证书有效期,确保没有被吊销
  • TLS 协议版本
  • 加密套件
  • HSTS(HTTP 严格传输安全)
    RFC 6797。
    作用是避免用户遭受使用 SSL stripping(剥离,HTTP 降级攻击) 的 中间人攻击(man-in-The-middle,MITM)

    add_header Strict-Transport-Security "max-age=31536000";
    
  • HTTP/2 支持

  • Apple ATS(App Transport Security)
    > 苹果 ATS 证书的选择及配置
    > 自 2017 年 01 月 01 日起,根据苹果公司要求,所有 iOS 应用必须使用 ATS(App Transport Security),即 iOS 应用内的连接必须使用安全的 HTTPS 连接。

#815 用户和客户

2022-11-11

公司曾经强调过在内外交流中区分客户和用户,不要混用。今天突然想到这个问题,特意来仔细琢磨一下。

公司的定义

我记得大概是说:

  • 客户:购买我们产品的人
  • 用户:客户使用我们产品服务的对象,也就是客户的客户

我们提供的是消息触达服务(邮件和短信),落到产品中,基础功能可以免费使用(定量),增值功能需要收费。这是背景。
在这个过程中,客户是付费的部分,用户是客户发送消息的接收方。

作为内部规范,怎么定义都行,上面怎么说就怎么来。

但我私下越细琢磨越觉得不太对味,不严谨:

  1. 免费使用的这些人,是我们的什么?
  2. 消息接收方一定向客户付费么?

我认为

  1. 客户和用户

  2. 客户,这描述的是商务关系,认为是购买产品的人,没毛病。

  3. 用户,应该是使用我们产品的人,也就是我们的服务对象。

我在网上找了一遍,大概都是这个意思。和我对这个两个词语义上的理解是吻合的。

  1. 客户内部的角色

  2. 决策人(KP)

  3. 购买人/付款人
  4. 使用人

他们应该整体作为一个客户,不能区分开来,认为是决策人,或者购买人是客户,使用人是用户。

比如爸爸为孩子挑选了一款平板电脑,妈妈付钱,孩子使用。在平板电脑公司来看,应该认为他们是一个整体,然后找到 KP 做营销,告知这款平板多么利于学习,价格多么合适,还有最后一天的节日优惠。然后为了企业的品牌形象与二次营销,应该提升产品的质量和使用体验(持续更新的学习资料 + 游戏性能)。

  1. 客户使用我们的产品去通知也好,去营销也好,这些消息的接收方,应该叫做我们服务的影响人(我找不到更好的词)。

通过上面的定义,我们应该很容易理解下面几个观点:

  1. 仅根据是不是有商务关系(付钱)判断是不是客户。
  2. 用户分成免费用户、付费用户。免费用户一定不是客户,付费用户也不一定是客户。
  3. 免费用户:仅在免费额度范围内使用基础服务、部分产品提供的免费试用。
  4. 因为可能是客户购买之后提供给他使用的。
    1. 我以前做过的一款产品就是客户购买之后,客户的客户使用。
    2. 又比如,周黑鸭、良品铺子,每年有不少公司买他们的产品用来送人。那么他们的客户是这些购买的公司,接受这些礼品的就是用户了。

因为日常生活中,很多时候,客户(购买者)往往就是用户(使用者),我们才容易搞混。

在一些业务场景下,我们客户假定购买者就是使用者,那么付费用户也可以说成是付费客户。
免费用户不能说免费客户。

产品和服务

在写这篇文章的时候,我又想起另一个概念来了,我们是提供产品,还是提供服务?

如果没记错,在内部会议上讨论过,老板阐述了自己的观点。
当时的讨论内容我忘了,我这里就说说我现在的想法。

还是上面的例子,我买周黑鸭送给了客户,客户转手把周黑鸭送给朋友吃了。
周黑鸭的客户是我,没有问题。周黑鸭的用户是谁?

如果按照提供产品的观点,最后产品被老板的朋友吃了,那么老板的朋友是用户。
周黑鸭应该为我提供体面又具有性价比的产品,同时为最后吃到鸭脖子的人提供美味(产品体验)。

如果按照提供服务的理念,周黑鸭公司提供的是礼品服务,那么是我在使用这个服务,所以我既是客户,又是用户。
周黑鸭应该为我提供体面又具有性价比的礼品服务,同时为我提供最好的送礼效果(产品体验),就是让我的客户收到礼物之后觉得满意,然后我和客户的友好度 +1。

这个例子可能不太恰当,但是我想表达的意思都说清楚了。

#814 转载:Linux/UNIX 编程如何保证文件落盘

2022-11-10

我们编写程序 write 数据到文件中时,其实数据不会立马写入磁盘,而是会经过层层缓存。每层缓存都有自己的刷新时机,每层缓存都刷新后才会写入磁盘。这些缓存的存在是为了加速读写操作,因为如果每次读写都对应真实磁盘操作,那么读写的效率会大大降低。带来的坏处是如果期间发生掉电或者别的故障,还未写入磁盘的数据就丢失了。对于数据安全敏感的应用,比如数据库,比如交易程序,这是无法忍受的。所以操作系统提供了保证文件落盘的机制。我们来看下这些机制的原理和使用。

#813 为什么 C/C++ 不可替代

2022-10-31

英文原文:https://levelup.gitconnected.com/why-modern-alternative-languages-never-replace-c-c-cbf0afc5f1dc
中文翻译:https://mp.weixin.qq.com/s/tr69w96gOO7ia81ttilgVA

总结一下:

  1. C/C 是优秀的系统级编程语言,无数基础设施是用 C/C 开发的
  2. C/C++ 可以完全控制机器,开发者拥有最大的灵活性
  3. C/C++ 性能非常好
  4. C/C++ 学术友好,简单,高效,直接(没有封装得很抽象)
  5. 所有操作系统提供 C API,其他语言也都提供了和 C 交互的方式

#812 服务丧尸?为什么

2022-10-31

发现一个问题,暂时没有任何思路:

一个线上执行了 66 天的服务,突然在前天凌晨 1:30 没有日志输出了,从外网连接不上,内网可以连上。

等后续有了进展再更新。

#811 Go DNS 解析

2022-10-30

A 记录

package main

import (
    "fmt"
    "net"
)

func main() {
    // 方法 1
    // func ResolveIPAddr(network, address string) (*IPAddr, error)
    ipAddr, err := net.ResolveIPAddr("ip", "www.google.com")
    if err != nil {
        fmt.Println("解析IP地址失败:", err)

    } else {
        fmt.Println("IP地址是:", ipAddr.IP)
    }

    // 方法 2
    // func LookupMX(name string) ([]*MX, error)
    // func LookupTXT(name string) ([]string, error)
    // func LookupIP(host string) ([]IP, error)
    // func LookupHost(host string) ([]string, error) // only IPv4
    ips, err := net.LookupHost("www.google.com")
    if err != nil {
        fmt.Println("解析主机名失败:", err)
    } else {
        for _, ip := range ips {
            fmt.Println("IP地址是:", ip)
        }
    }
}

MX 记录

package main

import (
    "fmt"
    "net"
)

func main() {
    records, err := net.LookupMX("qq.com")
    if err != nil {
        fmt.Println("解析MX记录失败:", err)
    } else {
        fmt.Printf("%#v", records)
    }
}

指定 DNS 服务器

func queryMX(dns string, domain string) ([]*net.MX, error) {
    resolver := &net.Resolver{
        PreferGo: true,
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            dialer := &net.Dialer{}
            return dialer.DialContext(ctx, "udp", dns)
        },
    }
    return resolver.LookupMX(context.Background(), domain)
}

完整版本

package main

import (
    "context"
    "fmt"
    "net"
    "strings"
    "time"
)

var dnsServers = []string{
    "223.5.5.5:53",
    "114.114.114.114:53",
    "8.8.8.8:53",
}

type IPType int

const (
    NotIP IPType = iota
    IPv4
    IPv6
)

const (
    DNSLookupTimeout = 500 * time.Millisecond // time.Duration
)

func GetIPType(host string) IPType {
    ip := net.ParseIP(host)
    if ip == nil {
        return NotIP
    }
    if ip.To4() != nil {
        return IPv4
    }
    if ip.To16() != nil {
        return IPv6
    }
    return NotIP
}

type Resolver struct {
    DNS      string
    Resolver *net.Resolver
}

func NewResolver(dns string) Resolver {
    resolver := Resolver{}
    resolver.DNS = dns
    resolver.Resolver = &net.Resolver{
        PreferGo: true,
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            dialer := &net.Dialer{}
            return dialer.DialContext(ctx, "udp", dns)
        },
    }
    return resolver
}

func (r Resolver) QueryA(domain string) ([]string, error) {
    return r.Resolver.LookupHost(context.Background(), domain)
}

func (r Resolver) QueryMX(domain string) ([]*net.MX, error) {
    ctx, cancel := context.WithTimeout(context.Background(), DNSLookupTimeout)
    defer cancel()
    return r.Resolver.LookupMX(ctx, domain)
}

func (r Resolver) Query(domain string) ([]*net.MX, error) {
    addrs, err := r.QueryMX(domain)
    if err != nil {
        return nil, err
    }
    var result []*net.MX
    for _, addr := range addrs {
        // hostname := strings.TrimRight(addr.Host, ".")
        ipType := GetIPType(addr.Host)
        fmt.Printf("%s: %#v\n", addr.Host, ipType)
        if ipType == NotIP {
            ips, err := r.QueryA(addr.Host)
            if err != nil {
                continue
            }
            for _, ip := range ips {
                result = append(result, &net.MX{Host: ip, Pref: addr.Pref})
            }
        }
    }
    return result, nil
}

var resolvers map[string]Resolver

func Query(domain string) ([]*net.MX, error) {
    var addrs []*net.MX
    var err error
    for _, dns := range dnsServers {
        var r Resolver
        if r, ok := resolvers[dns]; !ok {
            r = NewResolver(dns)
            resolvers[dns] = r
        }
        addrs, err = r.Query(domain)
        if err == nil {
            break
        }
        fmt.Printf("Error: %s: %s: %#v\n", dns, domain, err)
    }
    return addrs, err
}

func QueryAsync(domain string, ch chan<- []*net.MX, errCh chan<- error) {
    addrs, err := Query(domain)
    if err != nil {
        errCh <- err
        return
    }
    ch <- addrs
}

func init() {
    resolvers = make(map[string]Resolver)
}

func main() {
    {
        domains := []string{"qq.com", "gmail.com", "google.com"}
        for _, domain := range domains {
            fmt.Println(strings.Repeat("=", 100))
            addrs, err := Query(domain)
            if err != nil {
                fmt.Printf("Error: %#v\n", err)
            } else {
                for _, addr := range addrs {
                    fmt.Printf("%#v\n", addr)
                }
            }
        }
    }

    {
        fmt.Println(strings.Repeat("=", 100))
        ch := make(chan []*net.MX)
        errCh := make(chan error)
        go QueryAsync("google.com", ch, errCh)
        select {
        case addrs := <-ch:
            fmt.Println("MX Records:")
            for _, addr := range addrs {
                fmt.Printf("%#v\n", addr)
            }
        case err := <-errCh:
            fmt.Println("Error:", err)
        }
    }
}

#810 CSDN 免登陆复制

2022-10-27

CSDN 会拦截复制,提示需要登录。

近日在网上学到一招,在控制台输入一行代码就好了:

document.designMode = "on";

#809 ping github.com 得到 127.0.0.1 问题

2022-10-27
git clone git@github.com:tornado/tornado
Cloning into 'tornado'...
ssh: connect to host github.com port 22: Connection refused
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
ping github.com

正在 Ping github.com [::1] 具有 32 字节的数据:
来自 ::1 的回复: 时间<1ms
来自 ::1 的回复: 时间<1ms
来自 ::1 的回复: 时间<1ms

::1 的 Ping 统计信息:
    数据包: 已发送 = 3,已接收 = 3,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 0ms,最长 = 0ms,平均 = 0ms
Control-C
ping -4 github.com

正在 Ping github.com [127.0.0.1] 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=64
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=64
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=64

127.0.0.1 的 Ping 统计信息:
    数据包: 已发送 = 3,已接收 = 3,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 0ms,最长 = 0ms,平均 = 0ms
Control-C
nslookup github.com
Server:         192.168.31.1
Address:        192.168.31.1#53

Non-authoritative answer:
Name:   github.com
Address: 127.0.0.1
Name:   github.com
Address: ::1

配置 DNS 为 AliDNS 223.5.5.5233.6.6.6

nslookup github.com
Server:         223.5.5.5
Address:        223.5.5.5#53

Non-authoritative answer:
Name:   github.com
Address: 20.205.243.166

问题依旧。

ping 20.205.243.166

正在 Ping 20.205.243.166 具有 32 字节的数据:
请求超时。
请求超时。
请求超时。

20.205.243.166 的 Ping 统计信息:
    数据包: 已发送 = 3,已接收 = 0,丢失 = 3 (100% 丢失),
Control-C
ping baidu.com

正在 Ping baidu.com [39.156.66.10] 具有 32 字节的数据:
来自 39.156.66.10 的回复: 字节=32 时间=23ms TTL=52
来自 39.156.66.10 的回复: 字节=32 时间=24ms TTL=52
来自 39.156.66.10 的回复: 字节=32 时间=23ms TTL=52

39.156.66.10 的 Ping 统计信息:
    数据包: 已发送 = 3,已接收 = 3,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 23ms,最长 = 24ms,平均 = 23ms
Control-C

总结

  1. hosts 没有特殊配置
  2. 改成可用 DNS,问题没有修复
  3. ipconfig /flushdns 也不管用
  4. 重启也没有好

那就奇怪了,这个 ping 里面的本地回环地址是哪里来的呢?

最后,

ipconfig /displaydns 可以看到,还是用的回环地址
ipconfig /all 发现还有一个 DNSv6 配置。。。

DNSv6 设置成 AliDNS 的 IPv6 地址:2400:3200::12400:3200:baba::1 再试,

ping github.com

正在 Ping github.com [20.205.243.166] 具有 32 字节的数据:
请求超时。
请求超时。
请求超时。
请求超时。

20.205.243.166 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 0,丢失 = 4 (100% 丢失),

终于好了。😂

事情的起源是 git clone 失败,最后 git clone 可以了。
ping 不通,可能是网络问题,也可能是 github 网络配置。
这就不管了。