分类
招聘

代友招聘:广州-移动(Android)开发-网赚产品

广州奇异果,需要移动开发一枚,做网赚类产品,主攻 Android,能同时搞定 iOS 最好。有意者请联系我,谢谢。

下面是 JD:

工作职责:

负责 Android 平台的软件产品的开发,对整个单独 app 项目负责

任职资格:

  • 3 年以上 Android 开发经验,具有多个成熟产品开发经验,能独立承担 Android 产品开发工作,有互联网行业工作经验优先考虑;
  • 熟悉 Android OS 体系结构,熟悉 Android SDK,熟悉 Android 常见应用实现机制,对 Android 应用结构有深刻的认识;
  • 熟悉 MVC 模式,熟悉跨进程应用交互方式;
  • 丰富的手机 UI 设计经验,熟悉网络编程、多线程、图形界面编程、熟悉 TCP/UDP、HTTP 协议;
  • 了解 Linux 基本命令,了解 NDK 基础;
  • 思路清晰,善于思考,能独立分析和解决问题;责任心强,具备良好的团队合作精神和承受压力的能力;
  • 熟悉软件工程,具有良好的代码编写规范和书写文档的习惯;
  • 有很强的自学能力,喜欢钻研技术。
分类
pouchdb

PouchDB 使用笔记

0. 缘由

我厂的 QA 产品是一个比较重的单页应用,支持用户在本地维护测试用例、执行测试、记录结果,所以需要相对比较复杂的数据存储支持。

首先排除 cookie;localStorage 因为空间有限,也排除了。FileSystem API 不知道目前是个什么状态,不过似乎很少听到它的消息,多半好不到哪儿去,而且明显数据库更适合我,所以最后选定了 IndexedDB

IndexedDB 可以使用不超 1/3 的硬盘空间,大部分场合应该都足够用了。不过 IndexedDB 的 API 层级相对较低,换言之,就是对普通开发者而言,不好用。所以,我选择之前听说过的 PouchDB 作为功能层,提升开发效率。

1. PouchDB 核心功能

首先是存储数取。PouchDB 使用 key-value 存储,然后在上面提供一层类 SQL 查询封装。这套架构已经被 Google 证明性能最好、扩展性最强,比起传统的关系数据库,比如 MySQL,它更容易横向扩展,天生分布式(参考自 Teahour 90 – 和 PingCAP CTO 畅谈数据库和编程语言)。

它的存储结构很像 LeanCloud,每次存储都会生成一个对象,对象里有若干属性。开发中可以很容易的增删属性,表的概念和列的概念在这里并不突出。

下一个重点是同步。PouchDB 完全兼容 CouchDB 协议,而 CouchDB 设计成可以很方便地把数据从一个实例同步到另一个实例。所以我本地使用了 PouchDB,将来就可以利用一个中心仓库在几个不同的实例之间同步代码,或者在本地的几个不同浏览器之间同步代码。

PouchDB 还提供版本管理功能,这点和我之前用过的其它数据库都不太一样。当我们修改对象时,就会生成一个新的版本,还可以回退到之前的版本。这个功能的核心场景我暂时还没太想出来,不过考虑到我们的产品是测试用例编辑器,似乎这个版本管理还的确可以派上些用场。

2. 实例

考虑到将来筛选(query)的需求,每个 PouchDB 实例应该对应一个 MySQL 表,只存放一类内容。

比如 QA,那么就是一个 pouch 实例存储测试用例,一个存储日志。测试用例可能要保留 5+ 个版本历史,日志就不需要版本历史。还有一些别的存储需求,比如测试用例所属的项目(类似打 tag),因为结构非常简单,我考虑用 localStorage 直接存,就不走 PouchDB 了。

3. 同步

PouchDB 实例之间的同步非常简单,只需要一行代码:

const db = new PouchDB('mydb');
// 假设本机启动了 pouchdb-server,端口是 5984
// live 表示当本地 db 发生变化时,自动推送变化到 server
db.replicate.to('http://localhost:5984', {live: true});

// 还可以使用 .sync() 确保 server 的数据能回到本地
// 这对开发过程中修改数据非常有用
db.sync(remoteDB, {live: true});

更完整的文档参考:Replication

4. 最佳实践

官方有一个最佳实践指导,刚开始用,很多地方不是很能理解,不过多看几遍总有好处:12 pro tips for better code with PouchDB

5. 官方文档

链接在此,建议认真阅读。

分类
ghost

Ubuntu 配置 Nginx + Ghost

按照惯例,买好机器,登录进去。建议是境外服务器,可以省掉备案环节。但是不要干坏事哦。

1. 更新系统

apt-get update
apt-get upgrade

2. 创建 ssh-key

ssh-keygen -t rsa -b 4096 -C "my-email@meathill.com"

3. 添加 authorized_keys

使用 ssh key 登录可以大大提升服务器的安全性。

首先,将你的电脑上的公 key 添加到服务器 ~/.ssh/authorized_keys。接着编辑服务器上的 /etc/ssh/sshd_config,禁用密码登录。

ChallengeResponseAuthentication no
PasswordAuthentication no

最后重启 ssh 服务:service ssh restart

4. 安装 Node.js

按照 NodeSource Node.js Binary Distributions 的指引,安装对应版本的 Node.js—— Ghost 要求 LTS,所以目前只能用 v12。

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs

5. 安装 MySQL/MariaDB

Ghost 默认使用 SQLite 作为数据库,不过生产环境中 SQLite 性能不够,因此建议直接安装并使用成熟可靠的数据库软件。这里建议使用 MariaDB,开源免费。不同版本的 MariaDB 源可以在 MariaDB Repositories 找到,我一般使用清华的源:

sudo apt-get install software-properties-common
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] https://mirrors.ustc.edu.cn/mariadb/repo/10.5/ubuntu focal main'

# 添加完仓库和 key 之后,就可以安装了
sudo apt update
sudo apt install mariadb-server

MariaDB 的安装过程没有配置密码的环节,可以使用 重置 MariaDB root 密码 一文中介绍的方法先重置 root 密码,然后创建需要的用户和数据库。

create user 'ghost'@'localhost' identified by 'ghost';
create database `ghost`;
grant all on `ghost`.* to 'ghost'@'localhost';

6. 安装 ghost-cli

ghost-cli 是 ghost 提供的命令行管理工具,可以大大减少我们管理 Ghost 实例的时间。

# 安装
npm i ghost-cli -g

# 创建一个目录安装 ghost
cd /var/www
mkdir ghost
cd ghost

Ghost 要求我们不能用 root 用户维护实例,所以这时可能需要创建一个新用户,并赋予其 sudo 权限(方便操作目录权限之类的),我习惯命名为 ghost-admin:

adduser ghost-admin
usermod -aG sudo ghost-admin

接着,切换到 ghost-admin,安装 ghost

su ghost-admin
cd /var/www
# 改变权限
sudo chown -R ghost-admin:ghost-admin ghost
cd /var/www/ghost
ghost install

安装完成之后,执行 ghost ls,可以看到正在运行的 Ghost 实例,就是一切正常了:

+ sudo systemctl is-active ghost_fav-meathill-com
┌──────────────────┬────────────────┬─────────┬──────────────────────┬─────────────────────────┬──────┬─────────────────┐
│ Name             │ Location       │ Version │ Status               │ URL                     │ Port │ Process Manager │
├──────────────────┼────────────────┼─────────┼──────────────────────┼─────────────────────────┼──────┼─────────────────┤
│ my-ghost │ /var/www/ghost │ 3.31.5  │ running (production) │ http://my-ghost.com │ 2368 │ systemd         │
└──────────────────┴────────────────┴─────────┴──────────────────────┴─────────────────────────┴──────┴─────────────────┘

接下来,编辑 config.production.json,把数据库配置和域名配置都写进去,就基本可用了。

7. 安装并配置 Nginx

首先,配置源。目前 Ubuntu 20.04,代号 focal,命令如下:

deb https://nginx.org/packages/ubuntu/ focal nginx
deb-src https://nginx.org/packages/ubuntu/ focal nginx
sudo apt update
sudo apt install nginx

增加配置文件,并进行反向代理,即把外来的访问反向代理给 Ghost 服务:


cp /etc/nginx/site-available/default /etc/nginx/site-available/ghost.conf
ln -s /etc/nginx/site-available/ghost.conf /etc/nginx/site-enabled/ghost.conf

配置文件大体如下:

server {
    listen 80;
    listen [::]:80;

    server_name my-ghost.com;
    root /var/www/ghost/system/nginx-root; # Used for acme.sh SSL verification (https://acme.sh)

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;

    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}

最后重启 nginx 服务或者重新加载配置即可:

# 一般建议先检查一下配置有没有问题
nginx -t
service nginx restart

总结

这次主要更新了使用 ghost-cli 安装和配置的内容,服务器也改用 MariaDB,新的技术使得安装配置都更简单了。

分类
前端

Boostrap 发布图表库 Bootrap Icons 1.0,该准备切换到 SVG 了

Bootstrap 团队一边开发 Bootstrap 5,一边发布了 Bootstrap Icons 1.0(以下简称 BI)。官方全文见此:Bootstrap Icons v1.0.0。这个版本里包含 1100 个图标,涵盖范围很广,全免费(MIT),可以自由使用、修改。

真正引起我注意的是,这个仓库不再使用 Webfont,而是全部使用 SVG。官方文档 介绍了三种使用方式:

  1. 直接嵌入 SVG 代码
  2. <svg> 中引用 BI 库并配合 <use> 使用
  3. 通过 bootstrap-icons/icons/*.svg 直接使用

如果你非常怀念 Webfont 方式,可以用(3)结合 CSS background-image 的方式近似的模拟。

其实,GitHub 很早以前就开始从 Webfont(Icon font) 向 SVG 切换了,他们还特地写了篇博客解释这件事情:Delivering Octicons with SVG,在里面列举了六个理由:

  1. Icon font 本身存在一些渲染问题,Webkit 内核的浏览器会使其边缘模糊,不利于辨识
  2. Icon font 一般会延后加载,而字体在完成加载之前无法判定大小,所以加载完成之后,需要重新计算布局并且重新进行渲染,造成不必要的性能损耗
  3. 可用性。参考这篇 Slide:Death to Icon Fonts,每十个人当中就有一个人存在阅读障碍,他们很难使用普通字体,必须使用专为他们设计的特殊字体,而这个时候,icon font 就会变成没有意义的方块。对使用阅读器的人更甚:icon font 是一种 hack,通常来说,我们并不会真的写文字进去,所以在他们看起来,图标会变得完全不可读。
  4. 合适的图标尺寸
  5. 方便重构字体文件
  6. 方便制作动画

所以,如今 Bootstrap 放弃 webfont,使用 SVG 作为主要格式,应该说是大势所趋。这几年使用 Webfont,的确很方便,给开发带来很大的效率提升,但与此同时,也存在不少问题:

  1. fontawesome 体积越来越大,现在一组字体要 1M+,加载很耗时,但是拆分非常困难,即使只用几个图标,也需要加载整个字体库
  2. 无法对图标进行稍微复杂的操作,比如双色,或者特定部位的动画
  3. 放大之后,图标比较丑,也不好加工
  4. 前面说过的那些问题

所以未来我和我厂也要慢慢从 webfont 切回 SVG。这样也有助于提升我厂产品的可用性。其实,互联网行业的可用性一直都做得不错,软件开发目前也算是残障人士最有机会获得体面收入的机会,所以,我辈仍需努力呀。

分类
flarum

解决 flarum 0.1.0-beta.12 升级 beta.13 时 `__PHP_Incomplete_Class` 问题

我在树莓派上部署了一个 flarum 实例,用来进行官网论坛的相关开发。前阵子升级 beta.13 之后,无法打开网页,报错:

Argument 1 passed to Flarum\Formatter\Event\Rendering::__construct() must be an instance of s9e\TextFormatter\Renderer, instance of __PHP_Incomplete_Class given

这个错误很奇怪,尝试调试了一下未果。我厂的论坛比较特殊,之前从邮件列表导入了很多帖子,这些帖子的 html 很不规范,所以我们魔改 /vendor/s9e/text-formatter/src/Renderers/PHP.php,去掉了 XML 校验。我怀疑跟这个有关。因为当时很忙,就没有深入。

这个周末稍微有点时间,就尝试深入了解一下,发现了这个帖子:https://discuss.flarum.org/d/19235-argument-1-passed-to-must-be-an-instance-of

在我的实例上尝试了一下,果然是这个问题。flarum 会在实例的 /storage/cache/ 下生成类的缓存文件,用来加速程序运行。但不知为何,在我的实例下,这个文件的权限既不是当前用户也不是 www-data,所以执行 php flarum cache:clear 的时候删不掉它,但是也不报错。而打开页面时,因为缓存文件有问题,就会报前面提到的错误。

使用 sudo 删掉后实例恢复正常。

分类
应用

应用创意:传承我的数字财产

我有很多数字财产,我相信大家也是如此。其中一些不怎么值钱,比如:

  1. 消费型会员账号,比如爱奇艺
  2. 各种云,比如套路云,大概3、4台服务器,托管着这个博客,以及别的一些朋友的东西
  3. 三五个域名

这些数字财产本身不太值钱,但是突然丢失会比较麻烦。比如域名,可能是帮朋友注册的,一次性付了十年二十年的租金,但是没有指向具体的服务器,如果因为某种原因我暂时无法操作账号,那么当朋友要用域名的时候,就无从下手了。

还有一些数字财产比较值钱,但又不像支付宝微信那样为大家所知,比如各种比特币、电子钱包之类的。很可能周围的人都不知道我有这个。

所以我觉得可以做个应用,叫做“传承我的数字财产”(本来想叫“我的数字遗产”,后来觉得这个名字好像不太吉利,所以改了一下),用来在特殊情况下把这些数字财产传递下去。

简单设计一下它的功能:

  1. 用户将自己的数字财产保存到应用里,比如“meathill,meathill is handsome”
  2. 这段字符会进行加密,确保运营方无法知道内容。这个加密是可逆的,因为将来需要让接收人看到明文。
  3. 用户可以把密码线索用另一种方式保存下来,比如“我小学的名字”
  4. 用户可以设置每个财产的接收人
  5. 用户需要每天打卡,打卡也需要使用密码,单独设置
  6. 如果用户没有打卡,则进入“传承倒计时”,一般是 30 天
  7. 倒计时完成后,系统自动将数字财产,和密码线索发送给指定的接收人
  8. 至此,流程完成

举个例子。有个朋友让我搭了一套网站,然后我就把“服务器、账号、密码”等保存到“传承我的数字遗产”app 里。然后有一天我突然被外星人抓走了,与此同时朋友谈妥了一个亿的融资,准备大力发展他的网站。一周后,他收到系统发送的短信,按照密码线索猜到密码,解密信息之后,就有了完整的服务器权限了。

分类
js

使用 webpack-mock-server 给组件库添加测试服务

再过一周,我就在我厂待满三年了。其实我的职业生涯还算比较顺利,除了第一次跳槽不太好,后面每个公司都选的不错,虽然远不能满足财务自由的梦想,但是几乎都能让我在技术上有所精进,在职业上也取得一定成长。

三年期间,我们做了不少产品,为了方便在不同产品之间复用代码,我把一些公共部分抽出来做成组件,独立开发和维护,并且通过 npm + GitHub Registry 管理依赖(这个部分,前面曾写过一篇文章《使用 GitHub Registry 托管私有 NPM 源》介绍)。

有一些组件,比如登录,独立出来开发没问题,但是测试比较难搞,为了它单独开发服务器有点太兴师动众。所幸我很快就找到 webpack-mock-server,它可以很方便的定义 API 接口,只要把它加到项目中,就能很容易的完成测试了。

使用方法

1. 安装

使用 npm 安装,并且添加配置文件。安装 typescript 是因为它默认会在项目根目录里找 webpack.mock.ts,我暂时不知道怎么不用 ts 写配置。

npm install -D webpack-mock-server typescript
const webpackMockServer = require("webpack-mock-server");
 
module.exports = {
  devServer: {
    before: webpackMockServer.use
  }
}

2. 配置接口

目前这个工具只会在根目录里找 webpack.mock.ts(或者说我用的还不太熟,只会这么做),好在写 express 配置并不复杂,也不需要 ts 语法:

import webpackMockServer from "webpack-mock-server";
 
// app is expressjs application
export default webpackMockServer.add((app, helper) => {
  // you can find more about expressjs here: https://expressjs.com/
  app.get("/testGet", (_req, res) => {
    res.json("JS get-object can be here. Random int:" + helper.getRandomInt());
  });
  app.post("/testPost", (_req, res) => {
    res.json("JS post-object can be here");
  });
});

3. 检查接口

接下来,正常启动 dev-server 即可:webpack-dev-server --config=build/webpack.dev.js,然后留心控制台,会多输出一个服务网址,比如:

WebpackMockServer. Started at http://localhost:8079/

这个服务一般是 dev-server 端口 -1,比如我的 dev-server 跑在 8080,那么它就在 8079。打开之后是如下所示的接口列表:

从中可以看到所有提供服务的接口,支持什么方法,点击还能查看返回结果,非常方便。

总结

使用这个工具,可以大大提升组件库的开发效率。目前我用的也不是很熟,文档中介绍的方法还没用完,也不清楚怎么不用 ts。先推荐给大家吧。

分类
linux

解决 WSL Ubuntu 20.04 下使用 apt 源安装 node.js 的问题

随着 Ubuntu 20.04 发布,各大平台都适配发布了对应版本的系统,Windows WSL 也不例外。如果你是新系统,直接在 Microsoft Store 里搜索并安装 Ubuntu 即可;如果你是老系统,已经装过以前的版本,那么需要先卸载再安装,如果直接安装 Ubuntu 20.04 会有多个不同版本的 Ubuntu 共存。

装完系统后,接着安装其它软件。我现在比较喜欢用包管理工具安装软件,因为容易更新,而我又是更新爱好者。所以按图索骥,找到 node.js 的二进制包安装指引,复制执行:curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - ,结果报错:gpg: can't connect to the agent: IPC connect call failed

经过搜索,得知这是 WSL 版 Ubuntu 20.04 的问题,与 WSL1 有一些不兼容,在 WSL2 上就没这个问题了。解决方案是装一些工具:

sudo add-apt-repository ppa:rafaeldtinoco/lp1871129
sudo apt update
sudo apt install libc6=2.31-0ubuntu8+lp1871129~1 libc6-dev=2.31-0ubuntu8+lp1871129~1 libc-dev-bin=2.31-0ubuntu8+lp1871129~1 -y --allow-downgrades
sudo apt-mark hold libc6

然后问题就解决了。

这个 issue 里还记录了一些别的方案,包括上面方案的修正版,不过我用起来没问题,也就没继续往下看。感兴趣的同学可以研究一下。

分类
前端

零宽空格的问题与使用

今天有同学在群里提问:

“我的请求路径是这样的”
“路径上面自动多加东西了,好奇怪”

我的第一反应是“零宽空格”,然后该同学试着手动敲了一遍 url 地址,问题果然解决了。看起来,就是因为原来的 URL 里含有零宽空格,直接复制过来,虽然看起来没问题,但是在发起请求时,非标准字符被 encode 之后就出错了。

我们姑且不管为啥他的复制来源里会有零宽空格,聊一聊什么是“零宽空格”,以及“零宽空格”能干什么。

零宽空格定义

零宽空格是空格的一种空格,但是它的宽度为零,即不显示,所以看起来跟没有一样。我们可以在浏览器里启动开发者工具,然后切换到 Console 面板,输入以下代码:

> a = '\u200b'; // 即零宽空格
"" // 其实是有内容的,只是看不到
> a.length
1 // 长度为 1,说明有东西
> encodeURIComponent(a)
"%E2%80%8B" // encode 之后跟截图里一样,破案了

维基百科的解释:

零宽空格(zero-width space, ZWSP)是一种不可打印的Unicode字符,用于可能需要换行处。

它的用法:

在HTML页面中,零宽空格可以替代 <wbr>。但是在一些网页浏览器(例如 Internet Explorer的版本6或以下)不支持零宽空格的功能。

MDN 上没有零宽空格的定义,但是有 <wbr> 的内容。之前我也写过一篇文章:使用 <wbr> 解决长 URL 的换行问题,里面介绍了 HTML 换行算法,以及我选择 <wbr> 的思路,建议大家看一下。

用途一:在特定位置换行

比如一首古诗:

锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦。

我们有时候会希望:

  1. 在宽度足够的时候,放一行
  2. 如果宽度不够,就在标点符号处换行

这个时候,我们可以先设置这段文字 word-break: keep-all,避免在汉字后断句换行;然后在每个标点后面加上零宽空格,这样,一行的时候就不会看到奇怪的空格,而宽度不够的时候,又能根据 white-space 属性正常换行。

用途二:特殊标记

我厂有一个产品,要输出大量日志,包含大量数字。为方便阅读,需要给数字添加千位分隔符;为了方便复制,又希望剪贴板上的是纯数字,不要千位分隔符;但是如果本来就是千位分隔符的,比如在别的软件里格式化的数字,就原样复制,不需要去掉千位分隔符。

这个时候就可以用到零宽空格。我先找出来足够长的数字,然后添加千位分隔符,然后在两头加上零宽空格。这样在用户眼里,看到的是千位分隔过的数字;等他们复制的时候,我就检查两端的零宽空格,如果有的话,就复原数字;如果没有的话,就原样返回。

其它零宽字符

除了零宽空格之外,还有很多零宽字符,可以用来在页面中加入特殊标记,或者实现一些控制功能。大家如果发现 url encode 之后的内容和之前肉眼看到的不符,那么多半是存在零宽字符,可以试着干掉它们,多半问题就能解决。

分类
linux

解决 Ubuntu 20.04 下无法打开蓝牙的问题

我的 Ubuntu 20.04 启动之后经常丢掉蓝牙。表现是设置里能看到蓝牙,状态是关闭,但是无法打开,每次点击开关过一会儿就恢复了,也没有报错。

因为要连接音箱,没有蓝牙很不方便,就搜索了一下,最后发现下面的解决方案。虽然没理解问题根源,但是解决了就好。

sudo rmmod btusb
sleep 1
sudo modprobe btusb

参考链接:

  1. Bluetooth firmware upload failing after ‘suspend’
  2. Bluetooth used to be enabled, now disabled and won’t enable. Ubuntu 16.04