Hexo 是我目前用过的最好的,为静态博客而生的框架。Hexo 到今年为止,也陪伴了我 4 年的时间。
如果你需要的是快速建立一个自己的博客或者日志,并且就这么单纯的使用它,它几乎是你的最佳选择。
最初,moe.jimmy0w0.me 的定位是区别于 blog.jimmy0w0.me 的,Moe 更偏向于每日一更的日常记录;所以,当初在选择框架的时候我毫不犹豫地选择了 Hexo,因为我只需要使用 Markdown 编写这一切,然后发布即可。
Hexo 有着不可否认的便捷性和一定的拓展性,有很多不错的插件和主题通过安装和简单配置就可以使用。但,Hexo 的缺点也是显而易见的,那就是自由度。当然,我必须要说这并不是 Hexo 的问题,而是我个人的需求发生了变化。
我曾经有试着拓展 Hexo 的功能性,诸如 hexo-generator-restful
和 hexo-generator-json-content
这些插件可以生成出 JSON,以供其他的功能需求。例如我在多年前就使用这个方法开发出了可以与 Moe 同步的小程序。
但是这终究还是不完美,随便就能指出这种方案带来的劣势。比如无法对 JSON 进行分页和字段筛选,导致请求一次就必须一股脑地将所有的数据(包括文章内容)都返回回来。就截止到这篇文章,请求一次的大小近乎要达到 1 MB。
不仅如此,Hexo 无法提供一些 API。我无法或者难以使用外部的 API 或者 Markdown 以外的数据来生成页面;或者我也无法灵活的使用各种不同的 Web 前端的技术 (但是说的你好像很会 Web 前端的开发一样 :\
)
再加上新的需求不断涌现,例如随时随地的编辑和保存,部分内容的国际化处理,一些漫展/角色返图的展示,我甚至还考虑过让一篇文章的构成可以由不同的控件构成,而不是单纯的文字。这些需求让我开始反思,是否要在 Hexo 的基础上继续折腾。
我的最终答案就是:不
我需要前后端分离,但又方便我进行内容创作的东西。我希望可以自行部署,并且有完善的备份方案作为保护措施。最终,我选择了 Strapi
Strapi
提供了后端和一个方便数据模型和内容管理的后台,支持国际化,具有插件 API,更好的是,它有更加完善的账号的鉴权和权限管理。除此之外,它还能用 RESTful (是真的 RESTful) 和 GraphQL 两种方式来提供 API。
我并没有使用独立的数据库 (例如 Postgres 或者 MySQL),而是作为文件存在的 SQLite
,因为它可以被 Git 一同进行版本管理并被上传到 GitHub 当中。如果出现了任何问题,Git 作为分布式的版本管理,可以随时的让我进行可靠的版本回滚或者数据恢复。
现在,我终于拥有了一套完善的数据模型和非常好的编辑体验
并且依靠 Cloudflare 的 Zero Trust 服务,我还可以将我的后台给完全保护起来,免受未经授权的用户访问到。
而关于前端则是完全自由的状态。因为后端仅提供 API,前端可以以任何形式展现了。对于 Web 来说,目前的想法是使用 Next.JS 来重塑新的页面。总而言之,这是对于 2019 年以来,一次新的尝试与开始。
但这,也意味着,Moe 将要关闭了…
jimmy0w0.me 这个域名在这几年以来几乎是为 Moe 所服务的,但当新的网站上线后,这一切都会被改变。jimmy0w0.me 将会重新被使用。具备有新的 Web 技术,譬如像是 PWA,AMP (For Google Only),更好的搜索功能 (预计由 Algolia 提供) 以及更好的 SEO
以上画饼我尽可能实现好吧
新的网站将会继承,重写或者填坑在 Moe 中开的一些系列,使其变得质量更高,也更完善。同时,这篇文章也将寓意着 Moe 即将变成归档状态,不再更新与维护。
谢谢这些年,你们,身边的人,以及 Hexo 陪我度过的这些时光
2023/9/25 8:12 AM
]]>In this article, I will provide a rough overview of the main process from implementing HOTP to TOTP. I won’t delve into the detailed explanations of each line of code; instead, the primary focus will be on extracting the implementation steps and highlighting some points I encountered during the development process that require attention.
Dans cet article, je vais donner un aperçu général du processus principal de la mise en œuvre de HOTP à TOTP. Je n’entrerai pas dans les explications détaillées de chaque ligne de code ; à la place, l’accent principal sera mis sur l’extraction des étapes de mise en œuvre et la mise en évidence de certains points que j’ai rencontrés lors du processus de développement qui nécessitent une attention particulière.
在这篇文章中,我会粗略的介绍从实现 HOTP 到 TOTP 的主要重点过程。我不会细致的阐述每行代码的作用,相反,这篇文章主要提炼出实现步骤和我在开发过程中遇到的一些需要注意的地方。
If you wish to refer to detailed processes, principles, and information about HOTP and TOTP, please visit the following website:
Si vous souhaitez consulter des processus détaillés, des principes et des informations sur HOTP et TOTP, veuillez visiter le site Web suivant :
如果你想参考详细的 HOTP 和 TOTP 的过程,原理和资料,请查看下列的网站:
To implement RFC 6238 (TOTP), it has been stated in the standard’s Abstract section that TOTP is an extension of HOTP (RFC 4226), thus requiring the implementation of RFC 4226 (HOTP).
Pour mettre en œuvre RFC 6238 (TOTP), il a été indiqué dans la section Résumé de la norme que TOTP est une extension de HOTP (RFC 4226), ce qui nécessite donc la mise en œuvre de RFC 4226 (HOTP).
为了实现 RFC 6238 (TOTP),根据标准的 Abstract 部分已经说明 TOTP 是 HOTP 的一个拓展,因此需要对 RFC 4226 (HOTP) 进行实现
This document describes an extension of the One-Time Password (OTP)
algorithm, namely the HMAC-based One-Time Password (HOTP) algorithm,
as defined in RFC 4226, to support the time-based moving factor.
Implementing HOTP requires three essential parameters that we will be using today:
La mise en œuvre de HOTP nécessite trois paramètres essentiels que nous utiliserons aujourd’hui :
实现 HOTP 有三个重要的,今天我们会用到的参数
1 | Symbol Represents |
Using the HMAC-SHA1 algorithm, generate the hash value hs
. The key is the aforementioned K
(Key), and the message is C
(Counter in 8 bytes).
Utilisez l’algorithme HMAC-SHA1 pour générer la valeur de hachage hs
. La clé est la K
(Clé) mentionnée précédemment, et le message est C
(Compteur sur 8 octets).
利用 HMAC-SHA1 算法生成出散列值 hs
。密钥是上文提到的 K
(Key) ,消息为 C
(Counter in 8-byte)
1 | Step 1: Generate an HMAC-SHA-1 value Let HS = HMAC-SHA-1(K,C) // HS is a 20-byte string |
1 | var hmac = new HMACSHA1(Secret); |
The code above is written in C#, keeping only the main parts as an example.
Le code ci-dessus est écrit en C#, ne conservant que les parties principales à titre d’exemple.
上面的代码使用 C# 编写,仅保留主要部分作为示例
This step requires using a method called Dynamic Truncation
to convert the 20-byte HMAC hash result into a shorter fixed code, which serves as a one-time password.
Cette étape nécessite l’utilisation d’une méthode appelée Dynamic Truncation
pour convertir le résultat de hachage HMAC de 20 octets en un code fixe plus court, qui sert de mot de passe à usage unique.
这一步需要使用一个叫做 Dynamic Truncdation
的方法,将 20 字节的 HMAC 哈希结果转成一个较短的固定代码作为一次性密码
1 | Step 2: Generate a 4-byte string (Dynamic Truncation) |
Since the hmac.ComputeHash
method in .NET will return a byte[]
type, I will be directly working with data of byte[]
type.
Étant donné que la méthode hmac.ComputeHash
dans .NET renverra un type byte[]
, je vais directement travailler avec des données de type byte[]
.
由于 .NET 中的 hmac.ComputeHash
方法将会返回 byte[]
类型,我将直接操作 byte[]
类型的数据
1 | private int DynamicTruncation(byte[] p) |
This step has completed the entire second step, and the return type is an integer, as we will use it for numerical operations in the third step.
Cette étape a achevé l’ensemble de la deuxième étape, et le type de retour est un entier, car nous l’utiliserons pour des opérations numériques dans la troisième étape.
这一步已经完成了整个第二步,返回类型为整数,因为我们要带入第三步参与数字运算
1 | Step 3: Compute an HOTP value |
In this step, we need to perform a modulo operation on the number we obtained. The algorithm is num % 10^Digit
.
Dans cette étape, nous devons effectuer une opération de modulo sur le nombre que nous avons obtenu. L’algorithme est le suivant : num % 10^Digit
.
这一步需要将我们得到的数字进行取模运算,算法为 num % 10^Digit
1 | var code = (DynamicTruncation(hs) % (int)Math.Pow(10, Digit)) // hs is defined in Step 1. |
At this point, the one-time password generation for HOTP is complete, and we will convert it to a string type.
À ce stade, la génération du mot de passe à usage unique pour HOTP est terminée, et nous allons le convertir en type chaîne de caractères.
到这一步就完成了 HOTP 的一次性密码生成,我们将它转为字符串类型
1 | var code = (DynamicTruncation(hs) % (int)Math.Pow(10, Digit)).ToString().PadLeft(Digit, '0'); |
Since we are working with numerical data when calculating Dynamic Truncation and performing modulo operations on numbers, if a one-time password includes a 0
in the highest order of magnitude, the mathematical 0
will be discarded. Therefore, when we convert this to a string (or any data type in any language that represents arbitrary text), we need to apply the corresponding PadLeft
operation. In this case, you can use the string.PadLeft()
method directly in C#.
Étant donné que nous travaillons avec des données numériques lors du calcul de la Troncature Dynamique et lors de l’exécution d’opérations de modulo sur des nombres, si un mot de passe à usage unique inclut un 0
dans la position la plus élevée, le 0
mathématique sera ignoré. Par conséquent, lorsque nous le convertissons en une chaîne de caractères (ou tout autre type de données dans n’importe quel langage qui représente du texte arbitraire), nous devons effectuer l’opération PadLeft
correspondante. Dans ce cas, vous pouvez utiliser directement la méthode string.PadLeft()
en C#.
由于在计算 Dynamic Truncation 和对数字进行取模运算时,我们的数据类型为数字。因此,如果一次性密码中包含有 0
出现在数量级最高位,在数学中的 0
将会被舍弃。所以在我们转换成字符串 (或者在任何语言中可以代表任意文本的数据类型) 需要进行相应的 PadLeft 操作,在此,C# 中可以直接使用 string.PadLeft()
方法
To validate its algorithm correctness, test cases are provided for developers in the standard Appendix D.
Pour vérifier la justesse de son algorithme, des cas de test sont fournis aux développeurs dans le standard Annexe D.
为了验证其算法正确性,在标准的附录 D 中携带了测试用例供开发者测试
1 | Appendix D - HOTP Algorithm: Test Values |
1 | class HOTP |
1 | class Program |
1 | 755224 |
With the completion of the implementation of HOTP, we have already accomplished more than half of the entire project. Since TOTP is an extension of HOTP, our workload will be significantly reduced.
Avec l’achèvement de la mise en œuvre de HOTP, nous avons déjà accompli plus de la moitié de l’ensemble du projet. Étant donné que TOTP est une extension de HOTP, notre charge de travail sera considérablement réduite.
完成了对于 HOTP 的实现,我们就已经做到了整个项目的一大半了。因为 TOTP 是对 HOTP 的一个拓展,因此我们的工作量会大大减少。
Navigating to the standard’s Section 4 is the core description of the entire TOTP algorithm.
Se rendre à la Section 4 de la norme constitue la description centrale de l’algorithme TOTP dans son ensemble.
前往标准的 Section 4 部分是对整个 TOTP 算法的核心描述
1 | o X represents the time step in seconds (default value X = |
According to the description above, we need to obtain the time step X
and the timestamp parameter T
(usually in UTC timestamp format, measured in seconds). In general, the parameter T0
is 0, so subtraction calculation can be omitted.
Selon la description ci-dessus, nous devons obtenir le pas de temps X
et le paramètre de l’horodatage T
(généralement au format horodatage UTC, mesuré en secondes). En général, le paramètre T0
est de 0, donc le calcul de soustraction peut être omis.
根据上面的描述,我们需要获得时间步长 X
和时间戳参数 T
(一般为 UTC 时间戳,单位为秒)。一般情况下,参数 T0
为 0,因此可以不执行减法计算。
1 | var x = 30; // Time step in seconds |
Finally, by using the parameter T
as the Counter
parameter for HOTP, you can obtain the one-time password (OTP) for TOTP.
Enfin, en utilisant le paramètre T
comme paramètre de « Counter » pour HOTP, vous pouvez obtenir le mot de passe à usage unique (OTP) pour TOTP.
最后,将参数 T
作为 HOTP 的 Counter
参数传入,就可以得到 TOTP 的一次性密码
1 | var totp = new HOTP(SECRET, t, DIGIT); // This is pseudocode, please refer to the complete code example provided at the end of the article. |
In this step, we will discuss the correctness after the implementation of the TOTP algorithm is completed… First of all, here is the complete code for the entire project.
À cette étape, nous allons discuter de la justesse après que l’implémentation de l’algorithme TOTP est achevée… Tout d’abord, voici le code complet pour l’ensemble du projet.
这一步我们将探讨 TOTP 算法实现完毕后的正确性…首先,这是整个项目完整的代码
1 | class HOTP |
In the standard, Appendix B, there are also test cases similar to HOTP. However, please note that the Digit
parameter in the test cases consists of 8 digits, not 6 digits.
Dans la norme, Annexe B, il existe également des cas de test similaires à HOTP. Cependant, veuillez noter que le paramètre Digit
dans les cas de test est constitué de 8 chiffres, et non pas de 6 chiffres.
在标准的 Appendix B 中同样具有跟 HOTP 一样的测试用例。但请注意,测试用例中的 Digit
参数为 8 位,而不是 6 位。
1 | The test token shared secret uses the ASCII string value |
To test the correctness of this standard implementation, it is necessary to compute the same TOTP result based on the timestamps and keys provided in the test cases. Therefore, we need to make slight modifications and update the logic related to the T
parameter of TOTP as follows:
Pour tester la justesse de cette mise en œuvre standard, il est nécessaire de calculer le même résultat TOTP en se basant sur les horodatages et les clés fournis dans les cas de test. Par conséquent, nous devons apporter de légères modifications et mettre à jour la logique liée au paramètre T
de TOTP comme suit :
为了测试该标准实现的正确性,需要根据测试用例中提供的时间戳和密钥计算出同样的 TOTP 结果。因此我们需要稍作修改,将 TOTP 的 T
参数相关的逻辑代码修改成如下:
1 | +-------------+--------------+------------------+----------+--------+ |
1 | public string Code() |
1 | class Program |
1 | 89005924 |
Look, we are able to calculate the correct results given by the test cases in the standard.
Regardez, nous sommes capables de calculer les résultats corrects donnés par les cas de test dans la norme.
看,我们能计算出标准中的测试用例给的正确结果了
In typical real-world usage, keys are often encoded using Base32
. To obtain the correct results, developers usually need to decode the key using Base32
before proceeding. As a result, I will provide the implementation I’ve used for Base32
decoding in this article.
Dans des cas d’utilisation réels courants, les clés sont souvent encodées en utilisant le format Base32
. Pour obtenir les résultats corrects, les développeurs doivent généralement décoder la clé en utilisant le décodage Base32
avant de continuer. Par conséquent, je vais fournir dans cet article l’implémentation que j’ai utilisée pour le décodage en Base32
.
在一般真实的使用情况下,密钥通常经过了 Base32
编码。为了获得正确的结果,通常开发者需要先对密钥进行 Base32
解码的操作。因此我将在本文章提供我使用的 Base32
解码的实现。
1 | /* |
To test the usability of the TOTP code with real keys, I recommend using the TOTP testing page provided by Authentication Test as a test case. The key is typically I65VU7K5ZQL7WB4E
(but please refer to the key provided on the website). The time step X
parameter and the Digit
parameter are usually set to common values of 30 and 6, respectively.
Pour tester l’efficacité du code TOTP avec des clés réelles, je recommande d’utiliser la page de test TOTP proposée par Authentication Test comme cas de test. La clé est généralement I65VU7K5ZQL7WB4E
(mais veuillez vous référer à la clé fournie sur le site web). Le paramètre d’intervalle de temps X
et le paramètre Digit
sont généralement définis à des valeurs courantes de 30 et 6, respectivement.
为了测试 TOTP 代码在真实密钥情况下的可用性,我推荐 Authentication Test 的 TOTP 测试页面作为测试用例。密钥一般为 I65VU7K5ZQL7WB4E
(但请还是根据网站提供的密钥为准)。时间步长 X
参数和 Digit
参数分别为常用的 30 和 6。
1 | class Program |
Once you have the result, enter the TOTP password, click on “Login,” and observe whether you can successfully log in.
Après avoir obtenu le résultat, saisissez le mot de passe TOTP, cliquez sur “Se connecter” et observez si vous pouvez vous connecter avec succès.
得到结果后,将 TOTP 密码填入,点击登录。并且观察是否能登陆成功即可。
1 |
|
1 | // libotp.cpp : Defines the functions for the static library. |
五月份,紫川,一元,我参加了 20 日的漫展。
七月份是我的生日,凑在一起玩了一场密室
去玩前的午餐,紫川
密室最后的结束照,最前排的三个人是我们
后面都是一些好吃的晚餐
小插曲,因为最早我们都不饿,因此很遗憾紫川没能参加上这次晚餐
最后买了一块小蛋糕
一定要在一起长长久久啊!
这是我们三个人的新 Twitter
我: https://twitter.com/Jimmy0w0
一元: https://twitter.com/bmpof
紫川: https://twitter.com/Seamain37
这是他们两的 bilibili
一元: https://space.bilibili.com/37802015
紫川: https://space.bilibili.com/252931506/
持续更新(如果这个项目还活着的话)
使用工具:
Google Chrome ( To read the fking manual )
Visual Studio ( 我还需要说嘛 :\ )
OpenGL ( 同上 :/ )
vcpkg ( 真的需要说嘛 )
1 | vcpkg install glfw3:x64-windows glfw3:x86-windows glew:x64-windows glew:x86-windows glm:x64-windows glm:x86-windows |
使用 vcpkg 安装任何 OpenGL 的扩展和所需依赖
跟着 manual 写到目前我可以说 Intel 已经被针对了 ( 什么 )
1 | // Win32GLTesting.cpp : This file contains the 'main' function. Program execution begins and ends there. |
出现了
1 | ERR: Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible |
为了以最快的速度跑起我们的第一个 Demo Application,我找到了这篇 manual 的 GitHub REPO 并且使用了老写法
1 | // Win32GLTesting.cpp : This file contains the 'main' function. Program execution begins and ends there. |
至于为什么 glCleaerColor
要除以 255,根据 manual 说明这个函数接受的 RGBA 值是 0 到 1 的区间
更新分割线
]]>别看了,我跑去 Vulkan 了,下篇文章见
说这是我的第一个 Compose 写的 Android App,其实事实上也倒不是这么回事。之前入门的时候肯定也写了几个小 Demo,但我第一个写的 Compose Demo App 就是姐姐好骂。
Compose 是 Google 代替命令式 UI 的新产品,它采用新的声明式 UI 来构建 App。我之前有 SwiftUI 开发的经验,上手 Compose 相比于普通的命令式 UI 来说确实是非常简单并且符合我对 Compose 的想象的。
但又或许是我第一次入门 Android 开发的原因,我发现 Android 在开发的时候有很多基础(至少对于 iOS 开发来说是这样的)的功能都需要安装来自不同地方的依赖。最常见的就是网络请求,异步图片,ViewModel 和 Key-Value 存储等等。在 iOS 上均有系统自带的实现,例如 URLSession
,AsyncImage
,@StateObject
和 UserDefaults
,而 Android 需要安装例如 Retrofit
,Coil
,ViewModel
和 SharedPreferences
等等
还有一点,关于 Material Design 3。我承认是有先入为主,因为我一直以为 Android 原生开发默认采用的就是当前 Android 版本的组件库,例如 Android 12 级以上版本会理所应当的使用 Material Design 3。但后来我发现其实 Material Design 3 与系统组件库是没有任何关联的。在 iOS 开发中,更详细的说是 SwiftUI,系统组件库的样式是和系统版本相关的,一套代码在不同的平台上跑也会得到不同的 UI 样式。而 Android 中,Material Design 3 实际上是需要另外安装一套 Compose 使用的设计实现。这是因为 Material Design 3 仅仅提供了一份设计规范文档,而不提供代码或者任何实现。不过 Google 针对 Web,Flutter 和 Compose 都提供了 Material Design 3 的实现就是了。反正 Material Design 3 这套规范你在哪里实现都可以
姐姐好骂是我写的第一个练手 App,大概长这样
姐姐好骂这个名字怎么来的,是因为当时玩彩六的时候出了新干员 Solis,因为长得确实很帅,因此取了个外号叫做帅姐姐
后来又想把 Solis 的图片做成 App Icon,因此姐姐好骂这个名字就这么来了
骂人宝典,后祖安语录是一个项目。这个项目收集了来自用户们的投稿的各种骂人语句。最开始的时候还相安无事,但是大概在 22 年初的时候再次被 DNS 污染后作者放弃了挣扎,将数据库给开放了出来。姐姐好骂项目就是基于这个开放的数据库来实现的。
姐姐好骂 2.0 是因为初版实在写的太烂,我也完全不想再去碰这些老代码了,于是就重构出了新的版本。
我用了 MVVM 来重写整个 App,并且也将会新增一些新功能,这些功能将会帮助你在骂人之旅上越走越顺(不是)
目前已经新增了骂爹模式,还差一个搜索和筛选的功能就大概完成我心目中的样子了
害,在这个 App 之前其实我也写了好几个 Android App 来作为经验积累,每次都会被气到爆炸(
这次进行的姐姐好骂重构已经算是十分顺利了,至少到现在基本上都可以走之前走过的老路,开发起来还算是平滑
我是一个软件外貌协会的人,也算是蛮欣赏 Material Design 3 的设计,要不是 MD3 或许可能没法坚持下来到现在
]]>For developers who are too busy to read, I will briefly demonstrate how to develop a SwiftUI app using the MVVM pattern.
After creating a SwiftUI project in Xcode, you will usually get two Swift files, one with the same name as your project and the other one is generally called ContentView (may change over time, but currently it is still called ContentView).
Next, let’s create a View Model for ContentView.
Create a new Swift file and name it ContentViewViewModel, and remember to import SwiftUI at the top. Your code should look like this for now:
1 | import SwiftUI |
Now comes the highlight of the article. Because I want to create a View Model for ContentView instead of other Views, I can use a Swift feature called extension to extend ContentView, so that I can use what I am going to write in ContentView.
1 | import SwiftUI |
Go back to ContentView and rewrite it like this:
1 | import SwiftUI |
Great, we have already implemented the simplest MVVM. However, I am not going to end it here. I want to add the function of network requests to ContentViewViewModel to better meet the actual development needs.
Here, I will use a Mock API to simulate some data, which can be found at https://jsonplaceholder.typicode.com/.
I will use the address https://jsonplaceholder.typicode.com/posts, which returns a structure similar to this:
1 | [ |
I will create the model in ContentViewViewModel, which makes it more convenient. In actual cases, it is recommended to create a separate file to build the model.
1 | struct Post: Decodable, Identifiable { |
Then, let’s write a function for requesting data in ContentViewViewModel, and call it in init()
to achieve automatic request.
1 | import SwiftUI |
Finally, go back to ContentView
1 | import SwiftUI |
Mission accomplished, now you already have some concepts about developing SwiftUI App using MVVM pattern.
]]>为 Hexo 启用 LaTeX 公式支持
1 | npm i hexo-math --save |
在文章内插入
1 | {% mathjax %} |
这个插件似乎是可以由 MathJax 支持的,因此可以直接使用 TeX 数学表示法,理论上应该可以用基于 XML 的 MathML,但是我这边测试不出效果 (我想也不会有人手写 MathML 吧)
我们可以试试使用 TeX 来描述一个公式,例如 Glicko 评分系统中测算新评分的公式
1 | r=r_{0}+\frac{q}{\frac{1}{R D^{2}}+\frac{1}{d^{2}}} \sum_{i=1}^{m} g\left(R D_{i}\right)\left(s_{i}-E\left(s \mid r_{0}, r_{i}, R D_{i}\right)\right)r=r_{0}+\frac{q}{\frac{1}{R D^{2}}+\frac{1}{d^{2}}} \sum_{i=1}^{m} g\left(R D_{i}\right)\left(s_{i}-E\left(s \mid r_{0}, r_{i}, R D_{i}\right)\right) |
或者 Hesse-Matrix
1 | \mathbf{H} = \begin{bmatrix}\frac {\partial^2 f}{\partial x_1^2} & \frac{\partial^2 f}{\partial x_1\,\partial x_2} & \cdots & \frac{\partial^2 f}{\partial x_1\,\partial x_n} \\ \\ |
经过我的测试,它还支持一些常用的 LaTeX 宏包,例如适用于化学的 mhchem
硫喷妥钠的化学式如下所示
1 | \ce{C11H17N2NaO2S} |
把官方文章翻译了: Valve Developer Community
]]>因为需要长时间的查询相关文档和
玩游戏实践,因此在本消息移除之前,此文章都处于持续更新状态
这破游戏打得我心态爆炸了
©️ Valve Corporation
Counter-Strike: Global Offensive 具有一个叫做 Game State Integration (游戏状态整合) 或称为 GSI 的机制。其实并不止 Counter-Strike,Valve 的另一款游戏,Dota 2 同样也携带有相关的机制。
根据 Valve Developer Community 文档 (English)的介绍 (中文版: Valve Developer Community 文档 (简体中文))
Game State Integration 或 GSI 是一套可以允许任何第三方程序与游戏整合的一套系统。例如在实时直播的竞技比赛中自动控制舞台灯光和烟火系统,也可以让程序自动控制玩家的灯光或者任何其他的周边设施。通过 Game State Integration,开发者可以收集来自游戏内的实时游戏状态,将第三方的软件或者硬件设施与游戏绑定
当 CS: GO 客户端需要向目标服务器发送状态时,CS: GO 将通过 HTTP 协议发起 POST 请求,Raw Body 的内容为标准的 JSON 格式
如果需要在 CS: GO 中启动 GSI,需要向游戏的特定目录添加 .cfg
文件
目录路径位于: CS:GO 的根目录/csgo/cfg/
例如: /Volumes/Samsung T5/SteamLibrary/steamapps/common/Counter-Strike Global Offensive/csgo/cfg
在这个文件夹中添加如下命名的文件 gamestate_integration_consolesample.cfg
官方提供了一个配置文档示例
1 | "Console Sample v.1" |
这是上方示例配置文件所具有的几个属性
uri - 需要提供 HTTP 服务器的地址 (包括端口, 路径)
timeout - 等待 HTTP 服务器返回的等待时间;状态码可以是 200 - 299 的区间 (second)
buffer - 在向 HTTP 服务器发送数据前,游戏收集数据的时间 (second)
throttle - 在前一次数据发送成功后,游戏将会等待一定时间后再次发送 (second)
heartbeat - 在一定时间内,游戏将会检测 HTTP 服务器的存活状态 (second)
auth - 认证
output - 小数点控制
data - 需要发送的游戏内数据
以上是官方提供的示例配置文件中会返回的数据
完整的 data 配置对象如下
1 | "map_round_wins" "1" // history of round wins |
注意,有一部分数据,例如关于本局比赛所有玩家的数据
,炸弹数据
,玩家坐标和矢量数据
均需要进入观察模式才能返回
]]>更新分割线 2022/7/7
一般来说,一个好的命名可以让开发者更快速的明白这个字段所代表的意思,不过有些时候会事与愿违
这是什么…
恩…对于一个母语为汉语的人或许阅读起来都有一些困难,他们分别代表着
如果不希望在代码中继续沿用这样子的命名,转而采用以下命名
那该如何让 Swift 这件事
接下来,将介绍 Swift 中的 CodingKey
对该数据建模中,将想使用的名字填入
1 | struct User: Codable { |
就像这样,很简单对吧
然后,在结构体 (或者类) 的内部建立一个叫做 CodingKeys
的枚举,该枚举将会是 private
属性修饰并且遵循 CodingKey
协议
完整的结构体如下所示
1 | struct User: Codable { |
这么做,在 Decode 之后就可以通过 username
, userEmail
, userPhone
和 userAge
属性来访问这些诡异的命名的字段了
1 | import Cocoa |
1 | Name: acawood0, Email: cwerndly0@pinterest.com |
还有一些情况,一些开发者可能使用类似于 Python, PHP 等使用 snake_case
作为命名规范的语言
不过,Swift 采用的是小驼峰写法,在这里使用 snake_case
真的太奇怪了
可以使用上面提供的方法吗?是可以的,但是聪明的 Swift 还有另一个方法来处理 snake_case
的返回格式
现将上面的 User Struct 还原成
1 | struct User: Codable { |
接下来我们指定 JSONDecoder
的 Decode 策略
为了指定 JSONDecoder
的策略,我们就可以考虑将一个 JSONDecoder
的实例化赋值给一个常量
例如
1 | let decoder = JSONDecoder() |
最后 decoded 出来的数据还是跟上面的数据一致
1 | Name: bgarm0, Email: cwindas0@businessinsider.com |
WWDC 21 过后,Swift 也推出了 async await 语法糖,并且麻烦的 URLSession.shared.dataTask 闭包 也拥有了新的语法糖版本
采用 async await 的 URLSession 能够有效的帮助我们避免在使用闭包时可能出现的线程问题
如果想了解完整的详情内容,可以查看 Apple Developer 的视频
这就是今天本文要提到的 URLSession.shared.data
URLSession.shared.data
支持两种参数,一种是 from
接受普通的 URL
类型并且快速进行 HTTP GET 请求
一种是 for
接受 URLRequest
类型请求,可以创建 URLRequest 类型的变量来对请求定义 HTTP 方法,Headers,Body 等等属性
如果你还没有了解 URLRequest,可以试试看上次的文章 在 Swift 5 和 SwiftUI 中通过 URLRequest 与 URLSession 请求 JSON 并反序列化 或者访问 Apple Developer Documentation
因为 URLSession.shared.data 的使用方式都一样,为了简单演示,在这里将会使用 URL 直接发送 GET 请求
我们可以在 Xcode 中建立一个 Swift Playground
一个美丽的空项目就这么被构建出来了
这篇文章采用一个 Mock API 作为演示,我们管它叫做 Movie Ticket,API 可以返回一些当日的电影票数据
它包括了
Swift 作为静态类型语言,我们需要先建立这个数据的结构体或者类
1 | struct MovieTicket: Codable { |
这将会是接下来进行 Decode 时候要用到的结构体,请注意,这个结构体必须要遵循 Decodable
协议,当然你也可以遵守 Codable
协议
接着,我们会建立一个 async function
1 | func getTodaysMovie() async throws -> [MovieTicket] { |
在使用全新的 URLSession.shared.data
的时候,错误将不会再放入以前的闭包中,而是使用 try 来获取错误,因此请在方法指纹中加入 throws
为了接下来可以抛出一些自定义的错误,我将创建一些我自己希望抛出的错误
1 | enum MyError: Error { |
当意外发生时,我可以 throw
出这些错误
就像这样,使用 throw
来代替 fatalError()
URLSession.shared.data(from: URL)
方法将会回传一个带有 data
和 response
的元组
因此通过如此描写来取回数据
1 | let (data, response) = try await URLSession.shared.data(from: url) |
如果不需要检查 response,可以使用 _
来代替 response
在此,我们还是执行最佳做法,检查 response 中的 HTTP Status Code 来查看是否是正常的返回
1 | guard |
我们需要先将 URLResponse
类型的 response
转换为 HTTPURLResponse
的 response
并且检查 response
的 statusCode
是否等于 200,不等于将会抛出自定的 MyError.invlidServerResponse
错误
检查完毕后,就可以将 data
通过 Swift 自带的 JSONDecoder 转换为可供我们访问的 MovieTicket
类型数据
1 | let decoded = try JSONDecoder().decode([MovieTicket].self, from: data) |
因为服务器返回给我们的数据类型是多个 MovieTicket
的列表,所以使用中括号把 MovieTicket
包裹住,最后一步 return
好了,想看看服务器会返回什么数据吗
在这个方法外面的下面写点东西
1 | Task { |
1 | import Cocoa |
HyperText Transfer Protocol (HTTP) 是一种基于 TCP 协议之上的应用层协议
HTTP 协议将会通过 TCP 传输层协议来传输特定的消息格式,HTTP 服务器将会正确理解 HTTP 客户端所发来的特定格式信息,并且 HTTP 服务器再返回一个特定格式信息给客户端
这是一个 HTTP 的请求消息格式
请求行: 包括 HTTP 方法, URL, HTTP 版本
请求头行: 包括多个请求头
内容
在 HTTP 1.1 中,Header 必须包括 Host
接下来为了表示厌世精神,我们将会在 C# 中放弃使用 HttpClient, WebClient, WebRequest, HttpWebRequest 等五花八门的方式,转而使用 Socket 类来发送 HTTP 请求
在 C# 中用 Socket 请求可比 C 方便多了 xDDDDD
请确保使用 System.Net.Sockets
这个命名空间,这样我们就能构造一个新的 Socket 了
1 | var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
AddressFamily.InterNetwork 我们将要使用 IPv4 的方式连接上我们的服务器
SocketType.Stream 数据将会以字节流的方式返回
ProtocolType 为 TCP,因为 HTTP 基于 TCP 协议
根据上面的图,我们需要手动编写 TCP 传输的消息
可以先看一个例子
请求行中包含方法,我们要请求的 URL 和一个 HTTP 版本
1 | GET /get |
这行结束之后我们需要一个空格符和一个回车符,使用用 \r
和 \n
下一行是请求头,HTTP 1.1 中要求至少包含 Host 请求头
1 | Host: httpbin.org |
一样的,当请求头结束之后也要加入空格符和回车符
如果你打算结束请求头行了,再次加入空格符和回车符
因此,我们的 TCP 消息结构会写成
1 | GET /get HTTP/1.1\r\nHost: httpbin.org\r\n\r\n |
写好之后,就可以开始准备发送请求了,为了演示,我将使用 httpbin.org 这个服务
通过
1 | socket.Connect("httpbin.org", 80) |
可以向 httpbin.org 的 80 端口通信
然后用
1 | socket.Send(Encoding.UTF8.GetBytes(httpMessage)); |
发出 TCP 连接
接着我们可以准备接收服务器回传的消息了
建立一个 response 的变量用于存放 HTTP 返回消息
因为目前还没有回传的消息,可以使用 string 里面的 Empty 字段来声明一个空的字符串类型数据
1 | var response = string.Empty; |
接下来建立 byte 并且把数据放进 byte 中
1 | var receivedBytes = new byte[socket.ReceiveBufferSize]; |
receivedBytes 的大小应该是 socket 接收到数据的 Buffer 的大小
然后用 socket.Receive 方法将数据放进 receivedBytes,并且拿到有多少 Bytes 在里面
最后通过一个循环把数据转成普通字符串,存进 response 变量中
1 | for (int i = 0; i < bytesRange; i++) |
最后就可以打印出来了
1 | Console.WriteLine(response); |
1 | HTTP/1.1 200 OK |
如果需要发送数据时,修改下消息结构就好了
当我们要发送 JSON 数据的时候,其实只需要加入 Content-Type 头为 application/json 并且最后携带上 JSON 格式的消息即可
回到我们的 HTTP 消息结构,先把 GET 改成 POST,并且别忘了把 URL 的 /get 改成 /post;因为 httpbin.org 接收 POST 请求的路由在 /post 下
加入 Content-Type 的头,内容为 application/json; charset=utf8
并且放入 Content-Length,目前为了演示,我们直接写死 38 就好
最后放入 JSON 数据
1 | {"message":"Ja! Ich bin eine Katze."} |
新的请求结构是这样的
1 | var httpMessage = |
再次启动就可以拿到新的数据了
1 | HTTP/1.1 200 OK |
Httpbin 的服务器已经正确的理解了本次请求并且将 JSON 解析放入 json 对象中
最后放上整个程序的 C# 代码
1 | var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
21 年的 8 月份,neXt 这个项目就建立了,当时是为了尝试 Headless CMS 的架构并且尝试脱离 Hexo
倒也不是 Hexo 并不好用,但是 Hexo 确实拥有不方便多端开发,编辑等等问题
neXt 最开始采用的是 GraphCMS 作为后端,这是一个不错的 GraphQL Headless CMS,但是,当你的客户端并不太适合使用 GraphQL 的时候,手写 GraphQL 请求似乎又有点麻烦
这次新的 neXt 版本更新,依旧采用 GatsbyJS,但是后端将会从 GraphCMS 切换到 Directus
Directus 具有免费的托管服务,以及 RESTful 和 GraphQL 两种 API 可选,作为个人博客或者其他小型网站来说,是一个相当不错的选择
并且,这次的更新也换了一个新的脚手架
最早使用的是跟着 GatsbyJS 的文档教程写出来的,看上去的效果只能达到能看
这个脚手架原先是只适配了 Contenful,经过昨天一下午和 Seamain 的折腾,删除掉了大量无用和 Contentful 的部分,给适配上了新的 API
有兴趣的话,可以来 neXt 看看哦
]]>假设这是你第一次使用 SwiftUI,那就一起来建立一个 SwiftUI 项目吧
确认 Interface 为 SwiftUI,Language 为 Swift 后选择项目的保存目录
这是 Xcode 的项目页面,从左边往右分别为 项目目录,代码编辑区域和 SwiftUI 预览区
在顶部可以切换模拟设备
通过点击预览区域的 Resume 可以进行预览
SwiftUI 的代码中
1 | struct ContentView: View { |
第一个 ContentView 的结构体是 UI 界面
第二个 ContentView_Previews 是我们的预览界面,如果删掉这个结构体,右边的预览将会消失
每一个遵循 View 这个协议的结构体,都必须包含有 body 这个计算属性
body 里面就是要展示的东西
例如在 ContentView 里面的 body 有一个 Text()
,在 SwiftUI 中就可以绘制出一行文字
通过 链式调用 的方法,可以给组件添加不同的属性或者样式
比如 Text().padding()
在 Text 的后面加入了一个 Padding 来调整控件前后左右的内间距
在 SwiftUI 中,body 里只能返回一个 View
如果你需要向 body 中添加多个控件 (View),你需要确定好你的排版布局模式
VStack: 垂直布局
HStack: 水平布局
ZStack: Z 轴的深度堆叠布局
假设现在我想添加一个按钮,让我的文字置于我的按钮之上,这是垂直布局,我可以使用 VStack {}
来做到这点
我们先暂时去掉 Text()
并且加入一个 VStack {}
1 | struct ContentView: View { |
现在这个 VStack
里还是空白的,现在就要来添加一个 Text
和 Button
1 | struct ContentView: View { |
通过控件在代码中的排序,可以修改他们在 VStack 中从上到下的位置
我们还可以给按钮添加一些样式,通过前面提到的链式调用
1 | Button("点击此处来输出") { |
先不需要担心尚未提到的写法,目前你只需要知道这些即可
还有一个是 HStack,同样的,把 VStack
修改成 HStack
然后来看看效果
1 | struct ContentView: View { |
同样的,通过修改代码中控件的顺序来修改他们从左到右的位置
最后一个是 ZStack
1 | struct ContentView: View { |
现在修改代码顺序则是从下到上的顺序
]]>在 Swift 中,HTTP 请求可以通过 URLSession 发起;在这篇文章中,暂时不会涉及到新的 Async Await 特性来使用 URLSession
目前,我们所有的代码都会在 Xcode 的 Playground 类型项目中进行,后面将会简单介绍使用 @escaping
关键字的 Escaping Closure 在 SwiftUI 中的用法
首先,要使用 URLSession,我们需要 import Foundation
接下来,创建 URLSession 的 DataTask 之前,我们还需要建立一个 URL 类型的数据
URL 类型将会返回 optional,即有可能不会返回一个 URL 类型的数据,可能是因为错误的 Url 所导致的。
在非函数内,可以使用 if let
来确保接收到了正确的 Url 数据
1 | if let url = URL(string: "https://api.spacexdata.com/v3/missions") { |
也可以使用 guard
来确保接收到了正确的 Url 数据
1 | guard let url = URL(string: "https://api.spacexdata.com/v3/missions") else { |
有了 Url 之后,我们就可以创建 URLSession 了;当然,URLSession 还可以使用 URLRequest 来创建,我们将会在后头提到
一般来说,我们可以使用系统提供给我们的共享的 URLSession 来节省资源。可以通过 URLSession.shared.dataTask
来调用
dataTask
中需要两个参数传递到方法中,一个是 with
可以跟着先前的 Url 数据或者一个 URLRequest 类型的数据
一个是 completionHandler
用来处理当请求完成时应该做的操作
completionHandler
是一个 Closure,在 Swift 中我们也可以这么写
1 | let task = URLSession.shared.dataTask(with: url) { data, response, error in |
需要用 in
关键字来表明以下是闭包的代码部分,而不是参数部分
这三个都是 optional 的数据
1 | if let error = error { |
通过 guard let data = data
的语法,我们坚信已经取到了返回的数据了,接下来我们可以进行 print
了
哦,别忘了,这个 task 并不会执行,直到我们调用 task.resume()
我们修改下代码,让闭包内的代码看起来像是这样
1 | if let error = error { |
然后在外部调用 task.resume()
目前整个代码看起来像是这样
1 | import Foundation |
我们来执行一下看看控制台会输出何物
1 | 9180 bytes |
控制台输出了服务器返回数据的大小
这说明 data 是拿到了,但是我们想输出的是服务器返回给我们的文本数据看看,这时需要使用 String()
来做到这点
把我们的 print
改成
1 | print(String(data: data, encoding: .utf8) ?? "No data") |
这下控制台就会输出满满当当的文本数据了
服务器返回了一个有效的 JSON 数据
Swift 是一个静态类型的语言,在服务器返回 JSON 数据后反序列化前我们需要进行数据建模,我们先来分析一下 API 返回的 JSON 结构
Root 是我们 JSON 的起始花括号 {}
,并且 Root 的数据类型是一个 Array
因此返回的 Root 一定是 {[]}
所以,我们只需要着重建模数组内的单个数据模型即可
分析完毕后,我们在同一个 Playground 文件内建立一个新的结构体 struct
我们把这个 struct 叫做 SpacexMission
1 | struct SpacexMission { |
接下来我们使用 Swift 的 JSONDecoder 来对返回的数据反序列化
我们会利用 try!
来捕捉错误,但是我们暂时不想处理错误,如果出错就让程序先崩溃掉
1 | let jsonData = try! JSONDecoder().decode(Decodable.Protocol, from: Data) |
decode 里面的首个参数是一个遵循 Decodable 协议 (其他语言中的接口 Interface) 的结构体或者类
后面跟着一个 from 参数是我们从服务器获取到的数据
我们需要在我们的 SpacexMission 结构体声明这个结构体遵循 Decodable 协议,但是为了方便,我们可以遵循 Codable 协议;这样相当于既遵循 Decodable 也遵循 Encodable
于是我们把 Struct 改成
1 | struct SpacexMission: Codable { |
好,然后我们把 JSONDecoder 需要的参数填上
1 | JSONDecoder().decode(SpacexMission.self, from: data) |
等等,现在有点怪怪的
我们的数据结构不只是一个 SpacexMission 的数据,而是很多个 SpacexMission 组成的数据
所以现在我们其实应该往 JSONDecoder 里面使用 [SpacexMission].self
才对劲
最后我们处理 JSON 反序列化的代码应该像是这样的
1 | let jsonData = try! JSONDecoder().decode([SpacexMission].self, from: data) |
让我们跑一次看看
很不幸的是,我们的控制台抛出了一个错误
1 | __lldb_expr_44/MyPlayground.playground:28: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 1", intValue: 1), CodingKeys(stringValue: "twitter", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil)) |
这看起来是一个反序列化时候出现的错误,在我们的 JSON 中 twitter 这个键有些返回的数据可能是空的
我们回到服务器返回的数据看看
果不其然,这里有些 Twitter 的数据是 null,那我们需要在 struct 中让 Swift 知道这个 Twitter 是一个可选的
把我们的 Struct 改成这样
1 | struct SpacexMission: Codable { |
然后再运行一次就不报错了
接下来我们就可以循环我们的数据了
1 | for mission in jsonData { |
控制台就会成功输出
1 | Thaicom |
大功告成!
在这里,我们会简单介绍一下 URLRequest
在 URLSession 中创建的 dataTask 默认都是 GET 方法
如果你需要执行其他的 HTTP 方法,往 Body 中加入数据,添加 Headers 等等操作,将会涉及到 URLRequest
我们将会发送一个简单的 POST 请求到 https://httpbin.org/post
稍微改造一下前面的代码成
1 | import Foundation |
HTTPBin 将会返回以下数据
1 | { |
你会发现目前数据是按照 Form (application/x-www-form-urlencoded) 的形式提交的,如果你要提交的是 JSON,你可以复写 Content-Type 的 Header
1 | request.addValue("application/json", forHTTPHeaderField: "Content-Type") |
加入这段代码再次运行,回来的结果就会是这样
1 | { |
很好,达到目的了
在 SwiftUI 中请求数据,因为操作是异步的,我们可能需要涉及到自己写一个闭包
@escaping
将会允许我们的闭包在调用的函数生命周期结束后继续运行
我们先来启动一个 SwiftUI 项目
这是我在上个月新建的一个项目
我们把上面的 SpaceX mission 的数据结构给拿过来用
添加一个新的 Group 取名叫做 Models
并且在里面新建一个新的 Swift 文件叫做 SpaceXDataProvider
我们先把上面的 SpacexMission 的数据结构拷贝过来
然后再写一个新的 Struct 叫做 SpaceXDataProvider
在这个结构体里面写一个新的方法叫做 loadMissionData
目前看起来像是这样
1 | // |
我们要在 loadMissionData()
这个函数里面加入一个参数叫做 completion
1 | func loadMissionData(completion: @escaping ([SpacexMission]) -> ()) { |
接下来先按照类似于上面的方法,创建 URLSession 的任务,并且请求数据
1 | func loadMissionData(completion: @escaping ([SpacexMission]) -> ()) { |
我们的代码将会在主线程上执行我们的闭包,因此加入 DispatchQueue.main.async
然后在里面运行 completion()
然后把我们反序列化好的数据放进去
让我们先回到 SwiftUI
我们先在 ContainView 下建立一个类型为 [SpacexMission]
的状态
1 | struct ContentView: View { |
然后改一下下面的 UI
我希望这会是一个 NavigationView
我们的 body 目前会是这样
1 | var body: some View { |
这么写,可以让 SwiftUI 自动为我们侦测上面的 spacexMissionData 数组是否为空,如果为空就显示 Loading mission data 的转圈圈页面
我们再继续完善一下
1 | var body: some View { |
我们这里用到了 SwiftUI 的一个 List 组件,这里可能会报错,说 Initializer 'init(_:id:rowContent:)' requires that 'SpacexMission' conform to 'Hashable'
那我们到定义 SpacexMission 的地方让他遵循 Hashable
协议
1 | struct SpacexMission: Codable, Hashable { |
这样报错就消失了
基本的逻辑我们已经完成了,现在,我们要怎么让 SwiftUI 在启动 App 的时候加载数据呢?
我们会通过 .onAppear 来做到
我们在 NavigationView
的下方输入 .onAppear
然后在 .onAppear 的代码作用域中调用前面我们写好的方法
现在,整个 SwiftUI 代码将会像是这样的
1 | struct ContentView: View { |
让我们来看看实现效果
非常棒,达到了我们的效果!
]]>发现很久不更新博客了,不更新的期间也发生了很多事情
是一场有惊无险的「大病」,发生在一个很平常的星期五,那是我第一次体验心律失常和恐慌发作
我不知道原因,或许也永远不知道原因了,反正当时的舌头和四肢开始麻木,我脑子里不断的闪现出新闻中各种猝死的场景,于是我的心率开始加快
直到我很难受了,我急忙去了医院,但是因为时间问题暂时没有把检查做了
那是第一次感觉到健康的身体是那么重要
后来再过了四天,又发作了,去医院把检查做了
好消息是,心脏很健康,血生化结果显示钾只有 3.3
低钾确实能够引起心律失常,我家里人都是医生,于是在家里人的建议和帮助下开始服用氯化钾缓释片来提高血钾
不过经历了这两次的心悸,心里多多少少落下了一些焦虑和恐惧;好在后来都没有发作,我的情况也在慢慢变好,心律失常和早搏的情况在减少
]]>本篇文章主要针对 Intel 的 Mac 系列,M1 部分软件可能需要 Rosetta 2 转译运行,效果可能会有部分落差
Keka 是 macOS 下一个简单并且兼容性不错的压缩和解压软件,一般情况下 Keka 会自动接管 macOS 默认所不支持的压缩文件格式
目前 Keka 支持创建如下的压缩和归档格式:
7Z
ZIP
TAR
GZIP
BZIP2
XZ
LZIP
DMG
ISO
BROTLI
ZSTD
LRZIP
WIM
并且也支持解压以下压缩和归档格式:
7Z
ZIP
ZIPX
RAR
TAR
GZIP
BZIP2
XZ
LZIP
DMG
ISO
BROTLI
ZSTD
LRZIP
LZMA
EXE
CAB
WIM
MSI
PAX
JAR
WAR
IPA
XIP
APK
APPX
XPI
CPGZ
CPIO
Keka 使用简单,没有任何学习成本
解压的时候 Keka 会自动接管 macOS 所不支持的格式
压缩的时候仅需打开软件,拖动你需要压缩或者归档的文件即可
在右上角可以修改归档格式,对于不同的归档格式有不同的选项
让 Mac 来一点安非他命…
如果你是 MacBook 系列的用户,或许在某些情况下需要电脑不休眠来完成一些任务
例如下载,传输文件等等
如果每次都需要前往设置手动设置电脑不休眠,然后每次完成之后又需要前往设置设置自动休眠,反反复复十分麻烦
安非他命就提供了一个简单的解决方案来处理这种事情
启动安非他命之后,你的 macOS 顶栏会看到一个药片的图标
按住 control + 点击药片图标即可启动安非他命,启动之后会由原来横着的图标变为竖着的
当然,你也可以直接点击药片图标,在菜单中启动并且设置更详细的东西
启动安非他命之后,你的电脑将不会自动休眠,直到退出安非他命,Mac 将会恢复自动休眠功能
Paste 是一个类似于 Windows 自带的剪贴板历史工具
但是它提供了比 Windows 自带剪贴板历史工具更多的功能
例如剪贴板记录不会因为关机而丢失,可以查看历史记录来自哪里,可以为历史记录打上 tags,可以将历史记录拖动到任何位置
并且也支持通过快捷键快速粘贴最近几次复制
Dropover 是一个十分有用的辅助程序,它可以让 macOS 出现一个给你装东西的「临时篮子」
要调出这个篮子,方法十分多样
你可以通过按住你要拖入的东西摇晃几下,或者拖动的时候按住 shift,或者将你要拖入的东西拖到顶部的 Dropover 图标
将文件放入篮子内后,你可以找到一个合适的地方拖出去
这个软件特别适合整理在不同文件夹中的文件,或者进行多个文件的发送等等
]]>通过把里面的东西拽出去达到同时发送多个文件的效果,无需找一个发一个
MacBook Pro 带有的 Touch Bar 虽然没有了普通键盘那种按键带来的熟悉感,但是它也被赋予了更多的可能性
毕竟它是一个可以显示任何东西的小屏幕,于是,为什么不能用它来摸鱼当一个时间小偷呢?
最早看到 Thief 是在 v2ex 上的一篇帖子,不得不说这个论坛上奇妙好玩的个人产品还挺多
Thief 当然就算作一个,叫做 Thief 并不是教你如何 100 天上手偷东西的秘籍,而是一个帮你摸鱼的工具
得益于 Electron (又是一个浏览器软件) 框架,Thief 可以在 macOS, Linux 和 Windows 下运行
其中只有 macOS 版本并且带有 Touch Bar 的 MacBook Pro 系列才可以使用 Touch Bar 摸鱼
这篇文章主要介绍也是使用 Touch Bar 摸鱼,毕竟在顶栏和桌面摸鱼太容易发现了,还是 Touch Bar 隐蔽一点好
Thief 还分为了普通版本和收费版本,普通版本是开源的,收费版本可自行前往官网查看如何收费 https://thief.im/
收费版本在普通版本的基础上还多了一些小游戏和人脸识别快速隐藏自己的骚功能,不过对我来说用处不大,因此只考虑免费的开源版本
安装步骤没有难度,跟安装普通软件一样,拖拽到 macOS 的 application 文件夹,启动
如果警告是不受信任的开发者,前往 macOS 的设置,隐私性和安全设置,选择仍然打开即可
默认情况下,Thief 打开后会驻扎在 macOS 的顶部菜单栏并且保持任务栏模式
其中,可供 Touch Bar 使用的模式有小说摸鱼和股票摸鱼
而其他的摸鱼模式则不是在 Touch Bar 使用的,而是通过一个小窗口达到摸鱼效果,比较显眼就不演示了
首先先进入 Thief 的设置看下,大概长这样
主要设置有两个,一个是小说路径,一个是股票代码
当你设置了小说路径或者股票代码后,点击保存就可以开始摸鱼了
设置完小说路经之后
我们可以在菜单中将模式切换到 小说摸鱼
的模式,并且显示模式从 任务栏模式
切换到 Touch Bar 模式
接下来你会看到一个实时显示 RAM 信息的窗口,如下图显示
第一次看到这个信息框我也有点不知所措,明明切换到 Touch Bar 模式摸鱼怎么让我看内存信息
别着急,其实这就是一个隐蔽的摸鱼入口
众所周知 Touch Bar 需要对应的软件显示不同的菜单或者功能,而这个 RAM 信息显示窗口,就是让 Touch Bar 显示摸鱼内容的入口
当你点击这个内存信息框的时候,你的 Touch Bar 就会显示这些东西
接下来按下 下一页
的键盘快捷键 Option⌥ + Command⌘ + .
你的小说内容就会显示出来了
股票摸鱼和小说摸鱼的使用方式差不多,在设置设置好自己的股票代码,详情请阅读官方的文档来设置股票代码
然后模式选择股票摸鱼,当你点击 RAM 信息框的时候,股票信息将会自动显示出来
股票摸鱼功能不是非常好用,我试了好几个股票代码似乎都不行
]]>虽然之前有文章吐槽过微信小程序的双标情况,但是从那时候也上手了一些小程序的开发,所以近期也接了一点私活来玩玩
这篇文章主要是想吐槽微信小程序开发过程中的两个问题,也顺便说了一下为什么会使用云函数
云函数 / FaaS (函数即服务) 是一种更加精简的服务提供模式,你只需要用鼠标开通一个语言的运行环境,上传一个代码,就可以根据不同的触发方式来运行你写的代码,可以用于补足微信小程序自身的补足
比如因为需要精确的控制代码的总量大小,很复杂的逻辑不可能在本地做
例如生成 PDF 或者干一些其他的骚活就必须要经过服务器,如果你没有服务器,FaaS 就是你需要的东西
通过微信调用云函数,云函数就可以为你生成出一个 PDF 存储到一个中心的文件存储池,然后再把地址返回来;反正用户是无法感知也没必要知道是本地还是远程生成了这个文件,反正目的是达到了
腾讯云开发的云函数其实并不是非常必要的东西,毕竟它也算是一项收费服务,只不过云开发在初期都会有一些免费额度,如果你的访问量不高,其实也还能接受
活是一个还算有保密性质的管理和查询工具,为了确保人员在外还可以通过公网方便的使用客户端来修改和查询一些东西
Humm… 管理系统用小程序暴露在微信小程序生态中我并不认为是一个妥当的想法,不过还是有办法保护的,对吧?
两个主要的问题让我选择了使用云函数
竟然不支持 HTTP PATCH Method
需要防止抓包
后端的服务设计的十分不错,GET POST PUT PATCH DELETE 各有各的职责,开发起来非常舒服
但是有个问题,微信小程序竟然不支持 PATCH 只能用 PUT
PUT 用于新建或者更新资源,别忘了,PUT 的操作是幂等性的,请参阅 w3 的 RFC 2616 HTTP/1.1 标准文档
直接引用
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
引用文中讲述了 PUT 可以用于创建和修改 (整体覆盖型),不同于 POST 方法,如果你对一个地址执行多个 POST 方法则会建立多个完全不同的结果,使用 PUT 创建资源则会建立完全相同的结果,而且是唯一的。
如果 URI 指向的地址不存在资源,PUT 可以帮你建立一个资源;但是如果 URI 指向了一个存在的资源,它会使用新的数据整体覆盖到原有资源中
举个例子,我们现在对 http://example.com/books/1 发起了 PUT 请求,并且携带了 JSON 数据如下:
1 | { |
如果这个 /books/1 的资源还未存在,那么 PUT 方法应当为你创建出 /books/1 的资源
如果你想更新数据,例如这本书的 ratings 从 80 提高到了 85,当你再次用 PUT 方法的时候携带了以下数据
1 | { |
你傻眼了,本来有书名,类型,评分和作者的数据突然变成仅有评分的数据
对,这就是 PUT 的特性;如果你尝试使用 PUT 更新原有数据,请确保携带完全你要更新的数据,你不能单单对某个字段进行独立更新
这就是 PATCH 方法诞生的原因,它定义在 RFC 5789 标准当中
如果你在更新数据的时候使用 PATCH 方法,并且携带以下数据
1 | { |
之前的数据不会被重写,仅仅 ratings 被从 80 提高到了 85
简单来说,PUT 将会覆盖原有数据进行更新,PATCH 则是局部更新
微信小程序只实现了 RFC 2616 标准中的 HTTP Methods,尚未提供 PATCH
但是!我在一个反馈中竟然找到了一些好玩的信息
参考这个问题 wx.request 缺少 PATCH 方法
下面有一个回答
各位,我发现了个彩蛋。
开发工具不支持 PATCH,但是真机调试是支持的,欧耶!!!
这就十分有趣了,明明微信开发者工具中不支持,甚至你使用 PATCH 方法还会报错,但是在手机上就支持了?
于是我也试了一下,是的,确实是直接支持了
那,电脑小程序呢?我试了一下
灵异的情况来了,它并没有按照我所想的方向走,事实上是,它并没有出错,也没有按照正常路线走
Network 选项卡中显示请求走了 PATCH 方法,但是服务端却按照 GET 方法传递回了信息,并没有更新服务端中的信息
这就很诡异了,我用本地的 NodeJS 简单的写了一个服务端,用来返回目前用户使用的 HTTP Method
果不其然,如果你在电脑小程序中使用微信小程序不兼容的 HTTP Methods (包括自定义 Methods) 都会直接被当作 GET 请求发出去
服务端接收的就是 GET 请求
看到这里我真的 JJYBB
微信小程序不知道是限制了还是做了枚举,导致开发者无法使用 Patch 方法,但是诡异的是,电脑上无论是开发者工具还是电脑小程序都无法按照正常路线走,但是手机上运行又无比正常
顺便说一下,官方从 2019 年就说后续会支持这个特性,今年是 2021 年了,所以微信的承诺基本上各位是可以忽略不计的
你要问我既然 PUT 也可以更新信息,为什么我一定要用 PATCH
前面提到,如果你要用 PUT 更新信息你必须要确保你具有完整信息,否则更新上去可能会把整个数据结构更崩
这个时候比如有两个页面,前一个页面获取到目前的数据,下一个页面才会更新信息
你如果要用 PUT,你就得把前一个页面的数据用参数的方式加到跳转页面时的路径,数据多的时候真的是噩梦,而且十分傻逼
或者在下个页面重新 GET 请求一次目前数据,然后再 PUT 请求,也很傻逼
或许你会问为什么不在同一个页面做两个页面的功能,利用 wx:if 的方法切换两个页面
这么维护真的挺操蛋的说实在的…
第二个事情就是防止抓包,抓包,不得了
有些时候就不想被抓包,不想被第三方的客户端或者脚本利用
而且后端设计的 Token 系统还特别简单,建立一个具有相应权限的 Token 就可以访问数据或者修改数据了,也不会自动吊销 Token,除非后台管理吊销掉这个 Token
所以如果被抓包被第三方利用就 JJYBB 再继续 JJYBB
不过微信小程序连 PATCH 都不支持了,想都不用想 SSL Pinning 这种骚操作也是不可能做得了了
首先先来解决第一个不支持 PATCH 的拉跨问题
云函数支持比较好的就是 JavaScript,NodeJS
我比较喜欢用 axios 来发起各种请求,所以我就在 package.json 里面加上了 axios
还好微信还保留有一点人性,云函数可以在本地调试
如果不支持在本地调试,我不知道自己又要再骂微信多少次
把所有跟网络相关的请求都交给云函数,甚至还有一个奇妙用途,就是躲过微信小程序的域名必须备案的限制
哦对这个是可以躲过去的,代价就是你需要额外付一点访问费用
但是其他的好处当然是更多了,更加好用的网络请求库,更多的逻辑判断和实现
这都是在微信小程序本地无法实现的
对然后还有防止抓包
微信小程序跟云函数的沟通是采用的腾讯自己的通讯协议,所以普通的抓包就直接对云函数无效了
哦哦说到这点
微信小程序本身解包也非常容易,如果你把请求的 TOKEN 啊,地址啊和签名之类的敏感内容写在微信小程序里,这也是很危险的
所以这些东西也可以直接放到云函数里面请求,即使被解包对方也只能看到你调用了云函数,对方无法接触到云函数的代码
这也增加了多种方面的安全性
唯一一点就是云函数毕竟是 FaaS,如果有一段时间没人访问,例如下班时间,再次访问的时候需要唤醒 API,这时候需要一定的等待时间
]]>本站的中国区特别版 https://jmencrypt.ink 已经停止解析和使用,如果有友链正在使用该域名请切换到 https://moe.jimmy0w0.me
]]>不是因为误杀,不是因为没防住病毒,不是因为拖慢系统速度,而是…
老子的硬盘弹不出来
我也很乐意体验微软的东西,输入法我也没装过第三方的,杀毒它自带 Windows Defender 我也没关过了,现在浏览器用了 Chromium 内核之后我也用 Edge 了
但是说真的,微软的东西依然小问题不断,有些功能上甚至现代看起来都非常不人性化和傻逼
就比如大名鼎鼎的: 弹出移动设备
如果你是 U 盘,喜欢直接拔出来那可能这个功能对你来说影响就不大
但是如果你是移动硬盘的用户,特别是 HDD 的,那么这个死妈功能可能会让你十分暴躁
因为你会看到一个令人窒息的警告 「该设备正在使用中。请关闭可能使用该设备的所有程序或窗口,然后重试」
倒不是说这个防止有些程序还在占用硬盘文件的弹出警告功能死妈
而是这个憨批东西压根就不打算告诉你到底是什么程序在占用
它就是要跟你玩捉迷藏,欸,就是玩儿~
对比一下 macOS 弹出系统就显得人性化了很多,它会在你硬盘文件被占用的时候告知你是什么程序在占用,退出程序之后再次重试弹出
好,那 Windows 到这里就没办法了吗?没办法找出到底是什么再占用硬盘吗
有,打开 Windows 资源监视器,在 CPU 选项栏下的 关联的句柄 搜索你的盘符,这就能找到正在占用你硬盘的程序
接下来你又会面临一个船新的问题,那就是,可能这个办法对一些流氓后台程序有效,但是对于 Windows Defender?
完全无效,因为它压根不会显示在上面
那你要怎么知道是 Windows Defender 占用了呢?
我们接着打开 Windows 的 事件查看器
Windows 事件查看器,选择自定义视图,管理事件
你应该会在最顶上看到一个事件,内容大概是 「进程 ID 为 xxxx 的应用程序 \xxxxx\MsMpEng.exe 已停止删除或弹出设备」
这个 MsMpEng.exe 就是 Windows Defender 了
那你可能说,哇,那不简单,关了 Windows Defender 就不是完事了?或者加入到排除项中
想得简单,做了之后发现,没用!
我就开始去网上寻觅解决方案,看到好几个人都在 Microsoft Community 提出了相关的问题,可惜微软一直要解决的意思都没有
答题人基本上都是说关闭 Windows Defender 服务
于是我按照各种教程,不是去组策略停用实时防护和关闭 Windows Defender 功能,就是跑到服务尝试关停这个一直占用住我硬盘的 Windows Defender 服务
关服务竟然显示我没权限
我没权限,我是你大爷我没权限
FINE
真的,这东西你要是遇到,就别费劲去考虑把它关闭
我现在就告诉你最靠谱的方案
去下载第三方杀毒软件
别不情愿,我也不情愿过,结果发现微软一点逼脸都不赏
于是我去下载了一个小红伞 (Avira) 的免费版本,安装完之后 Windows Defender 会自动解除
这个时候,嗨!硬盘弹出来了,万岁 desu
真的,Windows Defender 死妈级别真的是把你电脑当撒泼地,直接在电脑里给它母亲摆起了灵堂一般恶心
请还请不走,要停止就说你没权限
那你能不能就是说,在我弹出硬盘的时候停止扫描占用我的硬盘呢?
真的是奇怪了这种毫无人性化的系统设计
微软的工程师不需要插硬盘的是吗?
有哪位在微软工作的老哥告诉我一下如果你们的硬盘被 Windows Defender 占用你们会怎么样,冒着数据丢失的风险直接拔出?还是关机?还是去下载第三方杀毒软件直接打微软一巴掌?
说起下载第三方软件,我想起了一次微软的公开演讲,因为 Edge 加载不出 Azure 的页面,现场被迫下载 Google Chrome 可谓是名场面
我承认 macOS 的软件不如 Windows;比如这次从 macOS 换回 Windows 也是碰到了我要用的软件只有 Windows 的版本
但是 Windows 很多不人性不好用的地方也是真的恶心到爆炸了!
]]>在正式内容开始之前,我先来说一下上一篇文章提到的新博客,地址在 https://next.jimmy0w0.me 可以体验到
为什么?事情起因在好几个星期前一名友人找我帮忙存档一些容易 404 的网站快照;可能是一些新闻,或者是在国内论坛上的一些敏感发言。我最初是使用 archive.org 来建立网站快照,但是不知道为什么当时 archive.org 即使我翻了墙访问速度依然很慢,可能是当时线路不好的原因 (现在不慢了)
于是我就萌生了一个想法,既然当时访问 archive.org 的速度那么慢,然后用的还是人家的服务,那为什么不自己写一个
存档网站这种事情方法有非常多种,有的存储下网页的源代码,有的用截屏,然后我选择用 PDF 来存储一个网页的内容
首先是 PDF 可以极大程度的还原网站的样貌,同时可以直接合并成单一文件,文件大小也可以接受;另一个是,PDF 同样具有可以交互的特性,比如你可以复制里面的文字,点击里面的超链接等等;最骚的是,你还可以通过一些工具 (比如 Windows 资源管理器自带的搜索功能) 来搜索 PDF 文件里面的内容,顺便还可以具有高亮文字,注记的特性
用户在 Telegram 中使用特定的命令 + 网页地址参数,就可以在服务端创建出 PDF 文件并且存档到一个地方
大致的构想有了,接下来就是实现了
我不会很详细的说实现的部分,不过我会说一些用 Headless Chromium 时候的一些些坑
我最早写 Bot 的时候用的是一个叫做 Telegraf 的 Telegram Bot 依赖,这个东西大倒是挺大的,不能直接接收命令参数倒是挺拉跨的
当时我用的版本是 3.x 的,有一个现成的中间件,安装之后就可以接收命令参数了,后来到了 4.x 就不兼容了,报错儿
于是这次写 Bot 我就换了一个依赖,找了一下,node-telegram-bot-api 似乎不错
官方文档直接演示了如何接收命令参数
1 | // Matches "/echo [whatever]" |
match 是一个数组,按需截取即可
Bot 依赖的部分找到了,我还挺喜欢这个依赖的,接下来就是如何生成 PDF 和存储在哪里的问题了
经过之前的搜索,我还是使用了 puppeteer 这个依赖来生成网站的 PDF
puppeteer 是一个 Headless 的 Chromium;简单来说,就是没有 GUI 界面的浏览器
它可以被代码所操控,例如建立一个浏览器实例,打开一个页面,打开 URL,最后生成出 PDF
1 | const puppeteer = require("puppeteer"); |
这样就可以生成出一个名字为 content.pdf 的 PDF 文件,当然,你想保存在一个文件夹里统一管理也是没问题,自己改一下路径即可
page.pdf
那里有几个参数,路径,格式和打印背景
可以参考官方文档来自行修改,反正我就这么写了,目前比较适合用于存档网页快照
顺带一提,page.pdf
也支持使用 width
和 height
参数来调整生成大小,但是当你设置了 format
参数之后,width
和 height
参数会失效
我没有太多的精力来构建一个存储文件的服务器,我也不想买服务器养着供着,所以我会考虑使用一个靠谱的 BaaS (后端即服务)
前面说过,网页存档可能会存档一些容易 404 的内容,这也就说明了内容肯定是非常违规国内国情的
因此像是国内的腾讯云开发 Cloudbase,LeanCloud 我都直接不会考虑使用
剩下来的只有 Google 的 Firebase
好在 Google 还算大方,Firebase 的 Storage 功能免费计划就可以存 5 GB 的内容 (跟现在普通的国外网盘服务差不多,比 Google Drive 小 10 GB)
比较影响 PDF 大小的还是图片,如果单单是文字,其实一般不会达到 1 MB;图文并茂的,例如一篇微信公众号文章,大概也可以稳定在 5 MB 以下
除非是那种大型网站,非常图文并茂,少张图都没办法说明件事的时候,基本上才会达到 10 MB 左右或者更大;不过目前还没见到有存档到这么大的单个 PDF 文件
所以目前,5 GB 可以说是非常宽裕
然后有些事情需要注意,为了让用户可以直接访问到他刚刚存档的文件,通常我们需要返回文件的地址
为了构建出可以直接访问的地址,使用 Firebase Storage Bucket 的 Upload 方法的时候需要自定义文件的访问 Token
Like this
1 | await bucket.upload(path, { |
这个 fileAccessToken
你可以用 uuid 依赖下的 v4 方法来生成
最终地址应该像是这样的
1 | https://firebasestorage.googleapis.com/v0/b/{BUCKET_SPOT_NAME}.appspot.com/o/{FILE_NAME}?alt=media&token={FILE_ACCESS_TOKEN} |
然后按照这样的格式返回给用户,用户就可以直接访问到刚刚存储的 PDF 文件了
然后我还弄了一个文件访问代码的功能,大概就是通过 /get
命令 + 文件访问代码就可以获取回之前的文件
完全就是卵用不大的功能,但是用户在记录存档的文件的时候,长长的 URL 可以被这个文件访问代码取代
文件访问代码是由 7 个随机字符组成的,用的是 random-string 这个依赖
在存储的时候像是这样
1 | await db.collection("archive").doc(fileAccessCode).set({ |
这个 fileAccessCode 就是通过 random-string 生成出来的七个代码
然后 fileAccessUrl 是上面用户可以直接访问到文件的 URL
查询的时候使用
1 | const data = (await db.collection("archive").doc(fileAccessCode).get()).data(); |
就可以访问到文件链接,然后返回给用户
我还写了几个逻辑来丰富 Bot 的功能,例如成功上传 PDF 文件之后自动清理 cache 文件夹下的 PDF 文件;以及在出错的时候自动向我的 Telegram 频道汇报错误
在开发到部署的时候碰到了两个坑
懒加载资源
中文字体
在我朋友存档一篇微信公众号的文章的时候,我发现上面的图片正确显示,但是下面的图片竟然还是加载的状态
经过实验,微信公众号确实是用了懒加载
也就是,当用户的设备没有往下滑动到图片位置的时候,图片不会加载
这种设计方案是用来减少腾讯服务器的资源浪费
于是我去搜索了一下解决方案,参考这篇 GitHub Issue 目前解决了公众号图片加载问题
1 | const puppeteer = require("puppeteer"); |
目前测试下来,下面的图片都可以正确加载
首先感谢 Maverick5g 友情提供的香港 Google Cloud Platform 服务器来部署我的 Bot
GCP 服务器用的 Ubuntu 系统是 English 的,没有安装任何中文字体
因此在首次部署测试的时候,发现出来的 PDF 全部都是方块字
这种就是没有中文字体的后果
解决方案也十分简单,去 Google 搜索如何给 Ubuntu 安装中文字体即可,按照自己喜好安装;我安装的是微软的雅黑字体,因为可以直接从我的 Windows 系统中复制出来,转换成普通的 ttf 字体文件,然后丢到 Ubuntu 中安装
好,至此,我的简单的存档网页用的 Bot 就开发的差不多了
截止到这篇文章撰写完毕,这个 Bot 已经有了 22 次的 commits
有兴趣了解的,可以来 Telegram 玩玩,搜索 nothing
即可
注:因为在中国某些案件原因,本 Bot 不再向公众开放服务
]]>在上回写完了关于 GraphQL 的文章之后,我逐渐深入到了一个之前听过名字,但是一直没有怎么去了解和深入的领域
就是 Headless CMS
Headless CMS 也是一种内容管理系统,它就跟 WordPress, Drupal 等等的内容管理系统差不多
那既然是差不多,那到底差在哪
它是 Headless,即 无头
,什么意思?
没有前台,只有后端,可能也有后端和后台
Headless CMS 的好处还是挺多的,对于开发者来说,后端和前端的开发工作可以完全分来
而对于编辑者来说,可以使用独立于 Headless CMS 的管理后台,因为大多数 Headless CMS 都暴露有 Create / Update 和 Delete 功能的 API
就拿 WordPress 来稍微对比一下 Headless CMS 好了
WordPress 基于 PHP 开发,因此如果内部没有懂 PHP 的人,公司可能需要另外招揽人力,或者开发小组自行研究
但是 Headless CMS 可以自行选择前端的开发技术,无论是 Web 还是 App
而且 Headless CMS 通常也有很好用的 SaaS 服务提供,例如 GraphCMS, Contentful…
一般来说,如果采用 WordPress 提供的托管服务,除了付费以外,你无法修改自定义的主题和安装插件
但是 Headless CMS 即使用的是 SaaS 的免费服务,它也只能管理后端部分,前端还是由你自由控制的
缺点就在于 Headless CMS 通常需要一些比较懂的人参与开发,而 WordPress 就是写完就直接可以上线
哦等等!WordPress 是动态网站程序,那 Headless CMS 到底算…
其实动态静态通吃,但是为了将我们的 Headless CMS 为首的 JAMStack 发挥到极致,我们推荐采用静态网站生成器 (Static Site Generator / SSG)
你想想,WordPress 作为动态网站,并且是前后端粘合在一起的项目,虽然具有便利性,也让很多初始建站的开发者们快速搭建了网站
但是久而久之你会开始发现,你的网站有可能会经历各种各样的攻击,或者 WordPress 自身对于 SEO 并不好,还需要另外折腾
但是 SSG 就不同了,当你在 Headless CMS 修改了内容之后,通过 WebHook 让 CI 启动;SSG 可以获取 API 返回的信息并且生成出新的页面推送到托管服务和 CDN 上
静态网页有效阻止了各种爆后台和服务器的攻击,以及你可以通过强而有力的 CDN 和 DDoS 防护服务来提高网站的安全性与速度而无需担心要在 WordPress 上设置
而且 SSG 直接生成出来的网页有利于 SEO,这好处太多了
这也是我下一代博客的主要实现架构,事实上,当你们看到这篇文章的时候,我已经在使用 Gatsby + Headless CMS 的技术栈着手开发一个新的 Blog 了 XD
到时候我会更新一些心得在这里和新的博客上,敬请期待 XD
]]>GraphQL 是 Facebook 于 2015 年公布的适用于 API 的数据查询语言和环境
它是标准,有着不同社区开发的不同语言的 GraphQL 服务端和客户端实现
目前属于 Linux 基金会下的 GraphQL 基金会
发布至今,GraphQL 并不缺大佬用户们;除了 Facebook 自己在使用 GraphQL,还有 GitHub, Airbnb, Audi, Atlassian, Lyft, PayPal, Pinterest, Starbucks, The New York Times, Yelp… 甚至他们的死对头 Twitter 都在使用 GraphQL
显然,它肯定有什么十分具有魅力的地方,才能吸引到这批在各行各业都有一定地位的巨头们使用
而 GraphQL 最具有魅力的特性之一就是它可以做到数据请求,不多不少,刚刚好
你可以想象一下,普通的 API 就好像点套餐,点到手发现其实你并没有办法吃下这么多东西,这太浪费了!
但是 GraphQL 成为了自助餐,根据你目前的情况决定这食物到底是要还是不要
普通的 API,通常都是一股脑的把所有后端写好的数据传回给你
1 | { |
这是一个很典型的 JSON 数据格式,也是目前最主流的 API 返回格式之一
其中包含了角色的名字,性别,类型,职阶,Voice Actor (VA),和角色原型信息
当前端的开发者只需要到名字和性别的时候,剩下的数据会造成浪费
当然,后端也可以配合前端接受查询参数
例如 query=[name,gender]
这样 API 只返回名字和性别,倒也是一个办法
但是前端或者后端的需求改变,或者需要做出适用于不同客户端的 API,这也会直接面临着 API 的重构
GraphQL 则可以让后端开发完成后,前端根据不同需求,拿取不同数据
在官网中的介绍完美诠释了 GraphQL 最重要的特性
Apps using GraphQL are fast and stable because they control the data they get, not the server.
一个很简单的 GraphQL 查询会像这样
1 | { |
Response 是
1 | { |
GraphQL 在客户端中查询,实际上并不需要特别的库,如果你想,甚至可以用普通的 cURL 或者 JavaScript 中自带的 fetch API 或者开发者常用的 Axios 库
取决于你使用的语言采用的网络请求模块,这对客户端开发者是超级友好的
在官网中,给出了一些范例来向你展示 GraphQL 是如何通过常见的网络请求程序或者模块请求的
cURL
1 | curl -X POST \ |
{“data”:{“hello”:“Hello world!”}}
又或者 Fetch
1 | fetch('/graphql', { |
data returned: Object { hello: “Hello world!” }
我们也开放了一套 GraphQL 的 API demo 接口,有兴趣你可以通过浏览器的开发者工具,在控制台中使用 fetch API 来获取
或者使用你喜爱的 API 调试工具,例如 Postman
更甚至是前面提到的 cURL
不过调试 GraphQL,我个人更加推荐的工具是 Insomnia,它可以自动 fetch GraphQL Endpoint 的文档
不知道为什么,Postman 虽然支持 GraphQL,但是一直不支持抓取 GraphQL API 的文档
地址在: https://fate.jmencrypt.ink/api
API 托管在腾讯云函数服务中,因为函数服务会自动休眠,因此首次访问可能需要一些时间
主要的结构大概如下:
1 | characters: [ |
1 | /** |
如果你是在手机上,或者对开发者工具不熟悉,也可以点击此处前往我们开发的在线请求演示玩玩
最后,你会拿到一个展开类似于这样的数据
1 | 0: |
以上的请求方式完全无需借助第三方的请求模块,再次着重强调 GraphQL 的查询请求实际上无需发生重大改变
对于 GraphQL 的大致介绍就到这里结束了,如果这篇文章的内容引起了你对 GraphQL 的兴趣,并且想实践一下,我推荐你可以去看看 GraphQL 的官网;他们具有完整的 GraphQL 入门教程,帮助你了解 GraphQL 的类型系统,参数,变更等等更加高级的功能。如果你在使用 NodeJS 和 Express,有个好消息,官网也有 express-graphql
的教程手把手带你从普通的 Express 变到支持 GraphQL 标准的 Express 服务器
English: https://graphql.org/
中文 (简体): https://graphql.cn/
]]>我并不是一个会折腾路由器的人,只是在买来的路由器里面发现卖家帮装好的一堆软件包中带有 KMS 服务,所以我不会教你如何给你的路由器固件安装软件包
一直没有找到机会,直到前不久给另一台电脑安装了新的 Windows 10;不在公司内网,也没设置 VPN,于是我将目光移到了在我家没啥存在感的路由器上
KMS 是从 Windows Vista 时代开始具有的批量激活方式,主要提供给具有大量的 Windows 和 Office 需要部署和激活的情况下使用;例如政府机构,企业和教育机构
就 KMS 本身来说,通过 KMS 激活仅仅是激活 Windows 和 Office,你的 Windows 或者 Office 不会被交给 KMS 服务提供者管理
我知道,网上有一些文章会说使用 KMS 激活会被远程控制,这实际上是一个错误的观念
首先,没有任何证据指出通过 KMS 的方式激活后,系统会被远程接管
再者,如果大学的学生通过 KMS 激活之后可以被学校所管理,这种事情一定会被各种媒体放大,你还会不知道吗?
因此,最有可能的情况就是,你使用了来路不明的 KMS 激活软件
注意,KMS 激活软件跟使用 KMS 的激活方式是两个东西
大多数 KMS 激活软件会通过在本地启动一个 KMS 服务器,然后运行 Windows 自带的命令来激活
但是不可排除的是,有些 KMS 激活软件可能会带有私货
因此并不是 KMS 激活的方式让你电脑被远程控制,而是你用的软件很有可能就是带有私货的
为此,你可以选择自建 KMS 服务器,然后手动用 Windows 自带的命令来激活你的产品
我必须老实讲,我当时并没有对这个 KMS 服务抱有太大希望
点开配置文件发现,哟,密钥端口之类的都已经配置好了,只需要在 Windows 上执行几个自带的命令就可以准备好
激活 Windows 仅需要几行命令即可
1 | slmgr /skms 192.168.50.1:1688 |
后面的 xpr
和 dlv
是用于查看激活的到期时间和激活详细信息
一般来说 KMS 激活的失活时间是在 180 天,不过据说 Windows 会自动根据你设置的 KMS 服务器自动续期,理论上你的 KMS 一直开着的话也等同于永久激活产品
我使用的是开源工具 Office Tools
在工具内点开激活,许可证选择 Office Mondo 2016 Volume
选择 安装许可证
完成之后在 KMS 管理中输入 KMS 服务器的地址或者 IP
例如本地的: 192.168.50.1:1688
最后点击 激活
如此一来,你的 Office 也就激活好了
KMS 原本只是提供给机构和企业的批量部署方式,本文仅作为相关的测试和经验分享,不鼓励盗版使用
我们公司内部提供微软的正版全家福,个人使用可以到微软的官网 https://www.microsoft.com/en-us/store/b/windows 购买正版的 Windows
机构和企业用户请询问微软或者许可的销售机构来购买和部署批量密钥授权服务器
]]>在这篇文章开始前,我必须先提醒你,要实现 Cloudflare Workers 随机文章的功能,你必须要对外提供 API,至少需要包含完整的文章列表;相关需求可以通过 hexo-generator-json-content 或者 hexo-generator-restful 这两个插件实现
Hexo 实现随机文章这种需求,我相信大多数人应该会想到
弄一个页面,然后再用 JavaScript 重新加载到一片随机的文章
怪怪的…要加载两次,不喜欢
如果你是 Cloudflare 的用户,那是真的享福
例如你就可以免费使用 Cloudflare Workers 这种好东西
它也是一种 吾
wang 服务器的计算服务,通过编写一些简单的应用程式的代码就可以无需服务器的随意运行
注意一下免费用户每天有 10 万次请求的限额,不过作为随机文章用途我想怎么用也不可能一天 10 万吧
点开 Cloudflare Dash,随后在 Cloudflare 的 logo 右边的 Menu
按钮中选择 Workers
新建 Workers
,语言用 JavaScript 就好
接下来我会用 fetch 函数来获取这个站的文章列表
1 | return fetch("https://moe.jimmy0w0.me/content.json") |
毕竟它将会是一个 Promise,因此 then 是不可缺少的,除非你使用 async/await 的方式来处理
否则你就给我把 then 加上
1 | .then(res => { |
这些并不是什么 JavaScript 入门教学,上面的代码作用应该就不用赘述了
如果请求的 HTTP 状态码为 200,就继续 return res.json()
的内容,否则就可以抛出错误了
下一个 then
1 | .then(res => { |
好然后建立两个常量:
最后用 posts[random] 的方式实现随机指向 posts 数组中的某一篇文章,通过 Cloudflare Workers 的 Response.redirect
对其进行重定向
注意的是,这里的 statusCode 必须是 302
如果你对 HTTP 状态码有一些概念,我是指,301 和 302 的区别的话
你应该会知道 301 作为永久重定向,请求是可以被缓存的
也就是说如果你的状态码设定为 301,在你清除浏览器缓存之前,你第一次随机文章之后,第二次,第三次进入的文章全都是一模一样的
所以你必须使用 302 来确保每次进去的文章都是不一样的
到此,整个代码应该像是这样的
1 | addEventListener('fetch', event => { |
代码写完之后,保存一下,接下来我们需要把它套用在自己的域名中
不然访问 Cloudflare Workers 的域名的话…
一 点 都 不 优 雅
还是刚才 Cloudflare logo 的右边,点一下 Workers
,在 Domain 处选择自己的域名
进入到某一个域名后,就是我们平常调整 DNS,SSL/TLS 等等之类设置的地方
找到 Workers
选择 Add route
在 route 输入自己的域名和期望别人指定访问哪一个资源会被激活 Workers
例如输入: moe.jimmy0w0.me/random
以后有人访问 https://moe.jimmy0w0.me/random 之后,就会激活 Workers,自动带你来一场刺激的地球转转转旅行,然后再把你丢到我博客的随机一篇文章
下面的 Worker 选择刚才新建好的 Worker 就 vans 了
你学会了吗?学会了还不点赞?
哦…我的博客好像没有这种意义不大的东西…
]]>在上上篇文章 NetFlix 一月底影片下架直接当头一棒 中,我说 NetFlix 要下架 Fate 的 UBW 和 Zero
哈!没想到他们竟然在
最!后!一!刻!
又找了 Sony 续命
没错,我想表达的是,Fate/stay night: UBW 和 Fate Zero 在 全新的 2021 年都不会被下架了
]]>有点好奇软件开发者们是如何做出各式各样不同的 DMG 文件的
类似于这样
于是我参考了一篇来自少数派的文章,简略掉了其他不太重要的地方,保留了:
DMG 文件的建立
如何放一张图片到 DMG 中
前往 macOS 自带的磁盘工具,可以从启动台中直接搜索 「磁盘工具」 并且打开
接下来通过顶栏菜单中的 文件 =>
新建映像 =>
空白映像
名称,大小,是否加密等属性可以自行调整,接下来保存,并且打开我们新建的 DMG 文件在一个 Finder 窗口中
接下来在 Finder 的顶栏菜单的 显示 =>
查看显示选项
背景选择图片,拖入即可
]]>虽然可能考量到影片热度确实是持续衰退
但问题是我还没看完啊干!
不要以为只有香港区下架哦,台灣也是
因为我有求真的精神,于是我打开了 Quantumult
点开台湾,一键线上移民
打开 NetFlix,期待奇迹发生
也!要!被!下!架!
UBW 和 Zero 都要被下架了,不过 NF 自产的几个 Fate 番外
不!下!架!
?
啊所以说你是,让人看番外然后不让人看…
正片
?
好吧…
淦!
今天这篇文章发布的时间是 2021 年 1 月 29 日
我要在明天的星期六和星期天
疯!狂!追!番!
才能把进度赶上来?
不然我要去哪看?巴哈姆特動畫瘋?
也不是不行 XD
打开 Google 搜尋 巴哈姆特動畫瘋
噢噢噢噢哦哦哦有有有可以看還免費淦!
看番就来巴哈姆特動畫瘋
免費看爽爽
(完成年齡認證還可以看無修正色情動畫哦!現在加入還可以立即 (閉嘴)
我看到這裏都出水了啊,我是説眼角流出了感動的淚水,你們不要老是想一些澀澀的東西了啊淦!
]]>本站的公钥信息发生了更新,主要是 公钥更换 为主,以下是详细信息:
本站的个人公钥,原指纹: 0A9F 3B11 BB42 6198 FC92 8B38 1A16 84AE C00E 9E17 的公钥将修改为指纹为: EE51 9C73 A07E BF5F 5F15 1726 2642 3C5A 3B54 C8B4 的公钥
公钥内容
1 | -----BEGIN PGP PUBLIC KEY BLOCK----- |
详细信息请前往: PGP Keys 查看
]]>啊就没什么好讲的嘛,就是用 Puppeteer 和 无头浏览器生成出 PDF 文件而已
1 | const puppeteer = require("puppeteer"); |
于是你就可以得到一个美妙的 PDF (还带有超链接那种)
]]>大概在 2020 年 12 月 14 日的 7:57 分左右 (GMT+8) 发现我的 Google Chrome 被登出
登陆的时候出现了 「无法找到该账号」 的错误
我顿时惊了一下,Google 不会把我给 ban 了吧,于是急忙去 V2EX 果然看到了相关的帖子
没错,Google 出现了全球性的大范围宕机,主要应该是登陆和鉴权相关的服务发生了故障
导致用户无法登录
而因为 Google 又极度依赖账号服务,因此大多数的用户均无法访问到自己的邮件,云端硬盘,Google 文档,Google 日历,YouTube 创作者,Google Cloud Platform 等等需要登陆 Google 的服务
直到 20:29 分左右服务才有恢复的现象
]]>用了两天,终于把我的系统从 Windows 10 1903 升级到了 Windows 10 20H2 了
期间需要备份各种东西,包括一些项目和文档
安装完 20H2 之后又要把软件装回来了,现在安装了 JetBrains ToolBox 可以在后台在线安装回 JetBrains 的 IDE
以及 Microsoft Office 365,VSC,Unity 之类的都还是先装回来了
不过后续像是 Adobe CC,MATLAB 之类的东西,也都会在近期装回来
晚上装软件是挺耗时间的
20H2 不知道是不是做了一些优化,整体感受比 1903 快了不少,或许是刚安装完系统的错觉,又或许是真的经过了一些优化 Windows 快了许多
包括通知中心,开始菜单,以及登录输入密码画面时模糊的动画效果终于不太会掉帧或者卡顿了
]]>我没睡着,彻夜难眠
脑海里再次浮现出一些画面
死亡和孤独
孤独的离去,太可怕了
人总有一死,但却没想过发生的那么突然,悄无声息
这一次我是真的被深深的震撼到了,很恐惧
她永远留在了手术台上
这句话在夜里浮现出来,便迟迟挥之不去
到底是哪里让我感到害怕,或许不是因为悄无声息
而是孤独
手术室,一个与世隔绝的房间,在无影灯的照射下,命运是否已经被注定
孤独,在手术室里,没有了身边的朋友与亲人,也没有了昔日的景色,取而代之的是冷冰冰的器械,和包裹紧密的身着绿衣的白衣使者
白衣使者们的抗争似乎没有得到命运的回应,冰冷的机器画出了一道横线,发出了刺耳的蜂鸣声,无情的宣判着一个生命的离去
孤独,在手术台上,眼睛合上却再也睁不开,无法再次睁眼看到这个世界还有那么多的可能性…
愿在天堂安息
献给那位年轻的生命
]]>Jupyter Notebook 算是一个比较好用的 「交互式计算开发」 用的笔记本软件
用于学习和编写文档,并且可以直接运行笔记本中的代码无需在本地构建所需的环境,因此也在科研和教学中广泛运用
这篇文章参考了两篇文章,汇总了安装和过程中遇到的问题的解决方案,参考文章将会放在文章尾部
系统为 Ubuntu 发行版,版本号是 18.04 LTS
1 | ./+o+- ubuntu@VM-8-10-ubuntu |
如果是 Python 2 与 Python 3 两个版本都有的环境中,通过以下命令将默认的 Python 版本修改到 Python 3
1 | sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 100 |
然后通过 PIP 来安装 Jupyter,如果 Ubuntu 默认没有带有 PIP,可以用 APT 安装
1 | sudo apt install python-pip |
然后再安装
1 | pip install jupyter |
生成配置文件可以用 jupyter notebook --generate-config
,请记录好生成出配置文件的目录路径
但是如果出现了 ZSH 报错找不到 jupyter 命令,可以尝试以下解决方案
用 pip show jupyter
查找位置
大概输出类似于这样的结果
1 | ➜ ~ pip show jupyter |
顺着以上 Location
中的目录找到 bin
目录,例如我的在 /home/ubuntu/.local/bin
下存在 jupyter
将以下内容追加到 .zshrc
文件底部
1 | export PATH=/home/ubuntu/.local/bin/:$PATH |
用 source .zshrc
重新将变量加载到环境中
然后用 python
命令进入到 Python 的命令行下
1 | from notebook.auth import passwd |
接下来会提示输入密码和确认密码后,它会生成出经过了哈希函数计算的密码
复制生成的东西,接下来就进入到配置文件中
在 VIM 下,通过 / 按键进入查找模式,查找以下配置并且进行修改:
1 | c.NotebookApp.ip = '0.0.0.0' |
将接收所有 IP 的访问 - 字符串
1 | c.NotebookApp.password = '刚才生成出的密码哈希' |
访问的密码 - 字符串
1 | c.NotebookApp.open_browser = false |
将启动服务时自动通过默认浏览器打开 Jupyter - 布尔值
1 | c.NotebookApp.port = 2333 |
端口 - 整数
1 | c.NotebookApp.notebook_dir = 'notebook/' |
目录,默认访问的是用户目录 - 字符串
设定目录时,需要提前建立文件夹;例如我设定了 notebook/ 的话,需要在你的 home 目录下建立 notebook 文件夹
接下来按 Esc 并且键入 :wq 保存并且退出 VIM
最后用 jupyter notebook
应该就可以启动 Jupyter 服务了
Swift 不再单单用来开发 Apple 的应用程序,也可以开发服务端上的程序
1 | sudo apt install clang libcurl3 libpython2.7 libpython2.7-dev |
1 | wget https://swift.org/builds/swift-5.0-release/ubuntu1804/swift-5.0-RELEASE/swift-5.0-RELEASE-ubuntu18.04.tar.gz |
1 | tar xzf swift-5.0-RELEASE-ubuntu18.04.tar.gz |
1 | sudo mv swift-5.0-RELEASE-ubuntu18.04 /usr/share/swift |
1 | echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bashrc |
如果用的是 bash 只需要 ~/.bashrc 的部分,zsh 也是一个意思;如果两个都用,那就俩
1 | source ~/.bashrc |
1 | swift |
或者建立一个 Swift 文件
1 | func loveIt(_ name: String) { |
然后运行
1 | swift test.swift |
macOS Big Sur 最早在 WWDC 亮相的时候,看到的第一眼就肯定了一件事情
这绝对是 macOS 有史以来第三次大改
不过很不幸在当下并没有很习惯这次的大改,因为单单光看到全新的控制中心,小组件,新的图标…
就让我情不自禁想起了国产的各派 Linux 魔改 macOS 主题发行版
我甚至还害怕会不会有人在各种论坛下留言 「诶这不是什么什么 Linux 的 macOS 魔改主题吗」
不过在这几天的非深度体验了 Big Sur 之后,我对这种印象还是有所改观了不少
是的,UI 并没有那么糟
虽然这次全新的图标集在设计上简化了许多东西,再也没法继续体验到打开 Finder 中应用程序文件夹,然后把应用程序图标拉到最大找 Apple 那种变态级别的细节设计
不过确实也治好了我的 「Mac 审美疲劳」,配合新的提示框,提示音效,图标集,圆角菜单等等新的交互上的设计,确实让我感觉这台老 Mac 新鲜了不少
]]>可以在字符串中插入表达式
1 | let num = 3 |
1 | let list = [ |
1 | (key: "Steve Jobs", value: "Apple") |
爽
1 | let list = [ |
1 | Microsoft Bill Gates |
看起来不错
]]>在好几个月之前的 Apple WWDC 2020 中,iOS 14 新特性其中的 App Clips 应该算是一个冷门功能…
主要是大多数使用用户一般并不会太关注系统的一些新特性,特别又是跟微信小程序 「撞车」 的功能特性
不过话虽如此,好像国内还是有 Apps 适配了的,比如官方截图中的 网易严选
图源网络
很好,这并不是我想打广告,是 Apple 自己想打广告 :-)
网易严选的这个方案则是线下启用 App Clips,当客户使用自带相机扫描二维码后弹出 App Clips 入口,并且跳转到商品页
网易严选也有线上的 App Clips
App Clips 确实就是小程序,不然你还想期待什么?
不过并不是像腾讯系,阿里系或者字节系那种基于 Web 技术栈的小程序
App Clips 采用的是 Swift,也可以配合 Swift UI 或者完全自绘的 UI
它相当于一个完整 App 的精巧版,只负责一个或者一类功能,你可以用完就走,也可以作为长期使用
虽然 Apple 并不会限制你的功能,理论上你想做什么都行,只要小于 10 MB 就可以;不过 Apple 还是推荐功能尽量精简小巧,负责 App 或者用户需要的核心功能即可
例如一个商店的 App Clips,只需要让用户可以购买东西即可,并不需要给这个 App Clips 加入各种某某农场,某某置换等杂七杂八用户用不到的功能
目前官方给出了如下的 App Clips 触发方法:
这是 QR Code 的触发方式,只需要通过 iOS 自带相机扫描二维码即可
这是使用 NFC 触发的方式,只需要写入带有 App Clips 功能的 URL,就可以触发
在具有特殊 head 标签的网页中,你会看到 App Clips 横幅
向朋友发送带有 App Clips 信息的网页地址,同样会变成 App Clips 发送出去
据开发过 App Clips 的一些个人开发者说,要想把现有的 App 变成一个 App Clips 实际上很容易,可以直接使用现有的代码进行复用,只需要删减一些并不需要的功能,进行一些配置即可
App Clips 作为 Native App 的一部分,可以直接使用系统最新的 API
除了 Apple 不允许你接触一些高权限和隐私相关的 API,例如 HomeKit 或者 HealthKit 之类的,其他的功能几乎都可以像是普通的 App 一样运行
并且可以接入 Apple ID 和 Apple Pay,因此你的用户可以方便的在 App Clips 中登录和支付 (也同时兼容开发者自己的账号登陆)
此外,Apple 提供了相关的方案帮助开发者可以让用户的数据平滑的从 App Clips 转移到 App,让用户无需重新登陆,无需重新下载数据
还是网易严选的例子,App Clips 可以在 App Clips Card,加载进入 App Clips 和 开发者设定的区域显示 App Store 的引流区域
以便于引导用户下载完整版的 App 使用
目前的 iOS 14.2 的版本还是不支持 App Clips 放到桌面,可以在 App 资源库中找到
总结一下好了,App Clips 确实就是一个小程序,是完整版 App 分离出来的;也就是说,在拥有 App Clips 之前,你必须要有一个完整的 App
App Clips 的触发方式主要有扫描 QR Code,触碰 NFC,网页,地图,App Clips Code,iMessage
并且与 Native App 为相同的技术栈,支持系统原生的 API,除了高权限的以外;支持 Login With Apple 和线上的 Apple Pay
具有引流作用,并且具有从 App Clips 平滑移动数据到 App 的相关开发方案
简单来说,你看上了一款通过声音让你放松的 App,这个 App 提供了一个 App Clips,于是你不想下载,只是想试试看
试用了几天后,你觉得效果十分不错,于是你就开始想使用完整的功能,因此你会去下载 App
由于开发者可以平滑的将你的数据从 App Clips 移动到 App,当你下载完 App 后,你所存储的你喜欢的声音组合等等个人资料都会被套用到 App 中免去重新设定的烦恼
这就是 App Clips - 果味十足的小程序
]]>这是 Big sur 的新 UI 界面和一些功能
我是拿 Macbook Air 2014 中旬款来试水的,不过…
很卡!
至于主力机的 Macbook Pro 是否升级还是等大家排雷了再说吧 XD
终究我还是从小白鼠用户渐渐的到了养老等排雷的追求稳定的用户
]]>嘿吼,我们团队将会在 2020 年的 11 月 19 日在 Unity 线上技术大会见面
]]>这是一篇翻译测试用文章
Tokyo (/ˈtoʊkioʊ/ TOH-kee-oh, /-kjoʊ/ -kyoh; Japanese: 東京, Tōkyō [toːkʲoː] (About this soundlisten)), officially Tokyo Metropolis (東京都, Tōkyō-to), is the capital and most populous prefecture of Japan. Located at the head of Tokyo Bay, the prefecture forms part of the Kantō region on the central Pacific coast of Japan’s main island of Honshu. Tokyo is the political and economic center of the country, as well as the seat of the Emperor of Japan and the national government. In 2019, the prefecture had an estimated population of 13,929,280. The Greater Tokyo Area is the most populous metropolitan area in the world, with more than 37.393 million residents as of 2020.
Originally a fishing village named Edo, the city became a prominent political center in 1603, when it became the seat of the Tokugawa shogunate. By the mid-18th century, Edo was one of the most populous cities in the world, with a population numbering more than one million. Following the end of the shogunate in 1868, the imperial capital in Kyoto was moved to the city, which was renamed Tokyo (literally “eastern capital”). Tokyo was devastated by the 1923 Great Kantō earthquake, and again by Allied bombing raids during World War II. Beginning in the 1950s, the city underwent rapid reconstruction and expansion, going on to lead Japan’s post-war economic recovery. Since 1943, the Tokyo Metropolitan Government has administered the prefecture’s 23 special wards (formerly Tokyo City), various bed towns in the western area, and two outlying island chains.
Tokyo is the largest urban economy in the world by gross domestic product, and is categorized as an Alpha+ city by the Globalization and World Cities Research Network. Part of an industrial region that includes the cities of Yokohama, Kawasaki, and Chiba, Tokyo is Japan’s leading center of business and finance. In 2019, it hosted 36 of the Fortune Global 500 companies. In 2020, it ranked fourth on the Global Financial Centres Index, behind New York City, London, and Shanghai.
The city has hosted multiple international events, including the 1964 Summer Olympics and three G7 Summits (1979, 1986, and 1993); it will also host the 2020 Summer Olympics, which were postponed to 2021 due to the COVID-19 pandemic. Tokyo is an international center of research and development and is represented by several major universities, notably the University of Tokyo. Tokyo Station is the central hub for Japan’s Shinkansen bullet train system, and the city is served by an extensive network of rail and subways. Notable districts of Tokyo include Chiyoda (the site of the Imperial Palace), Shinjuku (the city’s administrative center), and Shibuya (a commercial, cultural and business hub).
The Chernobyl disaster was caused by a nuclear accident that occurred on Saturday 26 April 1986, at the No. 4 reactor in the Chernobyl Nuclear Power Plant, near the city of Pripyat in the north of the Ukrainian SSR. It is considered the worst nuclear disaster in history and was caused by one of only two nuclear energy accidents rated at seven—the maximum severity—on the International Nuclear Event Scale, the other being the 2011 Fukushima Daiichi nuclear disaster in Japan.
The accident started during a safety test on an RBMK-type nuclear reactor, which was commonly used throughout the Soviet Union. The test was a simulation of an electrical power outage to aid the development of a safety procedure for maintaining reactor cooling water circulation until the back-up electrical generators could provide power. This gap was about one minute and had been identified as a potential safety problem that could cause the nuclear reactor core to overheat. It was hoped to prove that the residual rotational energy in a turbine generator could provide enough power to cover the gap. Three such tests had been conducted since 1982, but they had failed to provide a solution. On this fourth attempt, an unexpected 10-hour delay meant that an unprepared operating shift was on duty.
During the planned decrease of reactor power in preparation for the electrical test, the power unexpectedly dropped to a near-zero level. The operators were able to only partially restore the specified test power, which put the reactor in a potentially unstable condition. This risk was not made evident in the operating instructions, so the operators proceeded with the electrical test. Upon test completion, the operators triggered a reactor shutdown, but a combination of unstable conditions and reactor design flaws caused an uncontrolled nuclear chain reaction instead.
A large amount of energy was suddenly released, vaporising superheated cooling water and rupturing the reactor core in a highly destructive steam explosion. This was immediately followed by an open-air reactor core fire that released considerable airborne radioactive contamination for about nine days that precipitated onto parts of the USSR and western Europe, especially Belarus, 16km away, where around 70% landed, before being finally contained on 4 May 1986. The fire gradually released about the same amount of contamination as the initial explosion. As a result of rising ambient radiation levels off-site, a 10-kilometre (6.2 mi) radius exclusion zone was created 36 hours after the accident. About 49,000 people were evacuated from the area, primarily from Pripyat. The exclusion zone was later increased to 30 kilometres (19 mi) radius when a further 68,000 people were evacuated from the wider area.
The reactor explosion killed two of the reactor operating staff. In the emergency response that followed, 134 station staff and firemen were hospitalized with acute radiation syndrome due to absorbing high doses of ionizing radiation. Of these 134 people, 28 died in the days to months afterward and approximately 14 suspected radiation-induced cancer deaths followed within the next 10 years.
Among the wider population, an excess of 15 childhood thyroid cancer deaths were documented as of 2011. The United Nations Scientific Committee on the Effects of Atomic Radiation (UNSCEAR) has, at multiple times, reviewed all the published research on the incident and found that at present, fewer than 100 documented deaths are likely to be attributable to increased exposure to radiation. Determining the total eventual number of exposure related deaths is uncertain based on the linear no-threshold model, a contested statistical model, which has also been used in estimates of low level radon and air pollution exposure. Model predictions with the greatest confidence values of the eventual total death toll in the decades ahead from Chernobyl releases vary, from 4,000 fatalities when solely assessing the three most contaminated former Soviet states, to about 9,000 to 16,000 fatalities when assessing the total continent of Europe.
To reduce the spread of radioactive contamination from the wreckage and protect it from weathering, the protective Chernobyl Nuclear Power Plant sarcophagus was built by December 1986. It also provided radiological protection for the crews of the undamaged reactors at the site, which continued operating. Due to the continued deterioration of the sarcophagus, it was further enclosed in 2017 by the Chernobyl New Safe Confinement, a larger enclosure that allows the removal of both the sarcophagus and the reactor debris, while containing the radioactive hazard. Nuclear clean-up is scheduled for completion in 2065. The Chernobyl disaster is considered the worst nuclear power plant accident in history, both in terms of cost and casualties. The initial emergency response, together with later decontamination of the environment, ultimately involved more than 500,000 personnel and cost an estimated 18 billion Soviet rubles—roughly US$68 billion in 2019, adjusted for inflation. The accident resulted in safety upgrades on all remaining Soviet-designed RBMK reactors, of which 10 continue to be operational as of 2019.
]]>Apple 在中国时间 2020 年 11 月 11 日的凌晨两点召开了今年第四次在线大会,也将会是 Apple 在今年最后一场大会
其中包括了一场开发者大会,和三次消费者产品发布会
本次发布会的重头戏自然就是 Apple Silicon 家族的新成员: M1
M1 是一个将 CPU,DDR 4 内存,Apple T2 保全芯片,Thunderbolt 控制器,I/O 芯片集成在一起的基于 Arm 架构的 SoC
SoC 所带来的好处就是可以减小主板所占用的空间,Apple 将这些空间安装了更多的电池
因此这次 Apple 宣称新的 MacBook Air 将会有更好的性能,同时还能更持久
至于性能方面,Geekbench 的娱乐评分跳来跳去,只能等到有人拿到实机进行体验了
Apple 对于生态圈给出的解决方案主要有三种
Universal apps 允许开发者花一天不到的时间将自己的项目重新编译出适用于 Intel 和 Arm 架构的应用程序来兼容新款和旧款 Mac
而 Rosetta 2 可以将还没有支持 Arm 架构的 Intel 架构应用程序翻译成 Arm 架构的程序来运行,避免开发者未适配导致无法正常启动软件的尴尬问题;不过多半会伴随一些性能问题
第三种就是在 WWDC 2019 推出的一个方案,在 macOS 10.15 上已经实行的 Catalyst,不知道这次项目名字是否还叫作 Catalyst;总而言之,这是让 iOS (iPhone OS & iPad OS) 开发者可以构建出适用于 macOS 版本的应用程序的一个项目,简单来说就是让 Mac 也可以直接运行 iOS 的 Apps,而且都为 Arm 架构平台
发布会还发布了三款新的 Mac ,包括了 MacBook Air, MacBook Pro 和 Mac Mini
Mac 系列其实没太多需要细讲的,官网就有参数和售价了
就从本人目前而言,虽然在用 MacBook Pro,但是偶尔依然还是需要使用 Windows 平台下的一些软件,例如 ChemOffice, Autodesk 3DS Max 之类的软件
或者需要开发一些服务端用的程序,难免都需要用到 x86 架构的 Windows 和 Linux 虚拟机
这次 Arm 的 Mac 无法运行普通的 x86 架构的 Windows 着实有点难堪
啊不过要注意一点的是,MacBook 系列仍然是会提供 Intel 芯片的,所以我依然会考虑 Pro + Intel 系列
虽然说 QEMU 是可以模拟 AMD64 的指令集架构,但是性能肯定十分堪忧,问题也会很多… (不过有团队实现了在 Arm 架构的 A12Z 芯片上通过 QEMU 在 iPad Pro 上安装 AMD64 的虚拟机)
]]>约定的梦幻岛电影版将会在 2020 年的 12 月 18 日首映
东宝在 4 周前释出了约定的梦幻岛真人电影的预告片,不过到今天 YouTube 才推荐给我,下面是视频
位于中国的用户需要使用特殊的上网姿势才可以看到下面的视频
看不了的话可以尝试点击链接进入 YouTube 观看: https://www.youtube.com/watch?v=1oiXaPeXCbo
]]>OpenPGP.JS 支持签名出于内容分离的签名
1 | const openpgp = require('openpgp'); |
这次更新由 Maverick5g 重写了所有页面的 UI
十分感谢
]]>工具人啦!
啊但是我喜欢爱蜜莉雅,你们可以说我是工具人了齁
]]>距离看完约定的梦幻岛第一季已经很久了,近期 Google 在主页上给我推送了一些有关于第二季即将推出和真人化的新闻
大致故事发生在一个孤儿院,因为一次意外的巧合让主角三人发现所谓的孤儿院实际上就是一个将他们养大然后送去当 「鬼」 的食物的农场,他们需要想尽办法带领所有人逃出这里并且瞒过这所孤儿院的管理者 「妈妈」 (Isabella)
极力推荐可以考虑入更为详细的漫画坑
不过作为时间不多的我还是靠动画补坑吧
]]>上一篇文章并未完全说明比较显著的功能更新
还有一处在于:
在设置 > 控制中心 中可以看到音乐识别
]]>装了一个 「暴雷」 插件 hexo-spoiler
点击显示 「暴雷」 内容
仆街龙粉肠龙]]>iOS 14.2 更新了
媒体中心是日常使用最容易察觉到的变化
八张随着明亮与暗色模式变化的新壁纸
一些新的 Emoji (不完全,可以去看看详细讲解 Emoji 方面的文章)
]]>1 | let a = 10 |
1 | let array = ["温迪", "空", "派蒙"] |
1 | var array = ["温迪", "空", "派蒙"] |
1 | var num, num2: Int? |
1 | var a: String? = "Apple" |
作为 JavaScript 开发者可能需要习惯一下 var
只为了变量,let
只为了常量这个设定
声明数据类型
1 | var text: String = "Hello, Apple" |
不声明
1 | var text = "Hello, Apple" |
1 | func uncle() { |
1 | func loveIt(name: String) { |
1 | func loveIt(_ name: String) { |
1 | func loveIt(name: String) -> String { |
1 | func superLoveIt(c1: String, c2: String, c3: String) -> String { |
1 | func loveIt(c1: String, c2: String, c3: String) -> (String, String, String) { |
1 | let number = 20 |
1 | let number = 30 |
1 | for i in "What the..." { |
1 | for i in "What the..." where i != "." { |
安装了 hexo-github
这个插件,效果如下
我一直以为他是女的
因为担心涉及版权问题,直接贴出了 Pixiv 链接
白裤袜,短裤子,披风,绿色小帽子
可爱得像个女孩子,但是他是男孩子啊!直戳 LZXP (还有空也直戳 LZXP XDDDD)
啊顺便乱入几张空的
]]>这次更新由 Maverick5g 重写了 UI
目前已经在加密功能中可用
]]>香吗?香!
]]>这次更新完成了 加密,加密,签名和验证的实现
]]>昨天 Maverick5g 帮忙重写了整个工具的样式,十分好看
功能部分还在重新构建
]]>之前有一个静态网页项目,也是使用 OpenPGP.JS 写的在线工具来着
今天和朋友一起重构成一个 SPA 应用
当然,主要目的还是为了学习 Vue
]]>使用 OpenPGP.JS 实现了在浏览器中加密文件并且下载
1 | const downloadBlob = (data, fileName, mimeType) => { |
上面两个用于下载二进制文件的函数是从 StackOverFlow 扒过来的
]]>今天发现大家穿汉服的穿汉服,穿 lo 的穿 lo,出 COS 的出 COS
仿佛在逛大型漫展
免——费——漫——展
太赞了
]]>没错,又安装了一个新的 Hexo Tag Plugin
这次的插件是用来显示键盘键位的: hexo-tag-kbd
效果如下
A安装了一个新的插件 hexo-auto-excerpt
可以让文章自动输出摘要到 HTML 的 HEAD 标签中,并且可以被 RESTful API 的插件抓到
]]>发现了个问题,如果直接访问的话,是很可能访问不到最新文章的
因此可以用试试 https://cdn.jsdelivr.net/gh/JimmyRice/moe-API@latest/posts.json
实在不行也可以直接用 https://purge.jsdelivr.net/gh/JimmyRice/moe-API/posts.json
清除缓存后再访问即可
]]>由于在国内直接访问 Cloudflare 代理的网站和 API 是真的很慢
因此把目光盯上了 jsDelivr…
old.jimmy0w0.me 是一个自嗨网站,主要是看无样式的网页很爽 (说是现在没样式,不过可能以后会有一些用来优化网站可读性)
里面的日志功能是与本站同步的,利用的是一个 Hexo RESTful API 的插件
但问题是,从国内访问 Cloudflare 速度确实是感人,即使是单单加载一个 JSON 也需要上几秒钟
于是我突然想起来这个东西,是因为 jsDelivr 的几大服务器节点赞助商中,QUANTIL 是在中国境内设立有合法的服务器机房和 CDN 节点的
甚至在杭州这里就有一个节点,住在这里直接有距离 Buff
Hexo 生成的 API 目录是在 public 文件夹下的 api 中,因为是私人项目,因此需要另外建立一个新的仓库来存放 API 内容
在 GitHub Actions 中当 Hexo 生成完毕之后,定位到 api 目录下进行 Git 仓库初始化,暂存和提交,以及 Push
最后使用如下方式访问可以看到所有的 API 文件:
https://cdn.jsdelivr.net/gh/JimmyRice/moe-API/
如果要获取文章列表,可以使用:
https://cdn.jsdelivr.net/gh/JimmyRice/moe-API/posts.json
除了从 moe.jimmy0w0.me/api/ 变成了 https://cdn.jsdelivr.net/gh/JimmyRice/moe-API/
其他都没什么不同,速度倒是提升非常非常多
只要有人访问过一次其中的某一个文件,就会被缓存到 CDN 中,下次访问秒加载
]]>在我的复古网页中想实现一个功能,用户点击一篇文章之后,返回文章列表不会被重新刷新
搜索了一下,可以通过 <keep-alive>
来实现
但问题就在于因为 文章列表 和 文章内容 渲染的地方都处于同一个 router-view
因此想通过命名视图来做到 文章列表 与 文章内容 分别渲染,同时通过 <keep-alive>
保留文章列表的内容
在上一篇文章 初步入门 Vue.JS 2 中有两个代码片段
在 JavaScript 中 routes
数组内两个对象中的 component
改为 components
,并且使用括号包裹
像这样
1 | const routes = [ |
接下来就可以在 HTML 中使用 named-views 了
把原来的 <router-view>
改成 <router-view name="COMPONENTS_NAME">
1 | <div id="app"> |
然后在渲染文章列表的地方用 <keep-alive>
包裹,就好惹
安装了一个 SoundCloud 插件 hexo-tag-soundcloud
最近突然有点兴趣来学习 Web 前端,自身有一点点 HTML 和一丢丢的 CSS 经验,因此上手了一款学习成本低,曲线较缓的 JavaScript 前端框架入门;如标题 —— Vue.JS
通过表严肃的 Vue.JS 和 Vue Router 的快速入门教程,大概能写点东西了
如下:
1 |
|
1 | const getFeed = { |
你可以在 https://old.jimmy0w0.me/blog/ 体验到这个程序
]]>因为需要跨源访问位于 moe.jimmy0w0.me 三级域名下的 RESTful API,因此得需要启动 CORS
根据官方文档提供的信息来看十分简单,只需要在上传的目录下建立一个 CORS 文件即可
我是通过 GitHub Actions 构建部署,需要在脚本中加入
1 | - name: Allow CORS |
接下来你就可以在 Response Headers 中看到 access-control-allow-origin: *
了
可以正常使用 fetch 来获取 API 的内容
]]>通过 https://t.me/miaowu_moe 即可订阅
]]>hexo-tag-qrcode
和 hexo-tag-hint
Parallels Desktop 对于大多数的 Mac 用户应该不太陌生,几乎是整个 Mac 上体验最好的虚拟机
不过根据官方的博客显示,Parallels Desktop 现在已经成功登录 Chrome OS 平台
当然,目前仅限于 Enterprise 的 Chromebook 设备
也就是说要使用 Parallels Desktop For Chromebook Enterprise 你除了需要有权限购进企业版本的 Parallels Desktop 以外,你的企业或者机构还必须使用 Chromebook 并且开通了相关服务
不过根据 Google 和 Parallels 官方的视频来看,目前仅仅支援 Windows 虚拟机方便 Chromebook 用户跑一些 Windows 程序
例如 Microsoft Office 系列,一些企业内部程序等等
制做一个 Windows 镜像也十分简单,企业 IT 部门人员只需要通过命令行在本地通过 MSDN 原版镜像构建出虚拟机,然后在虚拟机中的 Windows 进行简单的配置
例如进行软件安装以及其他的预配置
当完成之后,再使用命令行导出成单个镜像文件,由 IT 人员在 Chrome Admin 后台部署该镜像后,即可让特定部门或者特定人员的虚拟机自动部署
多年以来 Chrome OS 最被人诟病的地方就在于系统软件不完善和匮乏,几乎就是一个靠 Chrome 拓展程序和网页生存的系统
近年 Chrome OS 也在逐步开放,例如可以运行 Android Apps,甚至可以运行部分 Linux 程序
加上 Google 自己本身在云端服务的优势,例如 Google Workspace (之前的 G Suite) 和这次与 Parallels Desktop 的合作
多少可以让 Google 的 Chromebook 设备在企业与教育机构中跟 Apple 分大饼
]]>这次去 Apple Store 的目的只有两个
不出所料 Apple Store 的人非常多,其实平时也挺多的,毕竟在西湖,而且周围都是商圈
不过这次需要排队,还好晚上的时候排队不算久,几分钟进入 Apple Store
上下两层都全是人,iPhone 12 被摸到几乎电量见底,已经红色了
这次 iPhone 12 背面正面做的都扁平,就我个人来讲还挺喜欢这样的手机
而且网传的蓝色似乎见了真机才觉得,嘶,好像也没其他自媒体拍出来的那么丑
另一个是 Mac Pro,就是 WWDC 2019 推出的 Mac Pro;哦,当然,还有 Pro Display XDR
在系统打开 Final Cut Pro 的 8K 演示工程下,多开一个 Logic Pro 的演示项目也不见得卡顿
演示项目中包含了大量的轨道,轨道中包含了大量的 EQ,Delay,压缩器等等的效果器和插件,同时这些轨道中除了人声轨道也有大量的乐器的 MIDI 轨道
每一条 MIDI 轨道代表不同的虚拟乐器,可想而知这种场景需要比较庞大的计算能力
最后把所有轨道经过处理后的声音播放出来,丝毫没有卡顿
值得一提的是,这次 Logic Pro 的演示项目是来自 Billie Eilish 的 Ocean Eyes
不过 Mac Pro 的目标群众肯定就不是普通人了,当然你是 Rich 得流油的普通人没地方花钱或许可以考虑一下这一台四万人民币起步的 Mac Pro 抱回家里炫富
自从用了几个月的 Xr 突然摸了一下 SE2 真的有一种很爽的感觉,毕竟我的手偏小,操作 Xr 实际上是有点困难的;需要经常把手机在手里的位置移来移去
你可以把 SE2 牢牢握在手中,整个屏幕都可以够到的感觉,现在很少有手机可以体验到了
手小的用户建议 SE2 和准备推出的 iPhone 12 Mini
新的 iPad Air 也体验到了,长得跟 iPad Pro 几乎没差别,除了内在用的是 A14 Bionic,以及没有 Face ID,不过已经可以满足大多数用户了吧
拿着 iPad 追剧,偶尔画画记笔记的用户已经可以轻松满足了
顺带一提,网传那些 Apple Store 的 iPhone 12 样机柜台的线被玩得绕在了一起还真不是夸张,这次去 Apple Store 我也发现那些线还真的已经被玩的缠绕在了一起,几乎没法解开
]]>本文中带有维基百科标签,请确保你的网络可以正确加载维基百科,否则维基百科标签可能无法正常显示 XD
对的,没错,又是睡眠瘫痪,也就是俗称的 「鬼压床」
虽然这次睡眠瘫痪的幻觉感觉不像是偏向于恐怖类型的,但是还是足以让人感到不安
而且是连续性的,也就是说好不容易发动全身力量挣脱开睡眠瘫痪带来的所有幻觉和幻听,在你极度疲倦的情况下不小心再次睡着之后又进入到睡眠瘫痪的情况
你知道这是睡眠瘫痪的情况,因为还有一点清醒的意识,让你想发动力气去挣脱开。在这个过程中你会开始进入幻听,大脑昏沉;这次我幻听到房间有流水声,那种,类似于河边流水,或者洗衣服的时候洗衣机注水时的声音,而这根本是不可能的事情
后来终于在三点多,最后一次睡眠瘫痪的时候最后用尽力气脱离开,并且急忙抓起身边手机打开 YouTube 随便看了几个视频保持清醒一段时间后,调整姿势再次入眠
终于没有再进入睡眠瘫痪
]]>
通过 hexo-generator-restful
插件,可以轻松的让 Hexo 博客输出 RESTful API
/api/site.json
/api/posts.json
/api/tags.json
/api/categories.json
/api/articles/ARTICLES_NAME.json
/api/tags/TAG_NAME.json
/api/categories/CATEGORIE_NAME.json
/api/pages/PAGE_NAME.json
官方的文档虽然没有给出使用 PGP 密钥对加密普通二进制数据,但实际上这个库是可以做到的
1 | const fs = require('fs'); |
昨天开发的 RSS 推送 Bot,它是可用的
1 | const rssParser = require('rss-parser'); |
开发了一个新的 Bot 用来自动推送新的文章,测试一下
]]>其实做这个一方面是为了想入门 Telegram Bot 开发,一方面是想另外拓展一下 OpenPGP 的使用范围
Telegram Bot 是开放注册的,并且拥有许多封装好的 API 供给开发者使用
注册一个 Bot 需要向 Telegram 的 BotFather 注册
注册好后会返回给你一个 Bot 的 Token
Telegraf 是一个 Node.JS 上的 Telegram API 封装
通过 Telegraf 提供的构造函数来初始化一个 Bot
1 | const { Telegraf } = require('telegraf'); |
接下来可以试试
1 | bot.start(ctx => { |
启动后当 Bot 接收到第一次使用 Bot 时的 /start 指令就会返回给你一个 Yo
生成一次性的登录 TOKEN 给用户并且要求使用用户的私钥签名,签名后把签名好的内容返回给 Bot,Bot 使用用户公钥进行签名验证;验证通过需要满足两个条件,签名必须可以被该用户的公钥验证为合法签名,签名内容必须与一次性登录 TOKEN 相符
生成一次性登录 TOKEN,通过用户的公钥加密后发送给用户,用户使用私钥解密拿到 TOKEN,再传送给 Bot 进行登录
这么做的好处是服务器并不需要存储用户的密码和其他敏感的登录凭证信息
即使发生了脱裤,攻击者只能拿到用户的公钥
用户只需要保管好自己的私钥,其他的账户就安全无忧
Bot 会在用户 /start 后,来检测在 Firebase 的 Firestore 中的 Keys 集合中是否存在使用 Telegram ChatID 命名文档,并且文档中存储有用户的公钥
如果没有,会提示用户使用 /reg
来上传自己的公钥
上传之后会在 Firestore 中的 Keys 集合通过用户的 ID 建立一个文档来存储该公钥
当用户需要验证的时候,使用 /getcode
命令
例如使用签名登录,会随机生成一个字符串并存储到 login 集合下用用户 ID 建立的文档中,并且该随机字符串也会发送给用户
用户只需要用自己的私钥签名,然后通过 /login
的命令发送给 Bot
Bot 会向 Firestore 中 login 集合中该用户 ID 的文档索取随机字符串,并且也会向 Keys 集合中获取到用户的公钥,然后用 OpenPGP.JS 的 Verify 方法来对签名进行验证
验证后 Verify 返回的对象中会包含 Valid
的合法值 和 verifyed.data
的签名数据
接下来只需要做一个简单理解的判断即可 verifyed.data === code && valid
两个都为 True 即可判断为是成功
如果使用解密的方式登录,还是一样输入 /getcode
并且在后端存储随机字符
下一步操作就是从 Keys 集合中获取公钥,并且用 OpenPGP.JS 的 Encrypt 方法来对随机字符加密
然后把加密的内容传送给用户
验证也是同理,用户输入的跟 Firestore 保存的一致就行
当用户通过两个任意一个验证,建议将随机生成出来的 TOKEN 移除
]]>Hi,这是公钥管理事务所的第一条测试文章
每当以后 OpenPGP 的公钥因为不同的原因发生更新时 (例如: 私钥遭到外泄可能,密钥对已经到期) 都会通过这种带有特殊作者,标签与分类的文章来通知各位
]]>别指望国内的任何服务商能够为你的隐私和安全提供保障
国内其实并不存在真正的 「隐私通讯」 软件,因此在一些特殊需求下需要一些真正可以保护你隐私的即时通讯软件
Telegram 作为老牌主打 安全、隐私、匿名、开放 的软件,享有的知名度自然很高
甚至还有人在上面开发 Bot 用于人肉信息买卖的自动交易
不过近几个月来
并且可能将会协助政府针对恐怖组织进行破案工作
另外一方面,Telegram 虽然以安全主打,但是默认情况下是不会启动端到端加密,并且端到端加密仅仅在手机上可以启动,电脑版完全不见踪影
但是话说回来,如果你仅仅是想进行安全的通信,保护自己的隐私,比如吐槽一些在这里不太能吐槽的东西,那么在 Telegram 是完全安全的
主要是上面还有很多有意思的小游戏 Bots
最早接触 Keybase 是因为当时在查找一些可用的 OpenPGP 社区,于是找到了一个 Keybase,Keybase 的核心思想在于通过 PGP 来整明你就是你
不过其实我在使用的时候,Keybase 已经很少在采用 OpenPGP 的相关内容了,我大概是今年七月份左右入的,已经把 PGP 相关的功能砍的差不多了
但是其实你去看一些元老级别的用户,你会发现他们都是从一个 PGP 密钥开始信任到其他的设备和服务的
不管怎样,Keybase 还是自带有端到端加密聊天的相关功能的,并且也是开源软件,但是这个端到端加密是官方宣称,如果你聊的东西是真的要紧,我还是建议你自己再手动加密一次
万年的推荐,OpenPGP (包括 PGP, GnuPG 等互相兼容的 OpenPGP 标准软件)
之所以万年推荐,是因为它已经做好了标准定制,不会把你绑在一个软件上
只要用不爽了,另外找一个软件,然后把密钥一迁即可
而且我相信 Android 下的 OpenPGP 相关的软件会更加方便,我记得有些开发者已经做好了类似的框架可以实现消息的自动加密和解密
至于 iOS 用户比如我来说,目前我手机上已经有了 4 款不同的 OpenPGP 软件
我最早用的实际上是 Pignus,似乎来自一位德国的作者,这个 App 的完成度很高,包括可以自动检测粘贴板的 PGP 消息然后弹出提示框进行解密等等
但是很可惜的是这款 App 在使用的过程中发现加密大量的中文会导致 App 崩溃,发邮件似乎没法成功送达,作者也不更新了好几个月,不了了之了
PGPro 解决了加密大量中文崩溃的问题,但是不如 Pignus 方便
Instant PGP 主要是解决了我前两个 PGP Apps 没有独立的签名功能的问题,这款 App 带了独立签名功能
最后一个 PGP Encrypt 是最近购买的,因为可以通过键盘插件方便的加密和解密 PGP 消息
这种自由度是其他加密软件或者其他即时通讯软件无法比拟的,不过稍微麻烦点是真的…
我其实已经换了好几个密码管理器,最早用过 1Password,多平台,软件颜值高,其实是一个非常推荐的软件
不过这个软件到了后来开始采用订阅制收费,据说现在也不给本地存储你的密码库了,而且其实对于当时没有下定决心统一管理所有密码的我来说是真的一点都没有吸引力
我也用过 Keeper,试用过一段时间,但是后来也没有坚持使用,软件的颜值还行,说简单也不太简单,说复杂也没啥功能,一些高级功能是要收费的,也没有吸引力了
最后,近期我也选择采用了 KeePass + 同步盘的 「私人密码库」 解决方案
KeePass 是一个开源的密码管理软件,官方的 KeePass 不得不承认是真的挺丑,不过其实我也没有管这么多,其实虽然丑,但是免费并且功能也不少
最重要的是这个软件因为开源,因此也有很多的社区维护版本兼容 KeePass 的密码数据库文件,让你有更多互相兼容 KeePass 的软件可选
作为一个超多密码被三家公司警告已经泄露的人,最近我已经开始慢慢地将所有的常用服务密码通过 KeePass 生成一个高强度的密码,然后保存
由 坚果云 和 Google Drive 同步
其实主要是坚果云,毕竟人现在还在国内,通过坚果云提供的 WebDAV 的话,我 iOS 上的 Strongbox 可以通过 WebDAV 的方式访问到存放在坚果云上的 KeePass 的密码数据库文件
配置好之后也是挺爽歪歪的
相较于其他的密码管理软件,首先基本上你可以不用担心被攻击,另外一点你比较拥有主动权
不放心你的存储服务提供商你可以换一家,不满意你现在的 KeePass 软件你也可以换一个其他兼容 KeePass 的软件
反正一切都可以按着你的心情,挺好
]]>iOS 14发布了更新,实际上我已经在昨天更新了
目前感觉不错,但是还是有一些小毛病,比较困扰人的就是 iOS 键盘可能偶尔会发生卡顿现象
其他例如隐私方面的更新和小组件都很满意
小组件方面还算是挺智能的
例如早上的天气小组件会按照小时显示,晚上的时候按天显示
日历小组件在早上显示一整天的日程,晚上没有日程的时候显示第二天的日程以便安排第二天所需要的东西
又或者创建了一个堆叠小组件,有天气和日历
当即将到达一个日程的时候,小组件会自动从天气小组件切换到日历
还算是不错的体验,而且刚出来的一天,已经有挺多的 Apps 开始支持了
其他方面例如翻译 App,家庭 App 更新等等还需要体验
]]>这其实是一件挺气人的事情,因为我不仅经历了微信的双标,还经历了微信无理耍智障
其实在 #38 中有描述到,微信并未使用任何端到端加密技术来保护用户在聊天时的隐私与安全,然后我去查了查微信上有没有加密相关的小程序,这一查,还真有,甚至有的小程序开发者还是个人
因此我也着手开发了一个以 OpenPGP (RFC 4880) 开放标准为核心的小程序,取名为 WePGPIt!
大概长这样
其实开发一个微信小程序绝非易事,虽然你看上去微信小程序使用的技术栈仍然是 Web 前端开发者熟悉的 HTML/CSS/JavaScript
但是微信则魔改出来他们的版本: WXML/WXSS
而且并不兼容大多数 HTML 语法
其实这也还能接受,微信小程序大概也有 3 年 到 4 年 的历史了,我选择了一款还算比较热门的前端开源组件库 iView-WeApp
没想到刚用的第一天按钮样式就出现了问题,查了之后才知道原来是微信更新了第二个版本的样式,导致跟 iView 的冲突了,而且更骚的是即使你去修改 app.wxss
的全局样式都没办法把微信所做的修改覆盖掉,因为这个修改竟然要比 app.wxss
的层级还要高…
根据社区给出的解决方案,把新版小程序在 app.json
中的 style: v2
给去掉,使其样式变回 version 1
这样 iView 的 Button 样式就正常了…
基本上从这点就可以悄悄地看出微信小程序对于开发者态度是多么的强硬和不友好
而且微信官方社区经常是让开发者自行提交代码片段解决,没有像 StackOverflow 一样良好的问答氛围,甚至有一些问题你可以看到从 2 年前问到现在竟然还问不出一个结果,或者有些 BUG 在 2 - 3 年前就存在了,官方也一直没有答复和解决
微信官方的开放文档质量也是十分糟糕,一些说明或者注意事项具有过时内容或者部分文档有实例,而部分直接都写的乱七八糟你都不知道它写的是什么玩意
例如在 微信开放文档 - 指南 - 生物验证 中
目前暂时 只支持指纹识别认证。设备支持的生物认证方式可使用 wx.checkIsSupportSoterAuthentication 查询
经过测试,iPhone 的 Face ID 也可以被正常唤起和调用,说明这就是一个过时内容
不过还好,这一部分的内容实际上在 API - 生物验证
中你就可以发现猫腻,实际上文档中是给出了 面部识别
这一验证方式的,但是不知道为什么一篇文档更新了另一篇没更新
对于第三方 Node 模块的支持能力更加差,基本上如果你用的模块/框架不是为了小程序而生的话,你 require 进去使用 99% 会直接报错
更好笑的是,微信开发者工具提供了 Base64 => UTF-8 字符串 的 atob
函数,但是当你想使用 btoa
函数从字符串转 Base64 的时候,直接报错 btoa
没有声明,压根就不存在这么个东西
因此,转 Base64 这种基础操作还得需要你从 GitHub 下载一个 兼容微信小程序的 Base64 互转模块
我已经被微信小程序如此拉跨的行为给震惊到了,但是自己在朋友的多方面协助下,例如提供备案了的域名,提供免费的服务器,不做出点东西好像真的挺可惜的
因此还是顶着这屎一样的开发环境和操蛋的规定走了下去
于是,我的第一个手巧出来的小程序 WePGPIt! 就这么诞生了…
于是到了上架环节,经历了大概一天多左右时间的等待,我收到了审核结果
失败了,因为微信不允许 「加密」 相关的小程序上线
对于这样的结果我自然不服气,凭什么一堆加密相关的小程序可以上线,我的却不行?
于是我提出了驳回,第二次,还是失败了
我开始搜寻,于是搜寻到一个同样结果的小程序开发者,也是开发了一个加密相关的小程序被告知无法上线:
看着官方的回答真的是如此的…官方
我只能找客服抗议
于是趁着星期一,我寻找了客服
这一找,彻底把我找懵了
这个客服简直就像一个机器人疯狂的跳针,每个回答都像是复制粘贴
我其实不止找了一次,而是三次
三次,客服的反应都像是随意的复制粘贴,完全不把你的问题当一回事
甚至叫你: 举报那些不合规范的小程序
首先我来着是要解决问题的,你作为客服没解决我的问题甚至不负责的复制粘贴答案,疯狂跳针式回答,甚至还叫我自己去举报?
先不说你这个态度如何,我去举报能解决什么问题?
好,成,我真的举报了,我这一举报就是举报了三个小程序,现在两个星期都过了,微信一个屁消息都没有
行,我作为个人开发者我玩不过你这个操蛋公司,我从加密小程序改成签名验证小程序,成了吧?
这次就没有加密了,而是进行 签名验证: 验证这个消息是否为本人发出的
于是我开始重构整个小程序和 API
我重写了大多数的小程序页面,重写了服务端上的 Node 的 API
根据这些版本记录,你可以看到 签名验证 的小程序很顺利地上线了,而且正常上线的有三个版本
这我还是挺开心的,甚至都有了点 「好了伤疤忘了疼」 的感觉,我也就没太计较微信疯狂双标的事情
这就是新的小程序,虽然主体还叫作 WePGPIt! 但实际上真正的功能已经阉割成了 WePGP Verify It!
但是就这样吧,似乎一切都朝着好的方向发展,而且功能也增加了许多
例如自动侦测用户剪切板中是否有 PGP 签名的消息,通过只有五个字符的 「消息码」 就可以请求到一个特定的签名消息进行认证等等,因此最近我还在考虑重构服务端的 API 使其设计成 RESTful API…
但是就在最近一个版本 1.2.2 版本的更新中,意想不到的事情又发生了
1.2.2 版本比较正式版的 1.2.1 版本,仅仅是更新了一个菜单栏的颜色
这是 正式版 (已经正常上架版本) 1.2.1 的样貌
这是 1.2.2 未上线版的样貌
我竟然再次被拒绝了上架,原因是…
我看到这个东西我差点就没喷口水出来,我好奇地点了进去看看客服发现了什么…结果
详情描述涉及平台未允许的内容:加密。
截图:
请问一下这位审核人员,你哪双神奇的眼睛看到了我的小程序具有 加密 内容的
眼睛不好使吗?这叫做验证
验证和加密本质上有巨大区别的好吗?中文不过关可以考虑幼稚园重读
于是就这样我连续重新驳回了两次,总共三次审核全部都是同样的理由和同样的截图
非常过分的是,他用了三个同样的截图跟我说,你的小程序有加密功能,涉嫌违规,不给上架
但是这截图里面 压根 就完全没有涉及到加密的任何功能,而这次 1.2.2 的更新就完全只是更新了 顶栏颜色
而且根据数据显示,我的小程序压根就没有提供任何加密相关的页面,更别说加密功能了
我第一次跟他解释了加密和验证的区别,第二次我甚至亲自教他如何使用我的小程序,如何能打开验证签名成功的界面
结果两次驳回,全部采用同样的理由,同样的截图,似乎都没看见我说的话一般拒绝我的上架请求
如此的随便,如此的不专业
我的两次驳回留言字数均100字以上,而他,返回来的结果就他奶奶个腿的十二个字
十二个字!还有三张完全一样的,我小程序 主页 的截图
WOW!
微信是多么的狗啊,多么会欺负个人开发者啊
有了巨大的用户基数就开始为所欲为,想双标就双标,不爽你就不给上架,客服复制粘贴,驳回相应态度十分糟糕,证据完全不充分体现了整个流程都非常,极为不专业!
到这里,我已经停用了小程序,进入了注销小程序的环节,并且也完全删除了所有小程序的项目
完全按着自己的心情和 「自己认为」 的东西去做审核,无中生有,态度恶劣;随意抹灭个人开发者的所有劳动成果
]]>自从接触了 PGP 之后便开始变得一发不可收拾
安利给了朋友,甚至推荐公司内使用 (还真的都用了)
于是我就在想,既然基于 PGP 自动加密和解密的邮件服务有了,那即时通讯呢?
那就动手做吧
目前 WIP 到原型设计的阶段,当然 CLI 版本是不可缺的
顺便用 KeyBase 的 kbpgp 库写了一个在线加密网站
当然,目前只是个人使用
]]>微信和QQ无疑是在中国在线人数最多和规模最大的即时通讯服务
除了微信与QQ的即时通讯服务,还涉及到了游戏和支付服务
这无疑使得腾讯在中国变成即时通讯服务霸主,甚至垄断的地位
但是在你使用这些产品的时候,你有好好关注他们如何处理的数据,保护你的隐私吗?
也许你懂得的隐私仅仅是线下的隐私,例如脱衣服的时候你会关门;自慰的时候会拉上窗帘,甚至会遮住电脑的摄像头…
又或许,你知道那些互联网企业在通过收集,共享和贩卖你的个人数据给你最精准的广告投放以获得最大的利益
因为互联网的庞大,万能和便利,使得无论是新一代还是老的一代都会接触它,甚至无法脱离它
现在任何的系统都几乎跟互联网搭钩,即使你做到完全不带电子产品出门,始终还是有很多办法暴露出你的位置
你可能会刷银行卡,你可能会被带有人脸辨识的监控系统拍摄到并且记录在案…
可能你会觉得自己太渺小,即使那些大公司们收集了又会怎么样?
是,现在还没发生什么事情
但是未来呢?或许一家公司发生了数据泄露,而你正好中招,这会造成什么影响?
你的真实身份,人生阅历,消息记录甚至什么开房记录都一览无遗
这些都有可能成为你的把柄,甚至被当作笑料在互联网的某处散开
可怕的是,这些信息不单单是在一处,而是在我们使用的所有服务中
他们都存储着我们不同的隐私数据
只要一处遭殃一次,你就已经相当于裸着上大街了
前面提到,腾讯的即时通讯服务已经达到了霸主甚至垄断的地位,因此,腾讯如何保护用户在通讯时的安全十分重要
不过很遗憾,根据 国际特赦组织[1] 的报告 和 微信官方的《微信隐私保护指引》[2],微信使用的加密技术仅仅是在传输的时候进行了加密,而不是目前最好的 端到端加密 (E2EE)
这是一个我们需要插进来的话题,仅仅在传输的时候进行加密和端到端加密有什么不同
传输加密实际上是在网络中很常见的加密应用方式,目的是为了防止在传输过程中数据被篡改和窃听
篡改: 即在传输过程中将数据改成其他的东西,发送和接收者不知道
窃听: 指数据在传输过程中有第三者通过某种攻击方式获取到数据,发送和接收者不知道
但是,即使传输的管道是安全的,那么微信的服务器呢?
微信服务器的安全性自然不用多说,但问题是,微信的服务器却可以看到我们发的所有消息
没错,所有消息,为什么它可以看到,不是加密了吗?
不,加密的只是传输的管道,但是你的消息还是以明文的方式进行发送;当消息抵达微信服务器后,微信的服务器自然可以看到你所发的消息,因为消息本身是明文的
而这就是端到端加密的不同
端到端加密采用的就是 公钥-私钥 的加密和解密方式,当一个人发送消息出去的时候,消息会通过公钥加密成所有人都看不懂的格式,包括微信服务器
当消息从服务器抵达接收方后,只有接收方的私钥才可以解密消息
所以,端到端加密可以确保窃听者,服务提供商甚至政府无法知道双方到底沟通了什么东西
例如绕过加密或者设置后门获取用户的私钥
但是仅仅使用端到端加密技术无法搞定篡改,最好的方案就是两个加密一起使用: 传输加密 + 端到端加密
这样就可以确保传输的安全和内容的安全
微信并未使用端到端加密来保护用户收发消息时候的隐私,这将意味着微信实际上完全可以查看用户到底在聊什么东西
是微信不想这么做吗?或者是没能力这么做吗?我觉得并不是
放眼望去,市面上大多数的即时通讯软件,例如 FaceBook 的 Messenger 和 WhatsApp,Line 株式会社的 Line,最初只有两个人设计开发的 Telegram,Apple 的 iMessage 等等都具有端到端加密技术 (即使不是默认使用端到端技术)
腾讯并不是一个缺乏技术和人才的公司,但是致使腾讯的 QQ 和 微信到现在还没有端到端加密的原因估计还是法律与政府的关系
根据知乎问题回答 微信声称不保存聊天记录数据,你们信么 ?
提到了三条法律
《互联网信息内容管理行政执法程序规定》 第十八条 [3]
《网络产品和服务安全审查办法》 第十二条 [4]
《中华人民共和国网络安全法》 第十七条 [5]
根据这三条法律来看,服务提供商在必要时需要对网络安全审查工作给予配合
并且要上交用户发布信息、日志信息等相关材料来看,微信似乎根本就没有进行端到端加密技术的余地
因为在上交用户发布的信息前提是,微信可以明文获取到用户所发送的内容,但是如果进行了端到端技术,很显然政府和微信将具有很大的挑战来解密出用户发送的信息
这当然不是政府和微信想要的结果,因此这也可以当作微信和 QQ 到现在都没有端到端加密技术的原因之一
说是之一的主要原因是,不采用端到端加密技术可能还真的给一些企业带来了通过入侵个人隐私的可乘之机
以下内容仅为 可能,并不是代表现实情况
聊天记录是最能暴露我们各种个人隐私的地方,你想想你一天在即时通讯软件里面都聊了什么
似乎都是在瞎扯对吧?或者也不是,可能是在谈论一些正经的东西
但无论你是在瞎扯一些东西也好,还是在聊一些正经的事情也好,基本上几句话就可以暴露出一个跟你有关的信息
随便的一句
我去点个外卖,今天有点想吃披萨了,平常老是吃麦当劳真的吃腻了
最近我的股票又跌了
iPhone 11 好贵啊…
瞧,让我们看看这些消息都能说明一些什么
可能你经常吃麦当劳,但是或许你现在想换一些新口味
你有一支股票跌了
你觉得 iPhone 11 太贵了
于是这些信息都可以被分析,在你的账号上贴上标签
这不是不能做,这是完全可行的
上面的三个例子都可以用于 广告 的精准投放
例如我可以给你尝试派发一些 达美乐 或者 必胜客 的优惠券,股票跌了我可以给你投放一个 教你如何操作的课程,觉得 iPhone 11 太贵我可以给你 某电商平台的优惠广告
当服务提供商可以肆无忌惮的接触我们的消息时,这些全部都是可行的,甚至还有可能会做出一些更加出格的事情
很遗憾,在中国合法上线的服务都有这种问题,没办法,法律摆在这里,如果你要在这里合法上线那就必须遵守这种规定
但是如果条件允许,或许你和你的朋友都可以使用一些免费的开源社区或者主打安全和隐私的产品
例如:ProtonMail, Telegram, KeyBase
虽然现在关心隐私的问题,好像显得一点用都没有
确实,隐私的问题无法马上被解决,许多大公司的商业模式很大一部分利益都还是基于通过了解我们的个人隐私来进行精准广告推送来获得
但,我们还是必须要强调隐私问题,就如同严育铨[6]所说的,当所有人都意识到隐私问题的严重性的时候,通过所有人社群的力量,我们或许可以创造出无需广告的巨额营收,保护我们个人隐私的产品与模式
以下内容按照上述脚注标号排序
国际特赦组织通讯隐私排名报告,腾讯,标准2: 公司是否有默认启用端到端加密? ↩︎
《微信隐私保护指引》第 3 章: 我们将在合理的安全水平内使用各种安全保护措施以保障信息的安全。例如,我们会使用加密技术(例如,SSL)、匿名化处理等手段来保护你的个人信息。 ↩︎
第十八条:互联网信息内容管理部门进行案件调查取证时,执法人员不得少于两人,并应当出示执法证。必要时,也可以聘请专业人员进行协助。首次向案件当事人收集、调取证据的,应当告知其有申请办案人员回避的权利。向有关单位、个人收集、调取证据时,应当告知其有如实提供证据的义务。被调查对象或者有关人员应当如实回答询问并协助、配合调查,及时提供依法应当保存的互联网信息服务提供者发布的信息、用户发布的信息、日志信息等相关材料,不得阻挠、干扰案件的调查。 ↩︎
第十二条:网络产品和服务提供者应当对网络安全审查工作予以配合,并对提供材料的真实性负责。 ↩︎
第十七条: 国家实行网络安全等级保护制度。网络运营者应当按照网络安全等级保护制度的要求,履行下列安全保护义务,保障网络免受干扰、破坏或者未经授权的访问,防止网络数据泄露或者被窃取、篡改: (一)制定内部安全管理制度和操作规程,确定网络安全负责人,落实网络安全保护责任; (二)采取防范计算机病毒和网络攻击、网络入侵等危害网络安全行为的技术措施; (三)采取记录、跟踪网络运行状态,监测、记录网络安全事件的技术措施,并按照规定留存网络日志; (四)采取数据分类、重要数据备份和加密等措施; (五)法律、行政法规规定的其他义务。 ↩︎
iOS 13.6 出来了,以下是已知或者可以感知到的更新内容
Apple Car Keys 是这次更新的最大亮点,也是于 WWDC 2020 新发布的功能
目前将会支持 BMW 于 2020 年 7 月 1 日之后的新车型,其他品牌将会陆续跟进
新的健康 App 中加入了 「症状」 选项,可以添加和记录症状
当原深感摄像头检测到面部遮挡 (戴口罩) 的时候,会立即终止 Face ID 的识别过程
改成使用密码或者开发者自定的身份验证方式
]]>过几天,将会推出全新的 App Review
系列和 Feature Review
系列
主要分享那些可能小众,但是有用且实用的 Apps,程序和功能们
]]>18年的时候银联推出了一个叫做 「碰一碰」 的支付,并且跟华为达成了合作,使华为成为了第一家支持银联 「碰一碰」 支付技术的厂家
碰一碰这个东西,就是你把手机靠上去的时候,手机可以自动弹出交易软件的金额输入界面,用户只需要输入交易金额,确认支付即可;大致分为几步:
靠近 NFC 区域
手机弹出银联的云闪付 App 并且自动进入交易金额输入页面
用户输入金额确认支付
身份验证
交易成功
从很浅的原理来想,最简单的实现方式就是 URL Scheme
例如
1 | mywalletapp://userfeature/payment?storeID=1234567890x |
然后在手机里面加入 URL Scheme 白名单,这样在读取到特定 URL Scheme 的时候直接弹出某个软件
一些手机自带有各种 Pay 的,例如华为Pay,可以通过接入银联的云闪付SDK
因此 URL Scheme 白名单 + 华为Pay 外加银联的云闪付SDK后
就完成了这种贴上去,自动打开自家Pay然后输入交易金额就能支付的效果
如何获取交易金额信息关系到了用户在碰一碰之后到完成支付的连贯性程度,因此…
用户自行输入金额是比较常见也比较好做的方案,用户接触 NFC 标签之后自动打开金额输入页面,然后用户自行输入交易金额,跟自己扫码然后输入交易金额一样
可以自动获取金额
这种场景需要商家有一个商家客户端向服务端发送这次的交易信息,例如用户购买的商品,一共交易的金额
让用户在接触 NFC 标签之后,系统默认查询出 Open 的交易,获取金额和商品信息,如此一来用户便不用手动输入金额
缺点就是这么做只能一个一个交易支付,需要排队,而不能像自行扫码一样谁先完成支付谁先走
将 NFC 标签或者卡片贴在特定设备上,当新的交易发起时,自动写入金额信息;当用户读取的时候即可读取到
注: NFC 标签写入次数具有寿命
方便程度来讲,其实很难分得出来
NFC 标签触发交易 App,或许在 特定 场景下会显得方便一些,但是实际上还是跟扫码差不多的流程
安全性来讲,NFC 标签可以通过密码保护或者锁定,在一定程度上防止了篡改与复制;比二维码在不经意之间被偷换要好一些 (如果整个 NFC 标签被换掉那也没辙)
NFC 标签支付通过信息返回的不同,是否适用于多人支付还无法过早下定论
例如如果是由用户自行填充交易金额,多人同时感应 NFC 标签发起支付当然没问题
但如果是系统自动抓取交易金额,那就必须一个人一个人的来完成交易流程,这样实际上速度也并未提升
在一个商店里面迎来了大量的顾客
通过二维码支付的场景,商家完全可以多打印一些二维码贴在靠近顾客排队的地方
通过 NFC 标签支付的形式,即使商家订购了大量的 NFC 标签,也还是必须考虑很多问题
例如贴多了店面的美观,贴上去又摘下来会让背面布满灰尘而导致 NFC 标签贴纸无法重新粘贴等等…
这类 NFC 标签调用交易 App发起支付的方式是无法干掉二维码的,要干掉二维码完全说不过去
到现在并不是所有手机都具备有 NFC 功能
并且,就目前得到的信息来衡量方便程度,安全性和多人支付场景,两位实际上都不相上下…
另外一提,银联和华为合作搞的银联碰一碰支付,实际上是 2018 年的产物了,到现在似乎…还没见过真身呢…
]]>是的,戴着口罩解锁 iPhone Xr
因为 Face ID 具有学习能力,因此戴着口罩输入密码,多了之后…
它就真的可以戴着口罩识别了
虽然摘口罩识别之后,过一段时间戴口罩又不能识别了,不过…啊算是一个方法吧
]]>因为 共享电动自行车翻车 导致我的 iPhone SE 左上角弯了之后,换了一部新手机
虽然说之前在 Blog #33 中曾经透露过想换成 iPhone SE…不过 Xr 的全面屏,原深度传感器等等全新特性让我有点想真香一会
即使 Xr 用的还是 A12 的芯片,不过我还是依然选择了 iPhone Xr 作为我目前的主力机
从 SE 到 Xr 是一个大跃进,因此在上手的这几天除了 WOW 还是… wow~ ⊙o⊙
最最最直观的感受就是,屏幕整个变大了不少,无论是在看视频还是阅读消息和文章都有更加好的效果
当然,对于手小的人来说,单手操作基本上就牺牲掉了
你不得不用一个手拿手机,或者辅助点击某些按钮
再来一点就是 Face ID,这是从 iPhone X 开始的重头戏,其实当时无论是看 Apple 官方的广告还是各种 YouTuber 的评测都不会怎么认为 Face ID 的解锁速度会让人有眼前一亮,惊叹到不止的感觉…不过当自己上手之后,我发现这似乎也不是一种夸张的表现
因为,它,确实很快…
当我从裤带掏出来的时候,直接很自然的往上滑就完成了整个的解锁操作,在解锁过程中是真的非常流畅,完全不会感到识别过程,也不需要特意等它识别一下再解锁
去掉了 Home 按键我会不适应吗?倒不会;虽然说到手之后可能有一小段时间还是会习惯的把手指放在以前手机的 Home 键位置拿出来,不过因为使用上了 Face ID 和大屏幕的关系,现在已经习惯整个手机握持拿出来
在这几天的使用当中,还发现了 Xr 这代 A12 及以上芯片设备的功能,那就是 捷径 NFC
当我把手机靠近某个 NFC 标签的时候,就可以执行一个捷径
捷径,就是以前的 Workflow,后来作者被 Apple 招入,并且从 iOS 12 开始变成 iOS 的自带 App 之一
捷径可以简单理解为一个更适合手机跑的脚本语言,你可以创建一个个的捷径在以后执行的时候可以运行一系列的命令
玩法也多种多样,下载 YouTube 视频,下载抖音视频,视频 转 GIF,跑 SSH 脚本,特朗普推特生成器,公交线路查询 等等只是冰山一角
除了官方提供的这些功能,第三方 Apps 开发者们也可以适配进入捷径,让用户自行定义捷径脚本来控制自己的 App
有点跑题了,不过开放性还算是不错的捷径 App 配合上 NFC 标签触发,玩法和易用性大升级
例如我做的这个,当靠近 客厅 的 NFC 标签后,播放我的 Apple Music 个人电台,并且使用 AirPlay 让客厅的电视盒子播放音乐
花了这么长时间介绍这个功能主要还是因为这个功能目前只有 A12 芯片及以上的手机才能做到 NFC 触发
Taptic Engine!
这个东西在 SE 上可没有
Xr 终于可以用上了
Taptic Engine 是一个震动引擎,舒服就完事了
让 Taptic Engine 配合系统的动效,提示音或者铃声音乐的节奏进行强弱分明的清脆震动,真的有些时候会让人上瘾
特别是锁屏界面的手电筒按钮,当你按下后出来的震动反馈是真的很舒服
它出来的那种清脆的声音,甚至有一种在按实体按钮的感觉 (个人主观感受)
不过有什么失望的地方么
有,我买的是国行机
国行机免不了一些功能阉割
例如 FaceTime 从语音通话和视频通话阉割成了视频通话
某些地方的旗帜消失了 (Taiwan)
不过大体上并没有怎么影响到日常使用
哦对,另外最近在折腾关于 HomeKit 的智能家居
后续可能会出一些相关文章
]]>因为 共享电动自行车翻车 导致我的 iPhone SE 左上角弯了之后,换了一部新手机
虽然说之前在 Blog #33 中曾经透露过想换成 iPhone SE…不过 Xr 的全面屏,原深度传感器等等全新特性让我有点想真香一会
即使 Xr 用的还是 A12 的芯片,不过我还是依然选择了 iPhone Xr 作为我目前的主力机
从 SE 到 Xr 是一个大跃进,因此在上手的这几天除了 WOW 还是… wow~ ⊙o⊙
最最最直观的感受就是,屏幕整个变大了不少,无论是在看视频还是阅读消息和文章都有更加好的效果
当然,对于手小的人来说,单手操作基本上就牺牲掉了
你不得不用一个手拿手机,或者辅助点击某些按钮
再来一点就是 Face ID,这是从 iPhone X 开始的重头戏,其实当时无论是看 Apple 官方的广告还是各种 YouTuber 的评测都不会怎么认为 Face ID 的解锁速度会让人有眼前一亮,惊叹到不止的感觉…不过当自己上手之后,我发现这似乎也不是一种夸张的表现
因为,它,确实很快…
当我从裤带掏出来的时候,直接很自然的往上滑就完成了整个的解锁操作,在解锁过程中是真的非常流畅,完全不会感到识别过程,也不需要特意等它识别一下再解锁
去掉了 Home 按键我会不适应吗?倒不会;虽然说到手之后可能有一小段时间还是会习惯的把手指放在以前手机的 Home 键位置拿出来,不过因为使用上了 Face ID 和大屏幕的关系,现在已经习惯整个手机握持拿出来
在这几天的使用当中,还发现了 Xr 这代 A12 及以上芯片设备的功能,那就是 捷径 NFC
当我把手机靠近某个 NFC 标签的时候,就可以执行一个捷径
捷径,就是以前的 Workflow,后来作者被 Apple 招入,并且从 iOS 12 开始变成 iOS 的自带 App 之一
捷径可以简单理解为一个更适合手机跑的脚本语言,你可以创建一个个的捷径在以后执行的时候可以运行一系列的命令
玩法也多种多样,下载 YouTube 视频,下载抖音视频,视频 转 GIF,跑 SSH 脚本,特朗普推特生成器,公交线路查询 等等只是冰山一角
除了官方提供的这些功能,第三方 Apps 开发者们也可以适配进入捷径,让用户自行定义捷径脚本来控制自己的 App
有点跑题了,不过开放性还算是不错的捷径 App 配合上 NFC 标签触发,玩法和易用性大升级
例如我做的这个,当靠近 客厅 的 NFC 标签后,播放我的 Apple Music 个人电台,并且使用 AirPlay 让客厅的电视盒子播放音乐
花了这么长时间介绍这个功能主要还是因为这个功能目前只有 A12 芯片及以上的手机才能做到 NFC 触发
Taptic Engine!
这个东西在 SE 上可没有
Xr 终于可以用上了
Taptic Engine 是一个震动引擎,舒服就完事了
让 Taptic Engine 配合系统的动效,提示音或者铃声音乐的节奏进行强弱分明的清脆震动,真的有些时候会让人上瘾
特别是锁屏界面的手电筒按钮,当你按下后出来的震动反馈是真的很舒服
它出来的那种清脆的声音,甚至有一种在按实体按钮的感觉 (个人主观感受)
不过有什么失望的地方么
有,我买的是国行机
国行机免不了一些功能阉割
例如 FaceTime 从语音通话和视频通话阉割成了视频通话
某些地方的旗帜消失了 (Taiwan)
不过大体上并没有怎么影响到日常使用
哦对,另外最近在折腾关于 HomeKit 的智能家居
后续可能会出一些相关文章
]]>就挺突然的…
今天租了一个电动自行车,在经过一个比较暗的路上没注意到地上的粗树枝
当车撵上去了的时候,整个车弹了起来,整个过程很快让我有点没反应过来
然后车撞上了人行道的边边,车倒了下来,我也摔在地上
手机弯了,Siri还能呼唤出来,但是基本上也废了
左手臂和大腿有明显外伤,其他估计是淤青,脑袋还撞了一下
]]>近期在折腾有关于OpenPGP的东西,除了加密,拓展性和使用场景比较多的我认为是签名
有几个新鲜想法= ̄ω ̄=
]]>PGP 是 Philip R. Zimmermann, Jr. 在 1991 年在互联网上公开发表的一个加密程序,因为是商业程序而不能另外修改和分发。在后来的 97 年,原作者开放了一个互联网标准 OpenPGP,因此有了非常多的同类产品。因此,本文中 PGP 与采用了 OpenPGP 标准的产品简称为 PGP。
为什么使用 PGP,因为它不单单仅仅是一个软件,也是一个标准 (OpenPGP)。
这意味着它的使用场景更加广泛,它可以作为一个软件运行,也可以作为某个软件或者网站的插件,甚至在浏览器中作为Web程序运行;换句话说,你可以「PGP Anywhere」。设计它的作者 Philip Zimmermann 对于公民自由和隐私权有着强烈的执着。
它或许对初学者有一定的学习成本,但是如果你有加密沟通或者类似的需求的话,我仍然非常推荐你使用PGP。
它可以在不怎么更变你使用设备和软件的习惯下与对方建立起加密沟通,让尝试破解的人干着急;你需要做的也仅仅是将消息加密成PGP消息然后发送即可;如果你加密的是文件也没问题,PGP除了讯息,当然也提供了文件加密。
PGP相比于直接使用密码就可以解密的工具来说更安全,因为解密一个消息和文件的前提是你需要拥有正确的私钥和解锁私钥的密码,基本上,非正常破解PGP讯息或者文件的可能性只有两个:
1.获得了私钥文件并且窃听到了密码。
2.将枪指在对方头上喊道:「你最好给我解开这个讯息,否则我就爆了你的头」。
否则,或许耗尽破解人的一生,信息都无法破解出来。
PGP除了可以加密解密消息以外,还可以帮助你给讯息或者文件来签名以用于证明:哦,这个文件确实是我发出来的。当然如果你给一个高风险的讯息或者文件签名那我只能说你像个蠢蛋,因为这简直就是在明摆的跟别人说:嘿!这个敏感消息/文件就是本大爷发出来的,来抓我啊!
PGP的信任模式是基于一种分布式的信任,当A有好朋友B和C,A使用私钥签署了信任B的公钥,C想联系B的时候C可以从A拿到B已经被A信任的公钥;当C添加B的公钥进来的时候因为有A的公钥,而A签署信任了B的公钥,因此B的公钥在添加进去的时候是信任的。
PGP的设备兼容程度可以说是非常好了,应该没有第二个可以与它抗衡的加密产品;
为什么这么说,也全都因为OpenPGP这个协议的公开。有了这个协议的公开,它可以被开发者们做成库,不同系统或者软件的开发者通过引入这些库来实现PGP的功能;当然,你也可以跳过开发一个库这种思路,直接写出一个软件。
你在主流平台Windows, Mac与主流Linux发行版都可以用到GPG;而一些现在看来的冷门平台,像是黑莓,塞班,它们都是支持PGP加密的。
因为被PGP加密出来的讯息和文件还是一个普通的文字讯息或者拥有特殊格式的文件而已,因此你原先是如何操作你的聊天工具,加密后你还是如何操作;你并不需要完全熟练使用PGP软件,你只需要懂得如何加密和解密就可以方便沟通。
例如你是一个微信的用户,你并不放心微信,你担心它会进行审查;你所做的,就是将消息放进去,加密,复制加密的消息,粘贴到微信,然后发送。瞧,除了需要多加一个加密步骤,其他的和你平常用微信的步骤一样,就是这个意思。
嘿,如果你有任何使用上的困难,也欢迎随时联系我
]]>由于这次疫情限制了外出活动和物流,逼着我只能折腾现有的电子产品;而我最想做的,则是在局域网内无时无刻的可以用到电脑,或者访问到里面的文件
作为一个电脑重度使用患者,我更喜欢什么事情都在电脑上完成;即使你有一块iPad,一个手机
虽然轻办公在移动设备上已经非常成熟,甚至已经向着更加硬核的方向前进,例如剪辑,还是4K;但是这未免给人一种“不专业”的奇妙想法
虽不敢说移动设备的性能会比电脑差,至少iPad的性能甚至可以反超部分PC,但问题还是在于App是阉割的,功能远不及PC来的完整,专业
我讨厌透了在移动设备上编辑体验不佳,所需功能缺失的体验;更喜欢电脑要什么拿什么的高度生产力,因此我开始想起了要折腾远程桌面
说起远程桌面,可以使用的工具有很多:AnyDesk, TeamViewer, Google Chrome Remote Desktop…而对于Windows在局域网中的远程,我选择了自带的RDP协议
RDP可选的客户端非常多,不必被单一的远程软件所束缚,基本上每个平台都有支持RDP协议的远程客户端。除了微软自家的远程桌面,还可以选择RDM,Jump Desktop这些不错的第三方工具进行连接
RDP的优势除了对网络的要求比其他程序要低,还支持客户端在远程会话中映射本机的文件夹和打印机到远程设备,双向的粘贴板,声音传输,如果你用的是Windows自带的远程桌面连接甚至还支持复制粘贴文件
最开心的是RDP可以激活Windows的多点触控,目前实测微软官方的远程桌面(iOS和Android)以及Jump Desktop(iOS和Android)可以支持,这将意味着你可以使用触摸手势来操作Windows系统;反正目前我还没发现什么远程App可以激活Windows的多点触控功能
RDP还可以自动根据客户端窗口的分辨率自动修改Windows的分辨率,这让不管哪个设备远程上去都不会出现“大黑边”的情况;当然,当你返回到宿主机操作的时候,分辨率会自动调整回来的
当然,RDP的缺点还是有的,当RDP登录进来之后,主机就没办法操作了;主机再次登陆进去的话,RDP就会断开
不过作为自己用的远程,这种效果已经很棒了,不得不说微软这个自带的远程桌面东西和诚意还是很足的
现在Windows PC变成了主要的工作电脑放在客厅连接着以太网,其他设备接入的是5GHz的wifi,远程的效果实测不错,当然看视频还是会出现卡顿或者是音画不同步
iPad作为一个可以随身拿着走的设备,连上蓝牙键盘,连上Windows就基本上成了一个Surface。加上Windows多点触控支持,让整体操作更加自然
相比以前直接扛着一台电脑进厕所用,现在iPad+蓝牙键鼠进厕所的方案明显优雅了不止一点半点
在Mac上使用远程桌面访问效果也是挺棒的,前面说到过RDP允许客户端映射本机的文件夹到远程设备上,有些时候在编辑一些Windows平台特有软件的文件例如Autodesk的3DS Max的时候,可以在远程的时候直接访问到Mac的文件夹进行编辑
而远程的文件访问,主要还是娱乐需求居多
因为这里的特殊网络环境使得访问Netflix,HBO等网站观看正版资源都变得十分困难,加上目前pilipili糟糕的社区环境使得我完全没心情也不想在上面看番;因此就得自行动手寻找资源
基本上资源都来自人人影视和bangumi.moe,家里目前还没有NAS,全都存储在这台PC上,因此文件共享不可少
Windows的自带文件共享允许用SMB连接,实测下来躺在床上用iPad看美剧看番,或者在Mac上看效果还是不错的,没出现什么卡顿情况,除非路由器抽风
Windows开了文件共享,Mac自然也就跟着开了一个,同样是SMB连接
这样无论是Mac还是Windows,两台电脑里面的文件都是可以互通的
]]>不用说也可以发现,我的博客更新已经越来越“月经”了
从2020年的一月份到现在一个月基本上也就只能产出一两篇文章
我自己进行了一些“自我检讨”,基本上可以判断我对博客好像还真的就是三分钟热度的那种人
我其实并不怎么习惯把我的生活日志,每天遇到了什么都写出来公开在网上;只有一些自己觉得好像这个分享出来能帮助到一部分人的类型的文章才会去写
那为什么还需要自己建立一个博客呢,用Wordpress不就挺好
其实我建立博客啊……喜欢的是折腾建立和折腾功能的过程,折腾完了,你也能看到,热度就过了
让我用Wordpress的话,热度过得更快了
你要问我这么久都没有什么新折腾的东西想分享出来吗?
其实也有,也有打算要写出来分享到博客上,但是VuePress硬核的更新方式减少了我更新文章的动力
或许,我真的需要改进一下VuePress更新文章的方式了
直到现在,博客的博文列表和分类,以及给feed服务抓取的列表都是我手动编写的
想到每次更新文章都要经过如此繁琐的过程…这个动力就消了一大半
不过事实上是,其实手动更新这些文章列表也不算想象中的那么麻烦
但是就因为种种因素,让我最终还是
咕咕咕了🐦
你要问我为什么会写这篇文章吗
那就是因为三月份要过了,赶紧水一篇出来啊。。
]]>PGP 英文为 Pretty Good Privacy
是由一名叫做 Philip R. Zimmermann, Jr. 的人于1991年公开发布的一个免费的加密软件
它可以作用于加密,解密,签名和验证
后来被 NortonLifeLock (前 Symantec 赛门铁克) 公司收购
由于PGP是商业软件,因此后来1997年的时候,原作者 Philip R. Zimmermann, Jr. 同意开放一个新的互联网标准叫做 OpenPGP
于是很多同类产品就出现了,其中包括了今天我们等会要介绍的 GPG
GPG全称是 GNU Privacy Guard
是 GNU 革奴计划 中的一部分,遵循OpenPGP标准,兼容PGP;目前预装在了大量的Linux发行版中
今天我们进行演示的设备有 macOS, Windows, Ubuntu Linux, Android 和 iOS
通过了解 PGP (包括OpenPGP标准) 的加密和签名流程,可以帮助可能一脸懵逼的你更快的了解如何使用这类产品
初次使用这类产品,我们需要建立一个 密钥对 ,密钥对会分为: 公钥 与 私钥
公钥用于加密和验证签名, 可以进行公开 ;私钥则进行解密和签名, 需要自行保管
我们会通过三个例子来说明加密流程
一个公司要向外部公布一些讯息/文件,不过为了可以验证该讯息/文件确实是来自公司而不是其他人,公司可以通过私钥签名出一个讯息/文件
当你想去验证这个讯息/文件是否真的来自这个公司的时候,只需要将这个公司提供的公钥加入自己的密钥库中,然后开始验证
验证成功则代表这个讯息/文件的确来自该公司,如果出现错误,则表明讯息/文件不来自这个公司
友人A要给友人B传输一个加密的讯息/文件
友人A会通过友人B的公钥加密出一个PGP讯息/文件
公钥加密过后的信息, 只有私钥持有人才可以解锁
也就是说,友人A使用友人B公钥加密过后的讯息/文件,只有友人B才可以解锁
当然,友人A也可以选择多个人的公钥来进行加密,这样,被选择的人也可以解锁
董事成员A要给董事会发送一个讯息/文件,现在,这个讯息/文件不但要经过加密,还想确保这个讯息/文件就是来自董事成员A,而不是来自其他人
因此,在加密的时候除了要选择使用董事会的公钥进行加密,还需要另外启用签名选项
签名这个操作是使用董事成员A自己的私钥进行签名,接下来,加密出来的讯息/文件就会带有董事成员A的签名信息
因为董事会已经预先存储好了董事成员A的公钥 ,因此,在董事会解密讯息/文件的时候,会看到这个讯息/文件带有董事成员A的数字签名的相关信息
如果在解密的时候没有看到类似的信息,或者提示未知签名者或者签名无效,说明董事会并未存储董事成员A的公钥,又或者,这不是董事成员A发来的讯息/文件
在 macOS 下常用的 GPG 工具集叫做 GPG Tools,包含了 GPG 的本体和一些周围软件,例如图形化的密钥管理工具,给 macOS Mail App 使用的加密和签名插件等等
请前往 GPG Tools 官网 下载安装档案
下载完毕之后打开 GPG Keychain
然后点击左上角 New 按钮
填写一些信息,例如名字,邮件地址和私钥密码
接下来点击 Generate Key
之后软件可能会询问你是否将你的公钥上传到公钥服务器,对于新手或者不知道这个是什么的人可以暂时选择否定答案
然后你会在 GPG Keychain 软件中看到你刚才新建的密钥对
对着你刚新建的密钥右键选择 Export… 可以将你的公钥导出,私钥导出同样选择 Export…,但是请勾选上 Include secret key in exported file;这样就可以导出私钥
Windows下使用的工具为 Gpg4win
安装完成后打开 Kleopatra
点击 文件 下的 新建密钥对
选择 创建个人OpenPGP密钥对
输入 姓名
和 电子邮件
下一步开始建立密钥对,在建立的时候会有弹窗让你设定 私钥密码
创建成功
在 Kleopatra
中右键密钥,选择导出可以导出公钥,选择导出绝密密钥可以导出私钥
事实上,命令行的操作适用于任何成功安装了 GPG 的操作系统
Ubuntu中默认带有GPG,因此建立密钥可以使用以下命令开始
1 | gpg --gen-key |
请根据提示依次输入: 姓名和电子邮件地址
然后弹出以下信息:
1 | gpg (GnuPG) 2.2.4; Copyright (C) 2017 Free Software Foundation, Inc. |
输入 N 更改名字,输入 E 更改邮件地址,输入 O 进入下一步,输入 Q 则退出
接下来我们输入 O 进入下一步
下一步需要设定私钥密码,用户自行设定
设定密码之后会有这种提示
好的,我们开始瞎敲键盘吧,整个过程会持续大概十几秒左右
接下来密钥就成功建立了
接下来通过以下命令分别导出 公钥 和 私钥
1 | gpg --output publickey --armor --export 邮箱/用户ID |
1 | gpg --output seckey --armor --export-secret-key 邮箱/用户ID |
导出私钥时需要输入密码,如果是在CLI界面下进行操作,需要在前面加上sudo
以在CLI界面下可以输入密码,否则将无法导出
在iOS下使用的工具为 Tessercube
下载后点击 我
点击 右上角 加号
点击 创建密钥
成功创建后即可在联系人中看到刚才建立的密钥
公钥和密钥可以很方便的导出
前往 我 选项卡
长按或者通过3D Touch刚才建立出来的密钥,会弹出菜单
选择导出密钥
这个时候会进行生物特征识别
使用 Touch ID 或者 Face ID 验证后,就可以一起导出公钥和私钥
打开OpenKeyChain,第一次打开你应该会在启动页面中看到 创建密钥
如果你不是第一次打开,通过主页右上角的 菜单按钮 进入 管理我的密钥 页面
然后选择 创建密钥
填写跟上面方法差不多的信息后 (例如名字, 邮件地址, 私钥密码设置) 即可建立成功
目前不清楚OpenKeyChain是否可以成功导出私钥至其他软件使用
公钥私钥的使用非常简单
现在有很多支持PGP的程序
公钥可以分享给别人,别人可以使用你的公钥来加密出一个消息
私钥可以用来解锁别人使用你公钥加密的消息
上述程序 GPG, Tessercube, OpenKeyChain 都可以用于加密和解密
]]>最近用腾讯云开了个服务器跟朋友愉♂悦玩耍
不过因为不打算长期开服,也不是每天都会很长时间泡在上面,因此没有打算直接买断一个月或者一年
我们用的是腾讯云的竞价实例,因为这样高配机子非常便宜,开服是真的有爽到
因为我们都是正版玩家,并且不怎么玩MOD服,所以采用的是官方的开服方式
Ubuntu安装一个OpenJDK
1 | sudo apt install openjdk-8-jre-headless |
然后去下载官方提供的开服jar
在: https://www.minecraft.net/zh-hans/download/server/ 可以下载到
注意的是官方的开服程序会随着Minecraft的客户端版本更新,比如你去下载的是1.15.2的Server,那么你就必须要用1.15.2版本的Minecraft登录
下载下来的文件名叫做 server.jar
用
1 | java -Xmx1024M -Xms1024M -jar server.jar nogui |
就可以启动
内存参数自己调整一下即可
]]>今天入正了Minecraft
是几年前玩的游戏了,拉回了一个朋友回来玩,大家也都一起入正了
找了个服务器hmsj.online
有兴趣的可以一起来玩
]]>计算机相信大家肯定都不会陌生,在我们的生活当中,无论是社交、娱乐、金融、交易;处处都可见计算机设备的影子。计算机可以说是我们当今世界和社会中的命脉,如果我们突然关闭全世界所有的计算机,那电网系统会被关闭、汽车会相撞、飞机会从天上坠落、金融市场会停摆各种社会系统会下线。即使那些看上去跟计算机没什么关系的东西,例如我们的衣服、鞋子等等都是计算机控制工厂流水线批量制造的产物。那这些运算速度比我们人脑快,随处可见的设备是如何工作的呢?
我们先来谈谈计算机的历史。在早期,我们还未进入到“电子计算机”的时代,但是我们人类对计算的需求早已有之。早期,Computer这个词实际上是一种职业的名称,这个词实际上是指专门负责计算工作的人。这个职位名在19世纪晚期才消失。这个时候,这个词开始代表着计算的机器。这种机器由德国的一位叫做戈特佛里德·莱布尼茨所发明,当时人们叫做乘法机,这种机器有点类似于汽车上的里程表。这个机器有一连串可以转动的齿轮,在每个齿轮上面有十个齿,代表着0-9的数字。当一个齿轮读数超过9的时候就会自动的跳回0,并且让临近齿轮的读数加上1。这个机器因为精密的设计让它可以具有完成乘法和除法的能力。
这种设计非常成功,在接下来的200-300年中,几乎所有的计算器都采用了这种解决方案。但是这种设计也带来了一个巨大的缺点,所有计算的操作方式都非常的繁琐,并且都要经过多次计算,而且这些人工制造的机器成本非常之高,绝大多数的人都是无法负担得起的。
后来又有一个叫做查尔斯·巴贝奇的人发明了一个叫做差分机的装置,这种装置可以计算更加复杂并且可以求取多项式近似值的这么一个机器,因为多项式的关系,这台机器可以描述几个变量之间的关系,当然了,也可以解决棘手的对数函数以及三角函数。当然了,到了后期,查尔斯·巴贝奇萌生了一个更加厉害的想法,他把这个新想出来的机器叫做“分析机”。这玩意更加厉害,甚至不同于前面所有的计算装置。这个装置,在当年可是没人听说过的,因为它是一种通用型计算机。
这种计算机有非常非常多的用途,已经不单单是某种特定的计算了。你可以给它数据,给它顺序进行操作,这个机器还带有内存以及一个内置的打印装置。这个想法太超前了,但是在当时这种“自动计算机”的概念是一个非常非常大的发现,为后来的电子计算机技术垫下了基础,也预示着“计算机程序”这个概念的诞生。这台机器,很成功的启迪了第一代的计算机科学家们,他们将巴贝奇的研究成果用于自己的机器中。所以为什么巴贝奇被称为计算机之父。
在美国一次人口普查中,因为普查时间消耗过长,普查局向一位叫做赫尔曼·霍尔瑞斯的人进行求助,他制造了一种制表机。他的机器是用电的,也就是所谓的“机电试”,虽然使用传统的机械系统来记录数字但是这个机器却结合了电力驱动的组件来辅助运行。而这个机器最大的特点在于“打孔卡”就是带有一种定点网格的卡并使用打孔表示数据。机器的针脚会穿过这些孔,如果某个点被打了孔,针脚穿过卡片上的孔后就会导电,接通电路。这样,就能实现统计。
这些机器为后来的数字型计算机慢慢拉开了序幕。
到了近代电子计算机,虽然从能占一整个房间巨大的机器变成可以携带在包里甚至拿在手上的装置,但是工作原理还是受到了几乎之前计算机的影响。如可编程,导电接通电路等等。电子计算机最重要的一个零件就是晶体管。我们都知道计算机是一个进行数字运算的机器,如果要实现在键盘上按一按就可以得出结果,当然不可能使用完全实体的机械进行操作。这时候在计算机内部就会将这些原本需要在实体机械上进行的操作,变成了电压。大部分的计算机都采用布尔数学体系或者布尔变量。
布尔变量只会反馈两个结果那就是“是或者不是”。
这里的是与不是是指这个是否是正确的逻辑条件并且利用1“是”与0“不是”来表示。那么问题来了,如何控制电压输出1“是”与0“不是”呢?我们使用高低电压来进行控制;标准为正5v和0v,而程序,则通过逻辑门运行。
通过逻辑门输出1和0来判断这个值是否符合逻辑描述。这些电路一般处理三种逻辑运算机制,他们分别是“与、和”“或者”与“不是” 又可以直接称为“与”“或”“非”逻辑运算机制。
通过这些运算机制就可以让计算机做简单的运算工作了,例如加法和减法。计算机在运行程序的时候往往需要一种非常可靠的并且精准的算法来控制电流以达到程序能正确运行。
在早期的计算机当中所采用的设备是真空管,就是在一个真空的玻璃里面放两个电极,用阴极电压上升温度来释放电子,如果在阳极处有一个正电位(被通电),那么电子就会被吸引过去;这样电路就联通了。后来的三极管也是同样的通电方式,只不过中间会有一个栅极,可以理解成可控的大网精准的控制着电子是否通过。但是真空管问题实在太多,而科技的进步使得我们的计算机慢慢的使用上了晶体管。
晶体管采用了半导体去代替电极,并且构建了由“电极”“基级”和“发射极”组成的复杂的电子沟通网络来精准控制电压并且正常输出1和0。
接下来就是内存(RAM),内存类似人类的记忆中枢,它负责存储很多很多的东西。存储在里面的东西我们都用单位“比特”来表示,比特也就是所谓的二进制数字。就是前面提到过的1和0。数不尽的1和0组成了我们现在日常使用的各种各样的程序和文件。
内存用来记录这些东西,那么处理自然是CPU来处理这些1和0的二进制数据了。但是内存就像是人类面对应急事件处理一样,仅仅是短期记忆。例如你敲下键盘的任意一个按键的时候,CPU会向其中一个位置来搜索这些数据,并传送到CPU进行处理。
这个过程花费的时间我们称之为延时。因为这些操作需要被反复并且快速的调用读取,所以内存又有一个名字叫做随机存储器(RAM)。但是这些数据,不仅随机而且还是暂时的,当计算机关机的时候,你想留着的数据就必须要转移到一个长期的存储设备当中,所以唯读记忆体(ROM)的概念就出来了。目前市面上的ROM设备主要有硬盘(HDD)、固态存储(固态硬盘和U盘都是同样的工作原理)光存储的DVD,他们都负责存储你的数据。
这样,通过电压、内存、中央处理器和许多概念相同,但负责的工作却不同的硬件设备,譬如图形处理器(GPU或者显卡)DSP(数字信号处理)声卡等等设备构成了我们今天利用在各个行业各个场景的计算机设备。
]]>截止至2020年1月10日,我的Blog已经成功开站了一年
在之前,我的博客实际上都属于随时搬家的状态
在我早期的博客是我和我一个朋友共用的,使用WordPress搭建,不过我那个朋友似乎不怎么写文章,平常的文章撰写和折腾WordPress也都是我弄的比较多
后来他的服务器搬家了,我也就没跟着他一起用一个服务器和网站了,于是乎空了一段时间;那段时间我也没有想过再去弄一个博客网站,后来到了2018年的9月14日,我在GitHub上fork了一个用Jekyll作为框架的博客项目,重新开了一个博客网站
由于是Jekyll,内建了GitHub Page的支持,因此每次更新文章或者页面之后由GitHub自己生成和部署也挺方便,也就用这个项目用了一段时间,这一段时间也就是两个月
因为我在逛Typecho主题的时候看上了一款主题,也就是Handsome
你要知道Typecho实际上是一个很轻量化的博客系统,而Handsome在Typecho的基础上弄上了一些亮眼的附加功能,看上去整体的UI还算不错,动效对于个人来说还挺好看
于是花了些钱买下了这个主题,并且在2018年11月30日在旧博客更新了最后一篇文章:博客迁移公告
我将Typecho部署到了腾讯云,并且在腾讯云购买了一个域名,当然,国内的VPS尿性就是域名需要备案
有一小段时间还是可以成功解析的,后来就直接被强制重定向到未备案的警告页面了
于是在我去买了Vultr,并且把Typecho部署到了Vultr上
后来当时我另一个朋友Shwinta买了滴滴云,看他们家的确挺实惠的,不知道为何我就去买了个滴滴云,可能就是单纯的想折腾吧
某一天在搜索关于Vue相关的视频的时候,突然看到Evan在发布一个VuePress
VuePress的自带主题是非常简洁的,所以我跑到VuePress的官网体验了一下,觉得又是一个可以折腾的东西
于是我就新建了一个项目,看着文档,按照自己的意思来配置,一个新的博客大概就出来了;于是我将它部署到了滴滴云上
过了几天想想,嗯,看上去效果不错…看着Handsome实际上也有点审美疲劳了,于是我就有了把这个项目直接代替掉Typecho的想法
买了一个新的域名,jimmy0w0.me
一个远古版本就出来了
这就是Blog站刚开始的版本
从滴滴云到Vultr,替换掉了原来的Typecho
由于项目在本地,生成之后部署到VPS,因此对VPS的选择是非常非常自由的,甚至都用不到VPS
之后就开始各种搬家,Vultr到Netlify,又回到Vultr,然后搬到Virmach,再去找新的托管服务
于是最终,选择了Surge最为最理想的托管服务商
经过了一年,脑子中对这个Blog站点的各种骚操作基本上都实现了
其实我自己对这个站有一种莫名的惊讶…我当时用的是VuePress 0.x的版本,不支持插件和主题…竟然能坚持了一年,并且在上面做了这么多的骚操作…总有一种把VuePress玩偏了的感觉,毕竟我一直认为VuePress是一个文档类网站的构建程序
目前还是打算一直用VuePress作为主要博客的构建程序,除非发现了一个更好的,更爽的XD
]]>2FA 的全称是 Multi-factor authentication,译作 多因素验证
也就是用户需要使用两种以上的验证方式来证明你是你
例如假设你需要访问一个资源,你除了需要输入账号和密码,你还需要另外提供你的指纹
你的指纹就是第三种认证方式,证明你就是你
这次使用的环境为
Ubuntu 18.04.2 LTS
使用用户密钥登录&禁用密码登录
给Linux多加一个2FA,无疑是为了安全
为了启用2FA,我们需要安装一个Google Authenticator (libpam-google-authenticator)
Ubuntu/Debian 用户使用APT安装: sudo apt install libpam-google-authenticator
接下来通过命令 google-authenticator
建立一个密钥
接下来按y和回车即可
你会看到一个二维码,如果你的终端字体设置太大可能无法显示二维码的其他细节,需要手动调节一下字体大小即可
请使用2FA App来进行二维码扫描,我目前使用的是iOS平台的Step Two
就像这样
下面出现的所有问题建议全部都疯狂按y和回车
接下来我们需要将它配置到SSH
使用编辑器,例如VIM或者nano来编辑/etc/ssh/sshd_config
文件
记住非root用户要使用sudo权限来执行
在sshd_config文件中找到:
ChallengeResponseAuthentication
UsePAM
这两个地方
然后改成yes
接下来运行命令来重新启动ssh
sudo systemctl restart ssh
接下来再去编辑另一个文件: /etc/pam.d/sshd
加上这行 auth required pam_google_authenticator.so
保存,重启ssh
重新登陆时应该会看到除了用户名和密码,还需要刚才的2FA验证
如果你是使用密钥登录,还需要做一些编辑
前往编辑 /etc/ssh/sshd_config
加上 AuthenticationMethods publickey,keyboard-interactive
接下来重启ssh
sudo systemctl restart ssh
然后再编辑 /etc/pam.d/sshd
这个文件
通过 # 注释掉 @include common-auth
如果需要重新配置验证码,再次执行google-authenticator
命令即可,将新的二维码扫描进入2FA验证程序后就好了
如果需要取消掉2FA验证,请按照上面步骤反着来
可以注释掉教程中编辑的东西,重启ssh服务
喵呜moe用的是Hexo作为博客框架,因此找到一个舒服的编辑和部署方式十分重要
因为是部署到适用于静态网页的托管服务,如果想部署Hexo后台之类的东西,就基本上告别静态网页的托管服务了 (主要是不想花钱)
因此,有很多人都开发出了适用于Hexo的各种客户端
例如:
我选择了Hexo Client
而我的Hexo项目是存储在另一台电脑上
但是在这些客户端选择Hexo项目路径的时候,不意外的都不支持远程路径,只能使用本地路径
所以在我安装了Hexo Client打开一次后就没怎么打开这个客户端了
在企业内,我们的文档同步一直用的坚果云
不过我其实并没有怎么了解过坚果云,因为我一直坚持离线存储,因为可以完全免去上传下载的时间,即插即用,传输速度快
最近因为想远程同步并且编辑一些文档,我也注册了一个个人的坚果云账号
突然就在昨天,我突然想起来坚果云的客户端是支持Linux平台的,并且最骚的在于坚果云支持排除特定文件/文件夹的同步
这正好都是我的需要,因为Hexo项目内的一些文件夹 (例如 node_module 和 theme ) 之类无关文章的文件夹可以不同步以节省上传下载流量和同步速度
反正部署和推送是在另一台电脑上进行,因此这些文件夹都并不需要在这台电脑上
根据坚果云的文档设定了排除文件夹同步之后,就可以很方便的右键启动同步了
我Windows的坚果云文件夹检测到有新的同步项之后会弹窗问我是否同步到这台电脑,当我点下是的时候,一切都变了
我再也不需要开一个Terminal在nano或者VIM中,或者在Xftp中用VSC打开Markdown文档来编辑了
终于可以用上专属于Hexo的编辑器了
还没有仔细的使用这款编辑器,或者说是Hexo的客户端
不过大致查看了一下,标签,分类做了单独的编辑区域,很赞
并且可以筛选不同分类和标签
基于Electron,所以自带F11全屏,可以当作沉浸写作模式
UI方面基于饿了么的Element
编辑器支持预览,方便写作
而且因为是开源的,我也Fork了一个自己的版本
因为我的部署方式不同于大多数人的项目,大家都是Push到自己的GitHub仓库后通过CI自动部署,或者直接本机生成部署
我却用电脑控制另一台电脑在它的本地生成部署
因此我可以修改这一部分的逻辑
Nice!
]]>今天家附近的麦当劳的网络挂了
导致所有人没法在店内的自助终端和手机的App上点餐,只能用现金或者转账给店长
让我想起了上一次超市断网的时候所有人都得排上几小时的队伍,只能用现金
以后还是身边备点现金吧…
]]>昨天睡觉的时候遇上了睡眠瘫痪,也就是大家说的鬼压床
之前几次睡眠瘫痪都是幻听,感觉有人扯被子…
而且不敢开眼睛,因为直觉一开眼睛就会看到那种很恐怖很恐怖的东西┌(。Д。)┐
这次遇上的有点不一样,有一种下坠感,看到白色光,幻听女人的声音
太难受了,不知道大家有没有过睡眠瘫痪之类的经历…
]]>非常感谢各位在2019年的陪伴和支持
那我们2020年再见吧
]]>给本站加了一个Live 2D的角色
live2d-widget-model-tsumiki
舒服了
]]>这篇文章主要介绍一下Ubuntu如何安装MySQL 8.0
如果你直接使用
1 | sudo apt install mysql-server |
的话,那么结果就是你会安装一个MySQL 5.7左右的版本
如果你想安装MySQL 8,那就需要去整一个官方的APT源回来
首先我们先去到这个网站
https://dev.mysql.com/downloads/repo/apt/
点Download
直接点击下面那个No thanks, just start my download.
当然如果你和我一样,用的是一个Terminal连上去的话,在你的宿主机上右键那个No thanks, just start my download.
复制它的目标地址,到Linux上用wget
或者curl
获取deb包即可
下载好之后就是安装deb包
sudo dpkg -i xxxx.deb
然后你会看到一个叫做MySQL-apt-config的页面
直接往下按OK (注意版本选择是MySQL 8再选OK)
跑一次sudo apt update
或者sudo apt-get update
看个人习惯
然后你就可以愉快的安装MySQL 8了
sudo apt install mysql-server
然后会出现熟悉的密码设置页面
需要注意的是这次MySQL 8引入了一个新的密码验证机制,部分的客户端可能没法登陆上,所以在安装的时候它会问你使用新的密码验证机制还是旧的,如果你的客户端支持了,可以选择新的,如果还没支持上,那就老老实实旧的吧
至此,MySQL 8已经安装完成,如果想远程连接,自行Google MySQL 8如何开放远程连接即可
]]>可能是我火星了,最近才知道了Google手气不错的正确用法
如果是经常使用Google的用户,都很容易发现Google有一个手气不错的按钮
如果你什么都不输入直接点下手气不错,Google会带你到Google涂鸦
我其实很久一度认为Google的这个手气不错实际上是Google涂鸦
事实证明,我错了…/(ㄒoㄒ)/~~
这个Google的手气不错功能实际上就是可以直接带你到你搜索的这个关键词的最热门页面
例如当你搜索日本然后点下手气不错后,Google会直接带你抵达日本的维基百科页面
又或者当你搜索Japan Gov的时候,点下手气不错,Google会直接带你抵达日本政府的官网
好骚的一个功能…
]]>好啦,又来水一篇文章
之前的Minecraft服务器是构建在虚拟机里面的,性能真的是可怜的令人发指
所以这回打算再重新弄一个
MacBook Air目前的配置是 i5-4260U @ 1.40GHz x4
+ 4GB RAM
+ 120GB SSD
好,所以后续我打算作死耍一下在本地重新建一个Minecraft服务器在上面
正文会在我的Blog和这里更新
喵呜,就这样!~o( =∩ω∩= )m
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
1 | $ hexo new "My New Post" |
More info: Writing
1 | $ hexo server |
More info: Server
1 | $ hexo generate |
More info: Generating
1 | $ hexo deploy |
More info: Deployment
]]>无聊之间看到了一篇逼乎的回答
这个回答比较有意思,至少我看下来…
有一种花粉被人戳G点的感觉…
既然这个回答里面觉得云电脑这么好,我就来谈谈我的观点吧
对我来说自然不算黑科技,这也就是云计算的另一种用法而已
云电脑在某些地方的确可以做到很多手机上不方便的事情,例如:
手机版本的App并不好用
还有吗?
…对不起想不出来了
的确,我觉得大家吹的出去只用带个手机,键盘,Hub和鼠标,可能还要加一个便携式显示器确实有点过了;为什么我要带这么多东西我却不考虑带一个本子出去?
当然,云电脑的确可以体验到拥有一台配置强劲的服务器的感觉,但是客户端体验一定好吗?
不一定…
客户端体验依然还是要看用户的环境,例如设备环境,App优化,解码,网络条件等等…
哦,黑科技可能只是华为在App上做的稍微好点而已
用用户的地理位置来连上最近的数据中心,用API接通本地的存储映射到远端的服务器上…
嗯!这或许…有点黑科技的感觉
不,在我看来目前云电脑无法实现“别了电脑”
首先专业用户是无法被满足的,特别是经常跟媒体和娱乐这类打交道的专业用户
一些人可能会说配置高不就好了?是,配置高跑的起软件,但是你除了配置高,你能外接设备吗?
配置高不用外接设备这个观点很显然是错误的
首先,2019年的今天的确有软件为了维护版权利益,例如Cubase,还在使用硬件加密的设备来保护自己的软件知识产权,云电脑自然无法做到这点,除非他愿意为每个用户都买下软件的版权提供给专业人员使用
第二,熟悉大型项目级别的后期自然不单单是十几GB或者几百GB的素材就完事了的,大多数片场都要实地搭建数据中心来存放每天拍摄素材,如果云电脑不在数据中心的内网,被迫使用VPN或者其他方式传输的话,效率会大打折扣
第三,你要如何实现打印呢?你虽然可以在接通一下手机的各种API来实现无线打印,那么,有线打印机呢? (毕竟国内大多数企业还是能用就行.jpg)
很多时候图传回来的画面不一定是最好的,云电脑的方案在质量和速度上多多少少都做了妥协,假设你在做调色的时候发现因为网络原因或者软件或者是编码的原因颜色不对劲,这种感觉绝对不是很好的
我认识的很多开发者都习惯自己在本地搭建虚拟机而不用云服务器,然而云电脑很难做到这点,虽然理论上是可以在上面使用VM没错
这部分主要会说体验,不仅仅包括华为云电脑
给我的感觉很多情况下你真的无法无法当作一个个人PC去使用
会影响的因素非常多,例如网络质量,IO接口等等
网络质量会影响到画面,延迟和操作;8跟我说什么5G来了一切都会有可能发生
老子用的是WI-FI,靠 有本事你一天到晚一周7天一直用5G给我看看?
第二点IO接口是我认为影响蛮大的一块,我偶尔会做一些编曲,混音和母带,会需要到一些声卡,很显然云电脑也无法让我这么做…
还有一点我是保持质疑的,知乎里的答案我直接引用一下:
用户都是高大上的商务人士,日理万机,手机在手,电脑不常带,处理文件真要命,试试云电脑,云端处理,3D图形等各种文件都能用起来,不行,AI小秘来帮你,Windows生态全支持。
手机和平板的屏幕这么小,你处理文档真的会舒服吗?还不如直接用App?假设说你身边还要带一个便携式显示器,那真的还不如带一个本子算了🐎?你们说我说的484🐎?
还有一点我必须要吐槽,引用知乎答案:
玩游戏的可以自行下载体验,新版本都支持手柄操作了,此外这个云电脑还有PC版和电视版,正好科普一下云电脑的黑科技属性:再差性能的终端,哪怕是七八年前的老电脑,千元内的低端智能机,几百块的平板或机顶盒,只要有网络,都能玩吃鸡等大型游戏。这一点有点为华为老用户鸣不平了,云电脑APP对终端其实是没有要求的,华为荣耀系列也是可以用的,为什么华为云电脑只支持新品呢?
(我当解码不需要资源.gif)
你还在幻想着用老电脑体验着高帧率高分辨率的绝地求生吗?
洗洗睡~
(当然你愿意看马赛克的绝地求生还是有希望的)
在当今社会能掌握我们隐私的大多数都还是我们使用的程序,而云电脑这也意味着更加中心化
你的一举一动完全都可以被监视,你在上面存的任何一个文件都可以被分享给全世界观看
你的隐私在大多数资本雄厚,关系雄厚的公司面前来说都是放屁
可以说,为了爱国去查看你的文件,可以说,为了用户体验去查看你的文件,可以说,为了保证公司安全去查看你的文件
越中心化,结果往往都不会太好 :-)
BIG BROTHER IS WATCHING U
好了,其实这篇文章就是想来吐槽一下这个知乎的回答的
华为:拿起手机用云电脑APP来告别电脑
华为4月12日发布会余承东首次提出:云电脑产品。紧接着5月初,各大媒体爆出:华为突然扔出一颗重磅炸弹《别了电脑,华为云电脑,横空出世!》。一时间朋友圈疯传华为这一黑科技,传统电脑将被颠覆,联想诚惶诚恐。
何为“云电脑”?就是不需要你再买硬件,掏出手机下载云电脑APP,就能顺畅运行和电脑一模一样的Windows系统。
看起来非常酷的黑科技,以后我们只要买手机,不用买电脑了,下载云电脑APP就可以了。
友商们听好了:华为云电脑现在仅在华为P20、Mate10、Mate RS、M5平板上线这一黑科技,以后也只扩展到华为全系。你们用户想用,换华为吧。
再差性能的终端,哪怕是七八年前的老电脑,千元内的低端智能机,几百块的平板或机顶盒,只要有网络,都能玩吃鸡等大型游戏。
不玩游戏的,可以看看评价,云电脑玩大型电脑游戏,对延迟和网络要求是极高的,是远高于办公的需求,能有如此多好评,甚至连吃鸡这样的游戏都能云端玩。那么自己脑补一下,黑科技仅仅是华为一家所有吗?华为所说“别了电脑”的时代,还远吗?
其实,我真的认为目前云电脑真的不值得如此宣扬,华为和花粉这次吹,在我几天前逛它的论坛的时候很显然就看到了几个问题
掉线
操作延迟
上不去
操作失灵
等等问题
而且写这些什么:“别了电脑!华为重磅炸弹云电脑横空出世” 或者 “华为重新定义个人电脑,以后就没有个人电脑了,只有云电脑” 的人
他们却忽略了一群真正热爱电脑的,热爱DIY的,热爱折腾的人
他们愿意花时间,花自己攒来的钱在上面折腾自己所热爱的技术,在自己热爱的圈子里讨论如何组装最好看,如何组装是最好的方案,怎样才是最豪的,怎样才是性价比最高的…
他们热爱折腾各种操作系统,崩溃了又一次一次的来,获得成果那种满足感成就感和急切分享到社区中的心情是这些认为一个云电脑就可以取代个人电脑市场,或者一些“低端玩家”无法理解的
好了,以上就是我的一些不专业观点,欢迎各种吐槽指正…什么的
文笔不好,还请多见谅
如果你是花粉,能看到这,我感谢你的大度 XDDDDD
]]>今天来跟各位谈一个话题,我会尽量的使用人话来解释,云计算如何改变了全世界
云计算这个东西,基本上接触IT或者身为Geek的你都得懂,即使你不是个IT guy或者Geek,你身边的各种程序和App以及游戏几乎也都得依靠云计算提供的服务来存活
在2000年左右,IT技术逐渐走向成熟,但是想要构建程序或者网站并且对外公布还是一件奢侈的事情,至少对于普通的或者初创企业来说是这样的…
毕竟你要做到这些事情,就需要用到服务器,有服务器就代表需要电,有服务器也代表会产生热,所以你要解决电力和散热问题;这还没完,有了这些你还得有网络吧,一台服务器不够用还得多买几台吧?多买几台服务器你需要找地方放吧?选好地方你还要办理网络吧?然后还需要办理各种各样的许可…最后你是不是还得雇几个人来给你管网络管服务器…
这一听哪是个小企业或者初创公司能做的事情…
假设,你在没有云计算的世界里说我想创业去开发一个大型的多人游戏
别想了,洗洗睡吧…你哪来的资本去搞这些东西的基础设施,即使真的有人投资你去搞这个项目,我相信很大可能性钱几乎都在搭建基础设施,办理各种许可的等待中烧完了,更何况你都还没法做出一个实例提供给投资者和大众,谁会愿意去花这么大的风险呢…
于是人类把虚拟化技术,准确的说当时是把一个叫做虚拟机的东西玩出了花
虚拟机对很多人来说应该都不会陌生,不过在这里还是给不清楚的人解释一下
我们用的程序,例如Adobe Photoshop是运行在操作系统上的,而操作系统则需要一个硬件平台,例如主板,CPU,RAM,硬盘,光驱,各种I/O接口…
而我们的虚拟机则是一个程序,但是这个程序可以在现有的操作系统上模拟出一个虚拟的硬件平台用来安装其他操作系统
为什么这么做呢?或者说,这个技术的优势到底在哪
从几个方面来说:
首先在单个实体机上可以安装多个操作系统来达到性能的最大化运用,毕竟一台机子只装一个操作系统给一个用户用太奢侈了
第二点在于虚拟机本身是一个软件,控制软件比控制实体机来的方便和快速,可以通过程序来分配给用户不同的运算资源,操作系统等等
第三点是驱动兼容性风险会比实体机来的少,不能说没有风险,但是这的确减轻了更新配置时驱动翻车的尴尬情况
于是在2002年,Amazon正式运作了Amazon Web Services(亚马逊网络服务),并且在2006年正式推出Amazon的VPS服务Amazon EC2 (亚马逊弹性云计算服务)
AWS成为了最早实施云计算的公司后,慢慢的各家大厂也开始纷纷推出自家的云计算服务
例如:
2009年开放的阿里云
2008年开始发展并且2010年推出的Azure
2013年推出的腾讯云
等等…
在云计算的技术下,新的经济模式随即产生
那就是你交了钱,我给你服务,即使你作为创业公司破产了,交不起服务器的钱了,那我不给你服务就行了,我不会亏,你也不会亏,因为服务器本身只是我借给你用用的
根据福布斯在2015年的报告,Azure就为微软赚了12亿USD的获利
随着云计算的发展,云计算分出了三大服务类型,并且每年都可能会有新的服务类型加入,三大服务类型分别是:
IaaS(Infrastructure as a Service,基础设施即服务):在IaaS中,基本上你购买到的服务器都是虚拟机,因为机房,网络,电力,散热和服务器硬件都由云端计算服务商所提供,所以你所购买到的会是一个虚拟机,你可以控制虚拟机的开关,虚拟机快照,系统等等,你不能控制到的是物理服务器,例如你不可以真正的关闭一整台物理服务器
PaaS(Platform as a Service,平台即服务):在PaaS中,云端计算服务商给你提供了一个程序的运行时(runtime)或者说是运行环境,你只需要关注好你的软件,然后交给他们,你就完成你要做的事情了。PaaS让你无需操作服务器的规格,操作系统是什么,当你的应用需要被大量访问的时候,PaaS还可以自动拓展配置,例如网络带宽,CPU核心数量和RAM的大小
SaaS(Software as a Service,软件即服务):SaaS很好理解,可以说SaaS几乎就出现在我们每天的生活当中。例如公司的CRM(客户关系管理系统)是Salesforce公司提供的,MySQL的数据库是阿里云提供的,企业邮箱是腾讯提供的,休闲的时候你可能会用用社交网络,例如微博,它也属于SaaS的一种。你不需要担心程序的安全,不需要担心操作系统是什么,不用担心服务器的规格也不用担心程序会不会突然间被大量的访问,完全交给第三方帮你做好,你用就好
有了云计算之后,给了非常多个人作品被挖掘和初创公司可以成功的机会
因为当你想上线一个服务的时候,你发现你完全不用从头建立机房,购买服务器,规划散热装置,办理机房网络,雇用人员;而这些,都由云端计算服务商给你做好了,你只需要花少量的成本租来服务器的使用权限,在上面部署自己的程序就好
这也让非常多有意思的游戏,App,程序,社区被开发出来,越来越多好的作品被挖掘,推动着技术甚至社会的革新
云计算现在就和你身边的手机差不多,已经变得无法离开,与我共事过的大部分企业都可以说是云计算的受益者,他们花少量的成本就可以收获非常大的回报,并且一步步走向成功…
非常感谢你看完了本篇文章,如果发现有不正确的地方欢迎邮件至:jimmy@mail.jimmy0w0.me
近期公司内部使用Slack进行多部门多团队之间的沟通和合作
当然,由于Slack有Bot机制,所以我们也可以玩自动化这种骚操作
我们需要到Slack的API官网,进入之后选择Your Apps
选择Create Apps
接下来填写 App Name 和 Development Slack Workspace
后面选择的是App的类型,第一次做Slack 的App,我们直接用Incoming Webhooks来做示范
Incoming Webhooks这个类型可以让Apps在特定的群组或者联系人发送消息
接下来点开Activate Incoming Webhooks旁边的按钮,使按钮为On
下滑选择Add New Webhook to Workspace
在Post to 中选择你要让App发送消息的地方
然后选择Authorize,大功告成
Webhook URL对应不同的Channel
例如我们发送一个信息到刚才Authorize的群组中,我们可用
1 | curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, Slack"}' Your Webhook URL |
Excelize是由Xuri使用Go编写的一个用于操作Excel的文档类库,除了简单的建立和读取Excel文件以外,还支持Excel中各种单元格的细致设定,工作表设定,追加图表,添加图片,添加迷你图以及其他自定义样式等等高阶玩法。
通过Excelize实现简单创建Excel文件的Demo也非常好实现,需要:
如何安装Excelize请参考官方文档 - 安装
上一个简单的代码片段:
1 | package main |
最后执行go run main.go
即可得到一个名为Go_Excelize_Demo.xlsx
的Excel文件
RSS是Really Simple Syndication的缩写,也就是简易信息聚合的意思。通过这个技术,你可以使用一些软件或者网站去订阅你喜欢的RSS源,从而达到快速获取到更新,专注于文章内容。RSS的文件通常都是 .rss 或者 .xml。
每个人都可以建立属于自己的RSS,这也造就了不同类型的网站都拥有不同的RSS源。例如我喜欢游戏,我可以去订阅机核的RSS源,我热爱科学,我可以去订阅ScienceDaily的RSS,我喜欢医学,我可以去订阅丁香园的RSS…
看到这里你应该明白了,订阅RSS就像订阅报纸一样,当网站推送了一篇新的文章后,内容将会被收录到RSS中,你的RSS订阅器检测到发生了改变后会给你推送(一些订阅器会有)。通过订阅高品质的RSS源可以快速高效的获取信息以及得到上乘的文章质量。
不是每一个建站程序都会有RSS功能,以前我用的WordPress和Typecho都有,换到VuePress之后是肯定不会有的…想来想去还是去做了一下RSS订阅这块的功能,在这里也分享出来。
首先先去打开FEED43的网页
点击中间的Create your first RSS feed之后开始制作RSS源
因为VuePress并不是一个Blog程序…所以我必须自己建立一个文章的选择列表…
像是HEXO之类的会自动建立文章选择就会容易很多,VuePress则需要手动建立…
进入Create your first RSS feed的页面之后填写一下存在有你文章选项的链接,就像这样
Encoding那块填写UTF-8,否则中文可能会出现乱码。
Feed43会自动返回该页面的HTML代码,接下来就是自定义编辑规则了
下面的内容是按照我博客的情况来编写的,自己操作的时候可能需要多折腾一下
往下滑动,直到开始出现自己文章标题的地方,就可以开始找规律了
我要求的功能不多,只需要正确输出文章标题和链接就好
通过图片不难发现,包含文章标题和链接的规律很容易被找出来
以下是代码:
1 | <div class="page"> <div class="content"> |
我们想要的文章标题+链接分别包含在
1 | <li></li> |
这三个标签中。
所以我们需要写入以下规则
1 | <li><p><a href="%">%</a></p></li> |
把那些会变动的信息,例如标题和链接用 %
替换掉,然后点击Extract按钮就可以得到我们想要的东西了
接下来到第三步,主要设置的是
订阅源标题
链接 (极其重要,之后Feed43的服务器会自动根据你给的规则抓取信息并添加到RSS中)
订阅源说明 (选填)
标题模板
链接模板
简介内容模板
现在主要来讲一下模板输出 (RSS item properties) 的内容
第二步系统自动输出的数据中,你可以发现 %1
和 %2
这样的字符,在这一设置中就会派上用场。
第二步里,%1
和 %2
分别是 链接
和 标题
所以第一项的标题模板我们只需要填入 %2
即可,标题填入 %1
即可;文章简介没有,所以爱填啥填啥,或者不填。大概是这样的结果
点击Preview就可以预览了
接下来在下面会有两个连接
Feed URL (RSS订阅链接)
Edit URL (RSS编辑链接)
把RSS订阅链接加入阅读器的订阅列表中即可。
]]>因为本人的Vultr服务器准备要耗尽资金了,而且用的是静态网页,所以干脆凭着静态网页的优势找了一家静态网页托管服务免费使用到明天(域名到期)。
开始吧↓↓↓↓↓
我们要利用GitHub来托管我们的静态网页文件。
由于我们并不会用到GitHub Pages,所以项目名字可以随便填自己喜欢的,然后项目是否公开也取决于自己;现在GitHub允许免费用户建立三个私人仓库,所以建议大家还是选择私人仓库好了。
接下来直接上传你的静态网页文件即可
上传完毕后像这样就行了(有index
文件即可)
接下来访问 https://app.netlify.com/ 进入 Netlify
直接使用GitHub账号登录即可…
成功进入之后它会提出访问你账号仓库的权限申请,所有都选择同意即可。
接下来看到这个页面
选择右上角的New site from git后,选择GitHub;接下来再次进行授权后就看到一个选择仓库的页面
选择仓库后Netlify会自动获取Master分支下的文件,之后你会看到这样的页面
接下来就是DNS解析的事情了,我在Godaday买的域名,然后修改成了Cloudflare的DNS域名服务器,所以我会在Cloudflare上进行操作
在Netlify中点击Domains setting,选择Add custom domain
输入自己的域名之后它会问你这个域名已被使用,是否是你自己的域名?
选择是后,就添加成功了,接下来按照Netlify的指示添加相应的信息进Cloudflare即可,就像这样…
Netlify有免费的SSL服务:
进入Domains Setting > HTTPS
即可开启。
一般需要一点时间
启动之后去打打游戏,刷刷番,回来看看有没有启动成功即可。
Netlify对带有中文的URL不太友好,不过不怎么影响访问。只是可能刷新的时候会出现问题。
]]>Linux的祖先是Unix,Unix在那时候走向了商业化的道路,授权费用高,于是乎一些研究人员们开始编写自己的操作系统。其中,一位名叫 安德鲁·斯图尔特·塔能鲍姆 的计算机科学家编写了一套系统叫做Minix(也就是Mini Unix)。当时这套系统虽然提供源代码,很多人也都想合并更加完善稳定的功能进去,但是安德鲁都拒绝了,因为他认为这样会破坏系统的纯净性…于是Linus(全名Linus Benedict Torvalds )看不爽了,作为一个暴躁老哥,撸开袖子就干,1991年,Linux的0.1版本在论坛上被Linus公布了出来。Linus说,这套系统完全开源,大家可以以各种方式使用这套系统,大家可以一起修改系统不完善的地方。
Linus最终于1994年公布了Linux 1.0版本,Linux这么成功也离不开 理查德·斯托曼 的GNU计划(GNU’s Not Unix 一种递归缩写)。 GNU计划又翻译成革奴计划,是一个自由软件集体的运动。该项目是想做一个免费、自由的操作系统。Linux的出生让理查德·斯托曼眼前一亮;依靠着众多支持开源的人们和GNU的努力,终于把Linux和GNU合在了一起。 在此之后,Linux提供系统最基本的内核部分,GNU则提供比较外围的软件。比较知名的项目例如:
等等…
现在,Linux也几乎是人人皆知的系统内核。但是与Windows进行对比又差在哪?
Linux刚出来的时候可是给Apple、Microsoft、Google这类闭源大厂起了个好头,Windows因为高度兼容性被很多的用户所依赖,也特别是游戏玩家。不过Windows的开发者也会被局限在一定的开发环境当中,不过这也就是Linux与Windows不同的地方。Linux因为是一个开源的内核(在操作系统中负责程序和硬件之间的交互),所以你完全可以对内核为所欲为。在你使用Linux的时候肯定也没少听说过一个词 发行版
。
前面说过,Linux只是一个系统内核,所以开发者还需要一定的开发才能开始进行交互。不过好在现在有很多社区和开源团队定制出了很多直接装直接用的Linux发行操作系统。例如Canonical公司的Ubuntu,Red Hat(红帽)公司开发的Red Hat Linux,又或者是Fedora社区基于Red Hat Linux开发的Fedora Linux等等。这些基于Linux内核开发但是提供给不同的工作环境,不同的GUI(图形用户界面),工具或者软件等等构成了不同的发行版。
Linux所关注的是安全稳定,Windows则是易用。而且Linux本身只是一个内核,所以要比Windows来的更加轻量化。也因为可以直接接触内核代码,每个企业都可以进行深度化的定制,并运用在生产环境中。
如今,越来越多的服务器,数据中心和科研机构采用Linux系统,原因有很多。例如一些专业的科研软件可能只有开发Unix(Linux)版本才移植到NT内核平台(Windows),所以不及Linux或者Unix稳定;还有就是Linux的可被高度定制化。
小到你家的冰箱,电饭煲,小区电梯,开发者的电脑;大到供应着大量玩家的游戏服务器,数据库,人工智能,大数据,区块链甚至火箭使用的计算机都有Linux的影子
当然,Linux内核也造就了像Android这样成功的产品
Linus全名Linus Benedict Torvalds,林纳斯·本纳第克特·托瓦兹
他是Linux内核的创始人,也同时撑起了Git的一片天地。
他在11岁的时候开始学习BASIC,那是他第一次接触编程。
在1989年的时候进入了国家的义务兵役,为当时的计算机部门提供支持和计算。从那开始他开始研究操作系统。
在他回去之后便有了他在论坛上发布了Linux 0.1内核的故事。
Linus的暴躁老哥外号可不是说说而已…
他的邮件没个脏字是不存在的,甚至在一次公开演讲活动中直接对NVIDIA竖起中指说:‘So NVIDIA, fuck you’…
我们告诉人们用 Linux 是因为它很安全,或者因为它是免费的、因为它是可以定制的、因为它是自由的、因为它有一个强大的社区支持着……
但是上面的所有原因都是在扯淡。我们这么跟非 Linux 用户讲是因为他们没法了解真正的原因。而且我们说多了这些借口,我们自己也开始就这么相信了。
但是在我们内心深处,还保留着真正的原因没说。
我们用 Linux 是因为它很有趣。
折腾你的系统很有趣;修改所有的设置,把系统搞挂,然后进入恢复模式去修复它很有趣;有上百个发行版供你选择很有趣;用命令行很有趣。
让我再重申一遍,用命令行很有趣。
难怪非 Linux 用户无法理解。
我是在说 Linux 的爱好者们用 Linux 是因为自己的缘故。当然,我们喜欢做好自己的工作;当然,我们喜欢避免染上病毒;当然,我们喜欢省钱。但是这些仅仅是副产品。
我们真正喜欢的是把玩这个系统,瞎胡折腾,并且发现些隐藏在软件深处迷人的真相。
]]>