looking a wood sprite in the forest

let's encrypt


Let’s Encrypt 已经公开测试,不需要再提交测试域名表单,直接就能申请
小项目以后都能用这玩意开 https 不用花钱买证书哦啦啦

文档 做一遍给域名签上证书还挺简单的

ssl

# 获取项目代码
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt

# 安装依赖
./letsencrypt-auto

# 获取证书
./letsencrypt-auto certonly --standalone -d www.example.com -d example.com
# 配置 nginx
server {
  listen 443 ;
  ssl on;
  ssl_certificate_key /etc/letsencrypt/live/youdomain/privkey.pem;
  ssl_certificate /etc/letsencrypt/live/youdomain/fullchain.pem;
}

需要注意的是 dnspod 等国内服务解析域名有问题
我这里直接切回 domains.google.com 就行了
使用 standalone 模式需要先停掉默认的 nginx
文档里提到可以使用 webroot 模式不用停 但我创建验证文件失败了 :(
默认90天过期,建议 crontab 定时更新

###Update

使用 acme.sh

  • An ACME protocol client written purely in Shell (Unix shell) language.
  • Fully ACME protocol implementation.
  • Simple, powerful and very easy to use. You only need 3 minutes to learn.
  • Bash, dash and sh compatible.
  • Simplest shell script for Let’s Encrypt free certificate client.
  • Purely written in Shell with no dependencies on python or Let’s Encrypt official client.
  • Just one script, to issue, renew and install your certificates automatically.

或者 caddyserver

Read more ⟶

simple crawler


半夜看了本小说觉得翻页太累

首先要解决的问题是找一个质量还过得去的小说站 然后写个脚本去把它爬下来章节合并到一起

import fs from "fs";
import { argv } from "process";
import request from "request";
import cheerio from "cheerio";
import iconv from "iconv-lite";
import sanitize from 'sanitize-html';


class main {
  constructor() {
    Object.assign(this, {
      path: './chapter.json',
      html: './reader.html',
      url: {
        list: 'http://www.piaotian.net/html/6/6658/'
      },
      store: []
    })
  }
  fetch(url, callback) {
    request.get(url, {encoding: null}, (error, response, body)=> {
      if (!error && response.statusCode == 200) {
        let $ = cheerio.load(iconv.decode(body, 'GBK'));
        callback($, body);
      } else {
        console.log(url);
      }
    })
  }
  runchapter() {
    this.store.map(d=> {
      if (!d.content) {
        this.fetch(this.url.list + d.href, ($, body)=> {
          let content = iconv.decode(body, 'GBK');
          d.content = sanitize(content);
          this.save();
        })
      } else {
        console.log('runchapter fail: ', d.id, d.title);
      }
    })
    this.output();
  }
  output() {
    //let content = this.store.slice(0, 2)
    let content = this.store
      .map(d=> d.content)
      .join('<hr />');
    let html = `<html>
    <head>
      <meta charset="utf8" />
      <style>
        ul, table, div {
          display: none;
        }
        hr {
          height: 1px;
          margin: 4rem 0;
        }
        body {
          padding: 0 20%;
          font:24px/1.5 'Songti Sc';
        }
      </style>
    </head>
    <body>${content}</body></html>`;
    fs.writeFileSync(this.html, html, 'utf8');
  }
  reload() {
    this.fetch(this.url.list, ($)=> {
      this.store = [];
      $('.mainbody .centent ul li a').map((id, d)=> {
        let el = $(d);
        let href = el.attr('href');
        if (!href.endsWith('.html')) {
          return
        }
        this.store.push({ id, href, title: el.text() })
      })
      this.save();
      this.runchapter();
    })
  }
  config(force){
    if (force) {
      this.reload();
    } esle {
      let text = fs.readFileSync(this.path, 'utf8').toString();
      this.store = JSON.parse(text);
      this.runchapter();
    }
  }
  save() {
    fs.writeFileSync(this.path, JSON.stringify(this.store), 'utf8');
  }
  init() {
    let force = argv[2] === '-f';
    console.log('run: ', argv[2], force);
    this.config(force);
  }
}

new main().init();

于是就水出来一篇日志啦

Read more ⟶

send notification when task finish


终端里运行长时间任务(比如 make systemimage)的时候经常会切换到其他环境做别的事情 容易忘记查看之前的任务是否完成, 查到一些方法用在任务结束时发出通知

#C-z 切到后台运行
fg; tput bel

# Mac OS X
#系统弹窗
osascript -e 'tell app "System Events" to display alert "Build Completed" message "The checkout and build have completed."'

say "Job finished" #语音播报

#notification center
osascript -e 'display notification "Job finished" with title "Alert"'
sudo gem install terminal-notifier
terminal-notifier -message "Job finished!" -title "Alert"

# Ubuntu
notify-send "Job finished!"

# KDE
kdialog --passivepopup 'Job finished'

还有 iterm2 trigger 也能用来触发通知, 高亮文字

Read more ⟶

create CNAME with internationalized domain name


前段时间买了个 idn: yányào.com 闲置了很长时间没动 趁着十一长假无所事事的机会, 把玩了一下 hexo 挂到 yanyaoer.github.io

然后 CNAME 的时候掉坑了, 看到有人说30分钟生效傻傻等就不提了 实际上这个域名的 CNAME 内容应该用编码后的字符串而不是 yányào.com

https://github.com/yanyaoer/yanyaoer.github.io/commit/7c0e4e6863904442d368e3ad5c822f8f189bb7fc#diff-adc4bfdb0829dae99e3699393e3fbaa4

diff --git a/CNAME b/CNAME
index 6cb647c..92d166c 100644
--- a/CNAME
+++ b/CNAME
@@ -1 +1 @@
-yányào.com
+xn--ynyo-2nad.com
Read more ⟶

xtag-and-shadowdom


最近在做的项目重构, 原本打算用 reactjs, 写了一些实验代码后心累无爱 找了个 domdiff 配合自定义标签和 shadowdom 的独有作用域也是爽 YY

可惜的是测试红米上的 android webview 版本(32?)不支持 ::shadow 伪类 inline 方式覆盖样式略嫌繁琐

示例代码

xtag.js

let dom = {
  shadow(el) {
    return el.createShadowRoot ? el.createShadowRoot() : el.webkitCreateShadowRoot();
  },
  attr(el, prefix='') {
    return Object.keys(el.dataset).map((d)=> `${prefix}${d}="${el.dataset[d]}"`).join(' ')
  }
}

document.registerElement('x-image', {
  prototype: Object.create(HTMLElement.prototype, {
    createdCallback: {
      value() {
        //xtag 嵌套时这里读不到attr, 放到 attach
        console.log('onCreate::image');
      }
    },
    attachedCallback: {
      value() {
        console.log('onAttach::image');
        let shadow = dom.shadow(this);
        shadow.innerHTML = `<style>
            img { max-width: 100%; }
          </style>
          <img ${dom.attr(this)} />`;

        this.onclick = (e) => {
          console.log(this);
        }
      }
    }
  })
});

// other x-tag;

index.js

import 'babel/polyfill';
import './xtag.js';

class BaseView {
  constructor(opt) {
    Object.assign(this, {
      root: dom('#app')
    }, opt);
  }
  mount() {
    console.log('event::mount');
  }
  onCreate() {
    console.log('event::create');
  }
  onUpdate() {
    console.log('event::update');
  }
}

export default class BannerView extends BaseView {
  init(...args) {
    this.mount(...args);
  }
  render() {
    return `<x-image data-src="${this.src}"
                     data-href="${this.redirect_url}" />`;
  }
}

http://www.html5rocks.com/en/tutorials/webcomponents/customelements/ http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-301/

Read more ⟶

remote pbcopy with netcat


Quick start

while (true); do nc -l 2224 | pbcopy; done
#If your laptop is running linux, replacing pbcopy with xcopy should work:
#while (true); do nc -l 2224 | xcopy; done

echo "This text gets sent to clipboard" | nc localhost 2224

echo "RemoteForward 2224 localhost:2224" >> ~/.ssh/config
ssh remote -t 'cat blablabla | nc -q0 localhost 2224'

Daemonizing pbcopy

launchctl load ~/Library/LaunchAgents/local.pbcopy.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>localhost.pbcopy</string>
    <key>ProgramArguments</key>
    <array>
       <string>/usr/bin/pbcopy</string>
    </array>
    <key>inetdCompatibility</key>
    <dict>
       <key>Wait</key>
       <false/>
    </dict>
    <key>Sockets</key>
    <dict>
       <key>Listeners</key>
       <dict>
           <key>SockServiceName</key>
           <string>2224</string>
           <key>SockNodeName</key>
           <string>127.0.0.1</string>
       </dict>
    </dict>
</dict>
</plist>

Remote pbcopy

scp pbcopy remote:/path_to/pbcopy
chmod a+x path_to/pbcopy

#!/bin/bash

[ -n "$SSH_CLIENT" ] && SESSION_TYPE="remote"

if [[ $SESSION_TYPE == "remote" ]]; then
  cat | nc -q0 localhost 2224
else
  cat | pbcopy
fi

http://brettterpstra.com/2014/02/19/remote-pbcopy-on-os-x-systems/

Read more ⟶

forward email by postfix


sudo aptitude install postfix
hostname -f


# sudo vim /etc/postfix/main.cf
myhostname = example.com
myorigin = example.com
mydestination = example1.com, example2.com, ...
virtual_alias_maps = hash:/etc/postfix/virtual

# sudo vim /etc/postfix/virtual
@example1.com    name@forward.com
@example2.com    name@forward.com


sudo postmap /etc/postfix/virtual
sudo /etc/init.d/postfix reload

https://wiki.debian.org/Postfix#Forward_Emails

https://www.linode.com/docs/email/postfix/basic-postfix-email-gateway-on-debian-6-squeeze

https://www.debian-administration.org/article/243/Handling_mail_for_multiple_virtual_domains_with_postfix

Read more ⟶

mitmproxy


mitmproxy 是个命令行下查看/修改 http 请求的交互式工具

#截图 screenshoot screenshoot

#安装

sudo apt-get install python-dev libffi-dev
pip install mitmproxy

#使用

ubuntu 上启动 mitmproxy
mitmproxy –host

手机 设置 -> WLAN -> 代理
主机名: ubuntu 的 ip
端口: 8080

然后访问网络就会在 mitmproxy 里看到请求记录(如截图)

#快捷键

j,k 上下移动
enter 进入
tab 切换 request/response

#参考 http://mitmproxy.org/doc/mitmproxy.html
http://blog.philippheckel.com/2013/07/01/how-to-use-mitmproxy-to-read-and-modify-https-traffic-of-your-phone/

Read more ⟶

brick intro


#Introducing Brick: Minimal-markup Web Components for Faster App Development #介绍 brick: 用于快速开发 webapp 的自定义标签组件

https://hacks.mozilla.org/2013/08/introducing-brick-minimal-markup-web-components-for-faster-app-development/

Those of you on the cutting HTML5 edge may have already heard of the exciting Web Components specification. If you haven’t, you’ll probably want to read up on what makes this so exciting, but long story short, Web Components promise to open up a new realm of development by letting web developers write custom, reusable HTML tags. Think of them as JavaScript plugins without the need for additional code initialization or boilerplate markup/styling.

Read more ⟶

my osx setup


#setting

# Enable full keyboard access for all controls
defaults write NSGlobalDomain AppleKeyboardUIMode -int 3

# Disable menu bar transparency
defaults write NSGlobalDomain AppleEnableMenuBarTransparency -bool false

# Allow quitting Finder via ⌘ + Q; doing so will also hide desktop icons
defaults write com.apple.finder QuitMenuItem -bool true

# Avoid creating .DS_Store files on network volumes
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true

# Disable the warning when changing a file extension
defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false

# Enable tap to click (Trackpad)
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

# Enable Safari’s debug menu
defaults write com.apple.Safari IncludeInternalDebugMenu -bool true

# To pin the dock to the right bottom
defaults write com.apple.dock pinning -string end
defaults write com.apple.dock orientation -string right

# Only Show Open Applications In The Dock
defaults write com.apple.dock static-only -bool true

defaults write com.apple.dock tilesize -int 24
defaults write com.apple.finder QLEnableTextSelection -bool true

# disable dashboard
defaults write com.apple.dashboard mcx-disabled -boolean true

#xcode with Command Line Tools

Read more ⟶