looking a wood sprite in the forest
Pour Over Coffee Makers
办公室日常用品
2020-0615 新购入 Mr.clever 聪明杯 6
2020-0622 新购入 hario drip kettle air 7, 用了两次手感偏轻水流控制得不稳 T_T
2020-1027 新购入 fellow ode 电动磨豆机 8
2020-1109 新购入 fellow stagg ekg 温控壶 9
2023-half 新购入 hoop 澡盆滤杯 10,类似聪明杯但会拉长萃取时间
每天一早 中午 到公司开始烧水,然后吭呲吭呲摇 40g 豆子兑 600ml 左右的水
手法就比较 随意 业余啦,主要参考下面两个视频,出品大概率自己都还挺喜欢的
按比例加注水量翻倍
“4:6 method” by Tetsu Kasuya 11
“The Ultimate V60 Technique” by James Hoffmann
家庭版
- 2020-0615 新购入 hario filter-in 冷萃瓶
已碎,21年夏再次购入 16
刚开始的时候装备上交了不少学费,比如:电动砍豆机、廉价手磨、aeropress,
虽然不能一概而论,实际上很大程度还是遵循一分钱一分货的原则,性能越好则价格呈指数上涨
条件允许的话尽量选择好一点的设备,毕竟也是天天要用的家伙事
Mastodon
Mastodon 长毛象
1 – 基于 rubyonrails/reactjs/nodejs 开发的分布式 &
去中心化 twiter clone。利用空闲时间在 aws lightsail 上开了个实例把服务跑了起来
一开始走了些弯路,因为选机房和省钱的缘故,重建了若干次操作系统,最后的选择是 tokyo+cloudflare,没错我又套了 cdn,实在是海外线路到北京联通不稳定
安装步骤没有使用 docker 而是参考文档从源码安装2,原因和解决方案如下:
机器用 $3.5/mo 512mem 最便宜的那档消费降级(512M 内存重启会拉垮弱鸡,服务已迁移到 oraclecloud),出于 net/io 性能考虑就不使 docker 啦内存问题,
RAILS_ENV=production bundle exec rake mastodon:setup
这一步骤执行到rails assets:precompile
, 不管是在 docker 里跑还是直接运行都会报 swap 分区不足,找到两个方案来解决:
# create swapfile <https://linuxize.com/post/create-a-linux-swap-file/>
$ sudo fallocate -l 2G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
# verify active
$ sudo swapon --show
# optional: low value is better for production
$ sudo sysctl vm.swappiness=10
邮件配置
# By default the memory limit in Node. js is 512 mb,
# use command —- max-old-space-size to increase the memory limit
$ NODE_OPTIONS="--max-old-space-size=4096" RAILS_ENV=production bundle exec rails assets:precompile
直接配置 localhost 发送邮件无效,先用 sendgrid 免费版顶着, 一天 100 封邮件目前够用
…Gitlab Repo
gitlab 里面已经有十来个 group,几百个 project,虽然不一定会每个项目都参与, 但是为了快速同步代码,又捡起了 android 开发常用的 repo 命令来管理多项目代码。 这里使用了 gitlab graphql 接口来遍历项目生成分组的 manifest 文件
#!/usr/bin/env python
import argparse
import json
import os
import urllib2
GITLAB_HOST = "gitlab.mydomain.com"
GITLAB_SSH_URL = "ssh://git@" + GITLAB_HOST
GITLAB_GRAPHQL_URL = "https://" + GITLAB_HOST + "/api/graphql"
GITLAB_TOKEN = ""
GITLAB_GROUP = [""]
BLACK_LIST = ("",)
# 以上配置修改为自己的设定
template = """<?xml version="1.0" encoding="UTF-8"?>
<!-- autogen by gen.py, do not edit this file -->
<manifest>
<remote name="origin" fetch="{ssh_url}" />
<default revision="master" remote="origin" sync-c="true" sync-j="4" />
{content}
</manifest>"""
def write_file(content, filename="default.xml"):
_file = os.path.join(os.path.dirname(__file__), filename)
with open(_file, "w") as f:
f.write(template.format(content=content, ssh_url=GITLAB_SSH_URL))
def fetch_project_of_group(group, write=True):
data = '{ group(fullPath: "%s") { projects { nodes { fullPath } } }}' % group
req = urllib2.Request(
GITLAB_GRAPHQL_URL,
data=json.dumps(dict(query=data)),
headers={
"Authorization": "Bearer " + GITLAB_TOKEN,
"Content-Type": "application/json",
},
)
resp = json.load(urllib2.urlopen(req))
content = "\n".join(
[
'<project path="{d}" name="{d}"/>'.format(d=p["fullPath"])
for p in resp["data"]["group"]["projects"]["nodes"]
if p["fullPath"] not in BLACK_LIST
]
)
if write:
write_file(content, filename=group.replace("/", "-") + "-group.xml")
else:
return content
def main():
parser = argparse.ArgumentParser(description="Process some projects.")
parser.add_argument(
"-G",
"--group",
help="for group/subgroup or all",
type=str,
choices=ns + ["all", "default"],
)
arg = parser.parse_args()
if arg.group == "all":
for x in GITLAB_GROUP:
fetch_project_of_group(x)
d = [fetch_project_of_group(x, write=False) for x in GITLAB_GROUP]
write_file("\n".join(d))
elif arg.group == "default":
d = [fetch_project_of_group(x, write=False) for x in GITLAB_GROUP]
write_file("\n".join(d))
else:
fetch_project_of_group(arg.group)
if __name__ == "__main__":
main()
USAGE: 创建完 manifest 文件之后提交到仓库就可以愉快的玩耍啦
…Dash Replacement with tmux
通过 tmux 快捷集成替换 dash.app 查询开发文档
$ brew install dasht
$ dasht-docsets | tr 'A-Z' 'a-z'
go
javascript
python_3
rust
tornado
# tmux.conf quick start
bind -n S-up command-prompt -p 'docset:' "splitw -h -fb -l 80 dasht '%%'"
Cloudflare Gost
从清明节开始,稳定运行好几年的 ss 服务器终于阵亡了,所有端口全挂。一直蹭公司的 vpn 查资料也挺到了五一,实在拖延够够的就再另外开了一台机器中转过去迁移数据,不过嘛年纪大了又开始犯懒,企图拯救获得资格认证的机器,通过一番网上冲浪学习到了目前(实测可用)能满足我需求的方案。简单来说就是:cloudflare[后文简称为 cf] + websockets over gost,实际的客户端通过 cdn 代理再接入服务
有几个需要注意的地方:
gost 启动时绑定的 localhost 不直接对外访问,走了 caddy 的转发,而这一步和 cf 的 ssl 证书配置会造成不停的重定向跳转,需要将 cf 加密模式配置为 flexible
然后修改 caddy 的域名配置为 http://domain.com https://domain.com { … } 阻止 cf 和 caddy 之间的 http -> https
gost 服务端监听 ws 协议,本地的 gost 客户端转发 wss 协议连接 cf_domain:443
需要鉴权的方案使用 socks5+wss://username:password@domain:port
android 客户端的设置,因为使用了 ws 协议,所以需要将域名写入到插件的配置里,直接用域名变量无法解析
具体配置参考官方文档,一切浪费的时间都是源于没认真仔细看文档
- https://github.com/haoel/haoel.github.io
- https://github.com/ginuerzh/gost
- https://github.com/xausky/ShadowsocksGostPlugin
update 2021-03-01 由于 shawdowsocks 的 android 客户端升级导致插件不可用,另外部署了 brook wsserver 给手机使用
…webpack resolve local module
最近的项目刚开始,设计的目录层级有点深
经常会在好几层本地路径之间互相引用
import Image from '../../../../components/image'
这层层叠叠的路径写起来实在丑陋
不由得想起 Python 从项目根目录引用模块
然后研究了一下 Node.js 里的几种简易实现
干脆利落的软连接: ln -s node_modules src
修改环境变量: NODE_PATH=. node app
从本地目录安装:
// package.json
// 需要运行 npm install
{
"name": "baz",
"dependencies": {
"foo": "file: ./src",
}
}
另外还有些修改 global,或者引入其他 require 实现的方法就不再一一列出了
最终选择的是修改 webpack 配置
// webpack.config.js
resolve: {
modulesDirectories: [__dirname, 'node_modules'],
}
https://gist.github.com/branneman/8048520 http://stackoverflow.com/questions/10860244/how-to-make-the-require-in-node-js-to-be-always-relative-to-the-root-folder-of-t/41078266#41078266 https://webpack.github.io/docs/configuration.html#resolve-modulesdirectories
…Leonard Cohen - you want it darker
Read more ⟶
web audio
照文档撸了一下 AudioContext 可视化音频
桌面浏览器上 Safari 9, Chrome stable 绘制正常
移动端只有微信的 webview 能工作, 纯玩票叻
ref: https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Visualizations_with_Web_Audio_API
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>AV</title>
</head>
<body>
<canvas id="vis"></canvas>
<audio id="av" src="YOUR_AUDIO_FILE"></audio>
<script src="index.js"></script>
</body>
</html>
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
window.onload = function(){
var canvas = document.getElementById('vis');
var canvasCtx = canvas.getContext('2d');
var isPlaying = true;
var audio = document.getElementById('av');
var audioCtx = new AudioContext();
var analyser = audioCtx.createAnalyser();
var audioSrc = audioCtx.createMediaElementSource(audio);
audioSrc.connect(analyser);
analyser.connect(audioCtx.destination);
analyser.fftSize = 2048;
var bufferLength = analyser.fftSize;
dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
WIDTH = 500;
HEIGHT = 150;
function draw() {
requestAnimationFrame(draw);
if (!isPlaying) return;
analyser.getByteTimeDomainData(dataArray);
canvasCtx.fillStyle = 'rgb(200, 200, 200)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
canvasCtx.beginPath();
var sliceWidth = WIDTH * 1.0 / bufferLength;
var x = 0;
for(var i = 0; i < bufferLength; i++) {
var v = dataArray[i] / 128.0;
var y = v * HEIGHT/2;
if(i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
canvasCtx.lineTo(canvas.width, canvas.height/2);
canvasCtx.stroke();
};
draw();
audio.play();
document.body.onclick = function() {
console.log(audio, isPlaying);
if (isPlaying) {
audio.pause();
isPlaying = false;
} else {
audio.play();
isPlaying = true;
}
}
}
move to caddy
去年用 Hexo 搭建的日志已经好几个月没更新了,最近休假有点空闲就继续更新吧。
先从 Github 迁移回自己的 Linode,然后安装一个 Caddyserver1 来渲染 markdown
Caddy is a unique web server with a modern feature set. Think nginx or Apache, but written in Go. With Caddy, you can serve your websites over HTTP/2. It can act as a reverse proxy and load balancer. Front your PHP apps with it. You can even deploy your site with git push. Cool, right?2
Download and install systemd
wget -O 'caddy.tar.gz' 'https://caddyserver.com/download/build?os=linux&arch=amd64&features=git%2Cipfilter'
tar zxf caddy.tar.gz
mkdir /opt/caddy
mkdir /var/run/caddy/
chown caddy:caddy -R /opt/caddy
chown caddy:caddy -R /var/run/caddy
mv caddy /opt/caddy/
useradd -d /var/run/caddy --system caddy
chmod 644 /etc/systemd/system/caddy.service
systemctl enable caddy.service
systemctl start caddy.service
systemctl status caddy.service
https://github.com/caddyserver/examples/blob/master/systemd%2Fcaddy.service
…mongodb backup
有台机器准备2月份下架 记一个 mongodb 备份小脚本 :)
#!/bin/bash
# vim: set et sw=2 ts=2 sts=2 ff=unix fenc=utf8:
MONGO_DATABASE="_name_"
MONGO_HOST="_ip_"
MONGO_PORT="_prot_"
TIMESTAMP=`date +%Y-%m-%dT%H:%M:%S`
MONGODUMP_PATH="/usr/bin/mongodump"
BACKUPS_DIR="/data/dumps/"
BACKUP_NAME="$MONGO_DATABASE-$TIMESTAMP"
while test $# -gt 0
do
case "$1" in
-m) echo "backup mongthly and clear week_dir"
#rm $BACKUPS_DIR"week/*"
find $BACKUPS_DIR"week" -type f -name '*.tgz' -delete
tar -czPf $BACKUPS_DIR"month/"$BACKUP_NAME.tgz $BACKUPS_DIR$MONGO_DATABASE
;;
-w) echo "backup weekly"
echo "tar -czPf $BACKUPS_DIR"week/"$BACKUP_NAME.tgz $BACKUPS_DIR$MONGO_DATABASE"
;;
-d) echo "just dump"
$MONGODUMP_PATH -d $MONGO_DATABASE --out $BACKUPS_DIR
;;
*) echo "do nothing"
;;
esac
shift
done
# crontab -e
10 3 * * * /bin/bash $HOME/bin/mongobackup.sh -d > /dev/null 2>&1
20 3 * * 1 /bin/bash $HOME/bin/mongobackup.sh -w > /dev/null 2>&1
30 3 1 * * /bin/bash $HOME/bin/mongobackup.sh -m > /dev/null 2>&1
每天早上3点按天/周/月分别保存
…