2017/12/24

tmux

Common Shortcuts

<prefix> means ctrl + b.

# start new session
$ tmux new -s myname

# list sessions
$ tmux ls

# attach to session
$ tmux a -t 0

# rename session
$ tmux rename-session -t 0 NEW_NAME

# kill session
$ tmux kill-session -t 0

Windows Control

  • <prefix> c — Create window
  • <prefix> w — List windows
  • <prefix> n — Next window
  • <prefix> p — Previous window
  • <prefix> f — Find window
  • <prefix> , — Name window
  • <prefix> & — Kill window
  • <prefix> [ — Check history

Pane Control

  • <prefix> % — Vertical split
  • <prefix> " — Horizontal split
  • <prefix> o — Swap panes
  • <prefix> q — Show pane numbers
  • <prefix> x — Kill pane
  • <prefix> q — Show pane numbers (press number key to switch to that pane)
  • <prefix> { — Move the current pane left
  • <prefix> } — Move the current pane right
  • <prefix> z — Toggle pane zoom

Resize Pane

  • <prefix> : resize-pane -D (Resizes the current pane down)
  • <prefix> : resize-pane -U (Resizes the current pane upward)
  • <prefix> : resize-pane -L (Resizes the current pane left)
  • <prefix> : resize-pane -R (Resizes the current pane right)
  • <prefix> : resize-pane -D 20 (Resizes the current pane down by 20 cells)
  • <prefix> : resize-pane -U 20 (Resizes the current pane upward by 20 cells)
  • <prefix> : resize-pane -L 20 (Resizes the current pane left by 20 cells)
  • <prefix> : resize-pane -R 20 (Resizes the current pane right by 20 cells)
  • <prefix> : resize-pane -t 2 20 (Resizes the pane with the id of 2 down by 20 cells)
  • <prefix> : resize-pane -t -L 20 (Resizes the pane with the id of 2 left by 20 cells)

Start With Configuration

#!/bin/bash
tmux -2 new-session \; \
send-keys ''cd /var/www/test; vim'' C-m \; \
split-window -h \; \
send-keys 'top''top' C-m \; \
resize-pane -R 30 \; \
split-window -v \; \
send-keys ''while true; do date; sleep 1; done'' C-m \; \

My Config

ๅฅ—ไปถ

  • tpm ๅฅ—ไปถ็ฎก็†
  • tmux-pain-control ๆไพ›ไพฟๆท็š„ๅฟซๆท้ตไพ†ๆŽงๅˆถ tmux ้ขๆฟ็š„ๅฐŽ่ˆช่ˆ‡ๅคงๅฐ่ชฟๆ•ด
  • tmux-sensible ๆไพ›ไธ€็ต„ๅˆ็†็š„้ ่จญ่จญ็ฝฎ,่ฎ“ tmux ๆ›ดๅŠ ๆ˜“ๆ–ผไฝฟ็”จ,็‰นๅˆฅๆ˜ฏๅฐๆ–ฐๆ‰‹ๅ‹ๅฅฝ
  • tmux-copycat ่ฎ“ไฝ ๅœจ tmux ไธญ้€ฒ่กŒๆ–‡ๅญ—ๆœๅฐ‹,ไธฆ่ƒฝๅค ๅฟซ้€Ÿ่ค‡่ฃฝ้ธไธญ็š„ๆ–‡ๅญ—

ๅฎ‰่ฃๅฅ—ไปถ็ฎก็†

git clone https://github.com/tmux-plugins/tpm.git ~/.tmux/plugins/tpm

~/.tmux.conf

# ~/.tmux.conf
set -g default-terminal "xterm"
setw -g mode-keys vi
set -g pane-border-status top
set -g mouse on

# List of plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'dracula/tmux'
set -g @dracula-plugins "git cpu-usage ram-usage time"
set -g @dracula-show-powerline true
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-pain-control'
set -g @plugin 'tmux-plugins/tmux-copycat'

bind-key -T copy-mode-vi 'v' send -X begin-selection

run '~/.tmux/plugins/tpm/tpm'

~/.bashrc

# ~/.bashrc
alias tmux='tmux -2'

้–‹ๅ•Ÿ tmux ๅพŒๅŸท่กŒ <prefix> + I。

Auto Start Config

#!/bin/sh

SERVICE=apache_watcher

tmux has-session -t $SERVICE

if [[ $? != 0 ]]; then
	tmux new -s $SERVICE -d

	tmux split-window -h
	tmux split-window -v -t $SERVICE:0.1
	tmux split-window -v -t $SERVICE:0.0

	tmux send-keys -t $SERVICE:0.0 'htop'  C-m
	tmux send-keys -t $SERVICE:0.2 'nload' C-m
	tmux send-keys -t $SERVICE:0.3 'sudo apachetop' C-m

	tmux clock-mode -t $SERVICE:0.1
	tmux resize-pane -R 30
fi

tmux a -t $SERVICE

Interesting Command

  • <prefix> : select-pane -T “NAME” - Set the title of the active pane
  • <prefix> : select-layout even-horizontal - Make horizontal pane widths equal
  • <prefix> : select-layout even-vertical - Make vertical pane heights equal
  • <prefix> : select-layout tiled - Arrange panes in a tiled layout
  • <prefix> space - Cycle through available pane layouts
  • <prefix> alt + arrow - Resize pane in the arrow’s direction
  • <prefix> : resize-pane -x 100 - Set the pane width to 100 cells
  • <prefix> : resize-pane -y 100 - Set the pane height to 100 cells

Reference Site

https://gist.github.com/MohamedAlaa/2961058
https://blog.htbaa.com/news/tmux-scripting

2017/12/18

Simple Regular Expression In PHP, Python, Node.js

ไปŠๅคฉ้œ€่ฆ็”จๆญฃ่ฆๅผๆ‰พๅ‡บ็ฌฆๅˆ xxx[yyy] ๅพŒ้ข้…ไปปๆ„ๅ…งๅฎน็š„็ตๆžœ,็ด€้Œ„ไธ€ไธ‹ไธ‰็จฎ่ชž่จ€ๆ€Ž้บผๆ“ไฝœๆญฃ่ฆๅผ。

PHP
<?php

$pattern = '/\w+\[\w+\].*/i';
$strings = [
    'aaa[bbb]',
    '1234[bbb][cccc]',
    '[111][22]',
    'dd[gg]ff',
    'aa[',
    'aaa][bbbb',
    'aaabbbccc',
];

foreach ($strings as $s) {
    var_dump($s.' is '.preg_match($pattern, $s));
}
Python
import re

pattern = r'\w+\[\w+\].*';
strings = [
    'aaa[bbb]',
    '1234[bbb][cccc]',
    '[111][22]',
    'dd[gg]ff',
    'aa[',
    'aaa][bbbb',
    'aaabbbccc'
];

for s in strings:
    print('%s is %s' % (s, True if re.match(pattern, s, re.IGNORECASE) else False))
Node.js
const pattern = /\w+\[\w+\].*/i;
const strings = [
    'aaa[bbb]',
    '1234[bbb][cccc]',
    '[111][22]',
    'dd[gg]ff',
    'aa[',
    'aaa][bbbb',
    'aaabbbccc'
];

for (v of strings) {
    console.log(v, 'is', pattern.test(v));
}

2017/11/06

crawler in PHP, Python, Node.js

็ถฒ้ ็ˆฌ่Ÿฒๆ˜ฏๆปฟๅธธ้‡ๅˆฐ็š„่ชฒ้กŒ,ไปŠๅคฉ็ทด็ฟ’ไธ€ไธ‹็”จ้€™ไธ‰็จฎ่ชž่จ€ๅฏซ็ˆฌ่Ÿฒ

PHP

sunra/php-simple-html-dom-parser

crawler.php
<?php

include 'vendor/autoload.php';

use Sunra\PhpSimple\HtmlDomParser;

$url = 'https://www.ptt.cc/bbs/NBA/index.html';
$html = HtmlDomParser::file_get_html($url);
$titles = $html->find('div.title');
$file = fopen('php.txt', 'w');

foreach ($titles as $title) {
    $subject = trim($title->plaintext)."\n";
    fwrite($file, $subject);
}

fclose($file);
$html->clear();

Python

crawler.py
# -*- coding: utf-8 -*-

import sys
import requests
from bs4 import BeautifulSoup

reload(sys)
sys.setdefaultencoding('utf-8')

url = 'https://www.ptt.cc/bbs/NBA/index.html'
res = requests.get(url)
soup = BeautifulSoup(res.text, 'html.parser')
titles = soup.find_all('div', 'title')
f = open('py.txt', 'w');

for title in titles:
    f.write('%s\n' % title.text.strip())

f.close()

Node.js

crawler.js
const cheerio = require('cheerio');
const request = require('request');
const fs = require('fs');

const url = 'https://www.ptt.cc/bbs/NBA/index.html';
const out = fs.createWriteStream('nodejs.txt');

request(url, (err, res, body) => {
    if (err) {
        console.log(err);
    }

    const $ = cheerio.load(body);
    const divs = $('div.title');

    divs.each((i, item) => {
        out.write($(item).text().trim() + '\n');
    });

    out.end();
});

้€™ไธ‰ๅ€‹็จ‹ๅผๅŸท่กŒ็š„็ตๆžœๆœƒๅพ—ๅˆฐไธ€ๆจฃ็š„ๅ…งๅฎน,็•ถ็„ถ้€™ๆ˜ฏๅพˆ็ฐก็•ฅ็š„็ฏ„ไพ‹,็…ง็†่ชช่ฆๅฏซไธ€ไบ›ๅฎน้Œฏๅˆคๆ–ท,ไฝ†็ทด็ฟ’็”จ็š„็ฏ„ไพ‹ๅ…ˆ้€™ๆจฃๅง,ๅ†้™„ไธŠๅŸท่กŒ็ตๆžœๆฏ”่ผƒไธ€ไธ‹้€Ÿๅบฆ

PHP 7.0.22
$ time php crawler.php

real    0m0.614s
user    0m0.072s
sys     0m0.032s

real    0m0.570s
user    0m0.056s
sys     0m0.032s

real    0m0.570s
user    0m0.056s
sys     0m0.032s

real    0m0.515s
user    0m0.040s
sys     0m0.032s

real    0m0.515s
user    0m0.040s
sys     0m0.032s

real    0m0.515s
user    0m0.040s
sys     0m0.032s
Node.js v9.0.0
$ time node crawler.js

real    0m1.357s
user    0m0.244s
sys     0m0.396s

real    0m1.744s
user    0m0.376s
sys     0m0.312s

real    0m1.312s
user    0m0.272s
sys     0m0.380s

real    0m1.522s
user    0m0.348s
sys     0m0.304s

real    0m1.369s
user    0m0.288s
sys     0m0.396s

real    0m1.523s
user    0m0.388s
sys     0m0.344s
Python 2.7.12
$ time python crawler.py
real    0m0.666s
user    0m0.232s
sys     0m0.064s

real    0m0.682s
user    0m0.204s
sys     0m0.088s

real    0m0.709s
user    0m0.240s
sys     0m0.064s

real    0m0.624s
user    0m0.228s
sys     0m0.032s

real    0m0.669s
user    0m0.264s
sys     0m0.052s

real    0m0.698s
user    0m0.260s
sys     0m0.068s

ๅŸบๆœฌไธŠ PHP ่ทŸ Python ้ƒฝๆ˜ฏๆŒ‰ไธ‹ๅŽปไธ€ไธ‹ๅฐฑๅšๅฎŒไบ†,Node.js ๆœ‰็ญ‰ๅพ…็š„ๆ„Ÿ่ฆบ,ไธ็Ÿฅ้“ๆ˜ฏๅ“ชๅ€‹็’ฐ็ฏ€ๅ‡บๅ•้กŒ,ๆˆ‘ๆœ‰ๆŠŠๆช”ๆกˆๅฏซๅ…ฅ่ทŸ dom ๆœๅฐ‹็š„้ƒจไปฝ้—œ้–‰,้€Ÿๅบฆๆฒ’ๅทฎๅคšๅฐ‘,ๆ‰€ไปฅๆ˜ฏ [request][] ๅฅ—ไปถๆ…ข?ไฝ†็ถฒ่ทฏๅคงๅฎถ้ƒฝๆŽจ [request][],ไป–ๆ‡‰่ฉฒไนŸๆ˜ฏๅŸบๆ–ผ node.js ๅ…งๅปบ็š„ http module ไธŠ็š„้ซ˜็ดšๅฐ่ฃ,่€Œไธ”ๅ…ถไป–ไบบไนŸ้ƒฝๆœ‰็”จๅฅ—ไปถ,ๅช่ƒฝ่ชชๅนพไนŽๆˆ‘ๆฏๆฌกๆธฌ่ฉฆๆฑ่ฅฟ node.js ๆ•ธๆ“š้ƒฝๆœƒๅŠ่ปŠๅฐพ,็›ฎๅ‰้‚„ๆ˜ฏ้ธๆ“‡ไฝฟ็”จไป–ๅฅฝ็”จ็š„ๅทฅๅ…ท็พค็‚บไธป。

2017/10/20

่งฃๆง‹่ณฆๅ€ผๆฏ”่ผƒ

PHP ๆœ‰ไธ€ๅ€‹ๅฏไปฅๆ›ฟๆ›่ฎŠๆ•ธ็š„ function ๅซ list,ไฝฟ็”จ็ฏ„ไพ‹ๅฆ‚ไธ‹

test.php
$arrs = ['chan', 37];
list($name, $age) = $arrs;
var_dump($name, $age);
$ php test.php
string(4) "chan"
int(37)

ๅœจ node.js ็š„ไธ–็•Œ็š„่ฉฑ,็”จๆณ•ๆ˜ฏ้€™ๆจฃ็š„

test.js
const arrs = ['chan', 37];
const [name, age] = arrs;
console.log(name, age);
$ node test.js
chan 37

python ็š„่ฉฑ่ถ…็ฐกๅ–ฎ

test.py
arrs = ['chan', 37]
name, age = arrs
print(name, age)
$ python test.py
('chan', 37)

ๅœจ PHP ไปฅๅŠ js ็š„ไธ–็•Œๅ…ถๅฏฆๆˆ‘่‡ชๅทฑ็”จ็š„ไธๅคš,ๅ…ถไป–ไบบ็š„็ฏ„ไพ‹ code ไนŸไธ็ฎ—ๅˆฐๅธธ่ฆ‹,ไฝ†ๅœจ python ็š„ไธ–็•Œๅ‡บ็พ็š„้ ป็އ้‚„็ฎ—ไธไฝŽ

2017/10/19

ๅ‹•ๆ…‹ๅ‘ผๅซ module - python

ๅœจๅฏซ็จ‹ๅผไธŠๆˆ‘ๅ€‘ๆœƒๅธธ้‡ๅˆฐไฝฟ็”จไธๅŒ็š„ๅƒๆ•ธๅ‹•ๆ…‹็š„ๅ‘ผๅซไป–็š„ class,ๅœจ PHP ๆœƒไฝฟ็”จ call_user_fun ๆˆ– call_user_func_array ไพ†้”ๅˆฐ้€™ๅ€‹็›ฎ็š„,่€Œ node.js ่ผ‰ๅ…ฅ module ๆœฌไพ†ๅฐฑๆ˜ฏๅฏไปฅไฝฟ็”จ่ฎŠๆ•ธๆ‰€ไปฅ็›ธๅฐ็ฐกๅ–ฎ,่ˆ‰ไพ‹ไพ†่ชช

tester.js
module.exports = {
    test() {
        console.log('test');
    }
};
main.js
moduleName = 'tester';
const tester = require(`./${moduleName}`);

tester.test();
$ node main.js
test

ไธ้Ž python ่ผ‰ๅ…ฅๆจก็ต„็š„ๆ–นๅผ็„กๆณ•ไฝฟ็”จๅญ—ไธฒ,ไฝ†ๅฏไปฅ้€้Ž __import__ ไปฅๅŠ getattr ้€™ๅ…ฉๅ€‹ๆ–นๅผ็ต„ๅˆ้”ๅˆฐๆˆ‘ๅ€‘่ฆ็š„็›ฎ็š„,ๆช”ๆกˆ็š„็ตๆง‹ๅฆ‚ไธ‹:

transportation/
	__init__.py
	base.py
	bike.py
	car.py
	main.py
base.py
class Base(object):
    def go(self):
        print('go with {excute_name}'.format(excute_name=self.excute_name))

้€™้šป module ็ฐกๅ–ฎไพ†่ฌ›ๅฐฑๆ˜ฏ car ไปฅๅŠ bike ๅ…ฑๅŒ็นผๆ‰ฟ็š„ๅทฅๅ…ท,็›ฎ็š„ๅœจๆธฌ่ฉฆ้€™ๆจฃ็š„่ผ‰ๅ…ฅๆ–นๅผไฝฟ็”จ็นผๆ‰ฟๆœƒไธๆœƒๆœ‰ๅ•้กŒ

car.py
from base import Base

class Car(Base):
    excute_name = 'car';

ๅŸบๆœฌไธŠๅชๆ˜ฏๅˆๅง‹ๅŒ–ไธ€ๅ€‹ module,ๆ”พไธ€ๅ€‹ property

bike.py
from base import Base

class Bike(Base):
    excute_name = 'bike'

ๅŒไธŠ

main.py
# -*- coding: utf-8 -*-

transportations = [
    'car',
    'nope',
    'bike'
]

for transportation in transportations:
    class_name = transportation.capitalize() # ๅญ—้ฆ–ๆ”นๅคงๅฏซๅณ็‚บ class name
    module_name = 'transportation.{name}'.format(name=transportation)

    try:
        module = __import__(module_name, globals(), locals(), [class_name])
        collector = getattr(module, class_name)()
        collector.go()
    except Exception as e:
        print(str(e))

ๆˆ‘ๅ€‘ๆŠŠ่ฆๅ‘ผๅซ็š„ module ๅฏซๆˆไธ€ๅ€‹ array ่ท‘่ฟดๅœˆ,ไธญ้–“ๆˆ‘ๆ•…ๆ„ๆ”พไบ†ไธ€ๅ€‹ไธๅญ˜ๅœจ็š„ module,็จ‹ๅผ็ขผ่ฃก้ขๆœ‰ไฝฟ็”จ try exception,ๆ‰€ไปฅไธ็”จๆ€•ๅ‡บ้Œฏๆœƒไธญๆ–ท,ๅŸท่กŒ็จ‹ๅผ็ตๆžœๅฆ‚ไธ‹

$ python main.py
go with car
No module named nope
go with bike

2017/08/07

Prompt In All Language

ๆœ€่ฟ‘ๅ–œๆญกๅˆปไธ€ไบ›ไบ’ๅ‹•็š„ๅฐๅทฅๅ…ท,็จ‹ๅผ็ขผๅฐ‡้œ€ๆฑ‚ๅˆ†ๆžๅพŒๅœจ terminal ่ˆ‡ user ไบ’ๅ‹•,confirm ๅ…งๅฎนๅพŒๅ‹•ไฝœๆ˜ฏๅธธ่ฆ‹็š„,้€™้‚Š็ด€้Œ„ไธ€ไธ‹ไธ‰ๅ€‹่ชž่จ€็š„ prompt ๆ–นๆณ•

prompt.php
echo 'Are you sure? [y/n]: ';

$handle = fopen('php://stdin', 'r');
$answer = trim(fgets($handle));

if ($answer === 'y') {
    echo 'Yes!'.PHP_EOL;
} else {
    echo 'Canceled!'.PHP_EOL;
}

fclose($handle);
prompt.js
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question('Are you sure? [y/n]: ', (answer) => {
    if (answer.trim() === 'y') {
        console.log('Yes!');
    } else {
        console.log('Canceled!');
    }

    process.exit();
});
prompt.sh
#!/bin/bash

read -p 'Are you sure? [y/n]: ' answer

if [[ $answer == 'y' ]]; then
    echo "Yes!"
else
    echo "Canceled!"
fi

2017/07/25

Express MVC

ๆœ€่ฟ‘ๅฟƒ่ก€ไพ†ๆฝฎๅˆๆŠŠ express ็Žฉไบ†ไธ€ไธ‹,ๅฏซ็ฏ‡ๆ–‡็ซ ไพ†็ด€้Œ„ไธ€ไธ‹ๆ•ดๅ€‹้Ž็จ‹,ๆœƒ็”จๅˆฐ็š„ๅทฅๅ…ทๅฆ‚ไธ‹:

  1. express 4
  2. node-orm2
  3. nodemon

้ฆ–ๅ…ˆๅฎ‰่ฃ express-generator,้€™ๆ˜ฏไธ€ๅ€‹ๅฏไปฅ่‡ชๅ‹•็”ข็”Ÿ express ้œ€่ฆๅ…งๅฎน็š„ๅทฅๅ…ท

$ sudo npm i -g express-generator

ๅฎ‰่ฃๅฅฝไน‹ๅพŒ,ๆˆ‘ๅ€‘ๅˆ‡ๆ›ๅˆฐๆˆ‘ๅ€‘ๆƒณ่ฆ็š„็›ฎ้Œ„ไพ†็”ข็”Ÿๅฐˆๆกˆ่ณ‡ๆ–™ๅคพ,ๆˆ‘้ธ็”จ twig ็•ถไฝœๆˆ‘็š„ๆจฃๆฟๅผ•ๆ“Ž,ๆˆ‘ๅœจ PHP ไฝฟ็”จ CI ็š„ๆ™‚ๅ€™ไนŸๆ˜ฏ้ธ็”จๅŒไธ€ๅ€‹ๅผ•ๆ“Ž,ๅฏไปฅ็„ก็—›่ฝ‰็งป

/var/www $ sudo express -v twig project

/var/www $ cd project
/var/www/project $ sudo npm i
/var/www/project $ sudo npm i orm mysql --save

ๅฐˆๆกˆ็›ฎ้Œ„็”ข็”ŸๅพŒ,ๆˆ‘ๅ€‘ๅฏไปฅไฝฟ็”จ nodemon ไพ†็›ฃๆŽง็›ฎ้Œ„,ไนŸๅฏไปฅ้”ๅˆฐไฟฎๆ”นๆช”ๆกˆๅพŒ่‡ชๅ‹•้‡่ตท็š„ๆ•ˆๆžœ

/var/www/project $ sudo nodemon bin/www

้€™ๆ™‚ๅ€™ๆ‰“้–‹ http://10.10.10.16:3000 (ๆˆ‘็š„ vm ๆธฌ่ฉฆ IP)ๅฐฑๅฏไปฅ็œ‹ๅˆฐ้ ่จญ็ถฒๅ€,ๅฆ‚ๆžœๅธŒๆœ›ๆ”น port ็š„่ฉฑๅฏไปฅๅœจ env ่จญๅฎš port,ๆˆ–่€…็›ดๆŽฅๅŽปๆ”น bin/www ้€™ๅ€‹ๆช”ๆกˆ

ๆ‰“้–‹ app.js ้€™ๆ˜ฏ้ ่จญ็š„ๅ…งๅฎน

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var index = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'twig');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

ๆ‰€ๆœ‰็š„ๅ‹•ไฝœ้ƒฝๅœจ้€™้šป app.js ่จญๅฎš,ๆˆ‘ๅ€‘ไพ†ๅปบ็ซ‹ไธ€ๅ€‹ product routes ๆช”ๆกˆ

routes/product.js
const express = require('express');
const router = express.Router();

router.get('/', function(req, res, next) {
    res.render('product/index');
});

module.exports = router;
view/product/index.twig
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h3>This is product page</h3>
</body>
</html>

ๅฐ‡ app.js ๅŠ ไธŠ้€™ๅ€‹ routes ็š„่จญๅฎš

app.js
var index = require('./routes/index');
var users = require('./routes/users');
var product = require('./routes/product');
var index = require('./routes/index');
var users = require('./routes/users');
var product = require('./routes/product');

app.use('/', index);
app.use('/users', users);
app.use('/product', product);

้€™ๆ™‚็ถฒๅ€ๆ”นๆˆ http://10.10.10.16:3000/product,ๅฐฑๆœƒ็œ‹ๅˆฐ This is product page ้€™ๅนพๅ€‹ๅญ—

ๆŽฅไธ‹ไพ†ๆˆ‘ๅ€‘้€ฒ่กŒ่ณ‡ๆ–™ๅบซ็š„ไธฒ้€ฃ,ๅœจ MySQL ๅ…งๅปบ็ซ‹ไธ€ๅ€‹ๅซ express ็š„่ณ‡ๆ–™ๅบซ,ๅฆๅค–ๅปบ็ซ‹ไธ€ๅ€‹ products ็š„ table ไธฆไธ”ๅกžๅ…ฅ่ณ‡ๆ–™

CREATE TABLE `products` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

ๆŽฅไธ‹ไพ†ๆˆ‘ๅ€‘ไพ†่จญๅฎš่ณ‡ๆ–™ๅบซ้€ฃๆŽฅ,ๅ› ็‚บๆˆ‘ไธๅ–œๆญกๅ…จ้ƒจ็š„ๆฑ่ฅฟ้ƒฝๅกžๅœจ app.js,ๆ‰€ไปฅๆˆ‘ๅฆๅค–้–‹ไธ€ๅ€‹่ณ‡ๆ–™ๅคพไพ†ๆ”พ่จญ็ฝฎๆช”

config/db.js
module.exports = (app, orm) => {
    app.use(orm.express("mysql://root:123456@127.0.0.1/express", {
        define: function(db, models, next) {
            models.product = db.define('products', {
                id: Number,
                name: String
            });
            next();
        }
    }));
};

ไฟฎๆ”น app.js ๅ•Ÿๅ‹•้€™ๅ€‹่จญๅฎš,ๅช่ฆๆ”พๅœจ app ไปฅๅŠ orm ๅˆๅง‹ๅŒ–ๅพŒๅณๅฏ

app.js
require('./config/db.js')(app, orm);

ๆˆ‘ๅ€‘ๅ…ˆๅฐ‡ routes/product.js ไฟฎๆ”นๆˆไปฅไธ‹ๅ…งๅฎน,่ฎŠๅฏไปฅๅพž็ถฒๅ€ๅ…ˆ็œ‹ๅˆฐ็ตๆžœ

const express = require('express');
const router = express.Router();

router.get('/', function(req, res, next) {
    req.models.product.all((err, data) => {
        res.json(data);
    })
});

module.exports = router;
http://10.10.10.16:3000/product
[{"id":1,"name":"product a"},{"id":2,"name":"product b"}]

้€™ๆ˜ฏๆœ€ๅŸบๆœฌ็š„็”จๆณ•,ไฝ†ๆˆ‘่ฆบๅพ—้€™ๆจฃๅฏ้‡่ค‡ๅˆฉ็”จๆ€งๅคชไฝŽไบ†,ๆ‰€ไปฅๆˆ‘ๅ€‘ไพ†ๅปบ็ซ‹้กžไผผ model ๆฉŸๅˆถๅง

routes/product.js
const express = require('express');
const router = express.Router();
const Product = require('../model/product.js');

router.get('/', async(req, res, next) => {
    try {
        const product = new Product(req.models.product);
        res.json(await product.getAllNews());
    } catch (e) {
        res.send(e);
    }
});

module.exports = router;
model/product.js
class Product {
    constructor(model) {
        this.model = model
    }

    async getAllNews() {
        return new Promise((resolve, reject) => {
            this.model.all((err, data) => {
                if (err) {
                    reject(err);
                }

                resolve(data);
            })
        })
    }
}

module.exports = Product;

้€™ๆจฃ็š„ๅŸท่กŒ็ตๆžœๆœƒไธ€ๆจกไธ€ๆจฃ,ไฝ†ๆŠŠๆฑ่ฅฟๆ‹†ไนพๆทจๆจก็ต„ๅŒ–ไบ†,ๅ†ไฟฎๆ”นไธ€ๆฌกๅœจ view ๅ‘ˆ็พๅง

routes/product.js
const express = require('express');
const router = express.Router();
const Product = require('../model/product.js');

router.get('/', async(req, res, next) => {
    try {
        const product = new Product(req.models.product);
        res.render('product/index', {
            products: await product.getAllNews()
        });
    } catch (e) {
        res.send(e);
    }
});

module.exports = router;
views/product/index.twig
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h3>This is product page</h3>
    {% for product in products %}
    <div>product: {{ product.name }}</div>
    {% endfor %}
</body>
</html>

ไปฅไธŠๅฐฑๆ˜ฏ้€™ๅ…ฉๅคฉ่ฉฆ็Žฉ express ็š„ๆ•ด็†,ๆฒ’ๆœ‰ๅฏฆ็พๆ‰€่ฌ‚็š„ controller ้ƒจไปฝ,ๅฐฑๅ…ˆๆŠŠ routes ็•ถ controller,็•ถ็„ถไธ€ๅฎšๆœƒๆœ‰ๆ›ดๅคšๆ›ดๅฅฝ็š„ๆ–นๆณ•,ๅ†ๆ…ขๆ…ขๆŽข็ดข,ๅœจๆญคไนŸๆ„Ÿ่ฌ้™ณ้ป˜ๅธๅคงๅคง็š„ๆŒ‡ๅฐŽ

2017/06/28

Monitor File Change And Execute Code

็พๅœจๅฏซ PHP ็š„ๆ™‚ๅ€™้ƒฝๆœƒ่ฉฆ่‘—ๅœจ node.js ไปฅๅŠ shell script ไธŠๅฏฆ็พ,ๆ‰€ไปฅๆœƒไธๆ–ท็š„ๅ˜—่ฉฆ็จ‹ๅผ็ขผ,ๆฏๆฌก่ฆ็œ‹ๅŸท่กŒ็ตๆžœ้ƒฝ่ฆไธ‹ไธ€ๆฌก command ไนŸๆŒบ็ดฏ็š„,ๅฏซไบ†ไธ€้šป w.js ไพ†็›ฃๆŽง้€™ไธ‰็จฎๆช”ๆกˆ็š„่ฎŠๅ‹•ไธฆๅธถๅ…ฅๅฐๆ‡‰็š„ๅ‘ผๅซๆ–นๅผไพ†็ฏ€็œๆ™‚้–“

ๆญคๅŠŸ่ƒฝ้œ€ๅฎ‰่ฃ watch,ๅŸท่กŒๆ–นๆณ•็‚บ node w.js xxx.php

w.js
const watch = require('watch');
const { exec } = require('child_process');
const path = require('path');
const argvs = process.argv;
const target = argvs[2];
const fs = require('fs');
const commandMap = {
    sh: 'bash',
    php: 'php',
    js: 'node'
};

if (target === undefined) {
    console.log('Need file argument!');
    return;
}

if (!fs.existsSync(target)) {
    console.log(`${target} is not exists!`);
    return;
}

let ext = path.extname(target).replace(/\./g, '');

if (Object.keys(commandMap).indexOf(ext) === -1) {
    console.log(`${ext} is not legal!`);
    return;
}

console.log(`Start to watch "${target}"!`);

watch.createMonitor('./', (monitor) => {
    monitor.on('changed', (file) => {
        if (file === target) {
            exec(`clear; ${commandMap[ext]} ${file}`, (error, stdout, stderr) => {
                if (error) {
                    console.error(`exec error: ${error}`);
                    return;
                }

                console.log(stdout);
                console.log(stderr);
            });
        }
    });
});

ๅฏฆ้š›ไฝฟ็”จ็•ซ้ขๅคงๆฆ‚ๆ˜ฏ้€™ๆจฃ

w.js ็คบๆ„ๅœ–

2017/06/27

Random String

ๅ…ฌๅธๆœ‰ไธ€ๅ€‹้œ€ๆฑ‚ๆƒณ่ฆ็”ข็”Ÿ unique id,้›–็„ถ PHP ๆœ‰ uniqid ๅฏไปฅไฝฟ็”จ,ไฝ†ไป–ๆœ€ๅŸบๆœฌ็š„้•ทๅบฆๆ˜ฏ 13 ๅ€‹ๅญ—ๅ…ƒ,ๆ‰€ไปฅๆฑบๅฎš่‡ชๅทฑๅˆปไธ€ๅ€‹ๅˆฉ็”จ 0-9、a-z ็”ข็”Ÿ็š„ไบ‚ๆ•ธๅฏ†็ขผ,ๆœƒๆœ‰ PHP、nodejs ไปฅๅŠ shell script ็‰ˆๆœฌ。

PHP
<?php

function code($length = 10)
{
    $codeString = '0123456789abcdefghijklmnopqrstubwxyz';
    $codeStringSplit = str_split($codeString);
    $codeStringSize = count($codeStringSplit);
    $code = '';

    for ($i = 0; $i < $length; ++$i) {
        $rand = mt_rand(0, $codeStringSize - 1);
        $code .= $codeStringSplit[$rand];
    }

    return $code;
}

for ($i = 0; $i < 10; $i++) {
    echo code().PHP_EOL;
}
node.js
function code(length) {
    var stringLength = length || 10;
    var codeString = '0123456789abcdefghijklmnopqrstubwxyz';
    var codeStringSplit = codeString.split('');
    var codeStringSize = codeStringSplit.length;
    var code = '';
    var min = 0;
    var max = codeStringSize - 1;

    for (var i = 0; i < stringLength; ++i) {
        var rand = Math.floor(Math.random() * (max - min + 1)) + min;

        code += codeStringSplit[rand];
    }

    return code;
}

for (var i = 0; i < 10; ++i) {
    console.log(code());
}
bash
#!/bin/bash

function code() {
    result=""
    length=10
    codeString="0123456789abcdefghijklmnopqrstubwxyz";
    declare -a array

    for (( i = 0; i < ${#codeString}; i++ )); do
        array+=(${codeString:$i:1})
    done

    if [[ -n $1 ]]; then
        declare -i length=$1
    fi

    max=$((${#array[@]} - 1))
    
    for (( i = 0; i < ${length}; i++ )); do
        rand=$(shuf -i 0-${max} -n 1)
        result="${result}${array[${rand}]}"
    done

    echo ${result}
}

for (( j = 0; j < 10; j++ )); do
    code
done

2017/02/27

Reverse Proxy For Docker

ๅ‰่จ€

็พๅœจๅพˆๆต่กŒ็”จ docker ่™•็†ไธ€ไบ›ๆœๅ‹™,ไปฅๆˆ‘้€™่กŒไพ†่ฌ›็”จไพ†่™•็† LAMP ๆˆ–่€…ๆ˜ฏ LNMP ๆœ€ๅธธ่ฆ‹,ไนŸๅฏ่ƒฝๆ˜ฏ็”ข็”Ÿๅ€‹ gitlab ไน‹้กž็š„,docker ๆœ€ๅคง็š„ๅ„ช้ปžๆ˜ฏๆœๅ‹™้ƒฝ่ท‘ๅœจ container,ๅฎ‰่ฃๅฟซๅ•Ÿๅ‹•ไนŸๅฟซ,่ฆๅนพๅ€‹ๆœ‰ๅนพๅ€‹,ๅฏซๅฅฝ config ๅƒๆ˜ฏ Dockerfile ไน‹้กž็š„่จญๅฎš้‚„ๅฏไปฅไธ€ๅ€‹ๆŒ‡ไปคๆžๅฎš,่ฎ“ไฝ ็š„้›ป่…ฆๅฏไปฅไฟๆŒๅพˆไนพๆทจ,็ถฒ็ซ™ไฝฟ็”จ 80 port ็•ถไฝœ้ ่จญ็š„ๅ…ฅๅฃ,ไฝ†ๆฏๅ€‹ server port ้ƒฝๆ˜ฏๅ”ฏไธ€็š„,ๆ‰€ไปฅๅ‡่จญไฝ ๆœ‰ๅพˆๅคš็ถฒ็ซ™ๆœๅ‹™,ๅฏ่ƒฝๆœƒๆŒ‡ๆดพๅพˆๅคšไธๅŒ็š„ port,ๅƒๆ˜ฏ localhost:66666 ๆˆ–่€…ๆ˜ฏ localhost:66667,ไฝ†ๆœ‰ไบ›ๆธฌ่ฉฆไฝ ๅฏ่ƒฝๆƒณ่ฆ็›กๅฏ่ƒฝ้€ผ็œŸ,ๅธŒๆœ›ๆ˜ฏ็”จ็ถฒๅ€ไพ†่ท‘ๆœๅ‹™็š„่ฉฑ,ๆˆ‘ๅ€‘ๅฏไปฅๅœจ 80 port ๅ‡ไธ€ๅ€‹ๅๅ‘็š„ proxy server,่ฎ“ไฝ ๅฏไปฅๅฐ‡ domain ๆŒ‡ๅฎšๅˆฐๅธถ port ็š„ IP,ไธ‹้ขๆœƒ็คบ็ฏ„ nginx ไปฅๅŠ apache ็š„่จญๅฎšๆ–นๅผ

Docker

ๆˆ‘ๆธฌ่ฉฆ็š„็’ฐๅขƒๆ˜ฏ Windows,ๆˆ‘ๅ…ˆไฝฟ็”จ Vagrant ๅฎ‰่ฃไธ€ๅ€‹ ubuntu 16 ็š„ VM,IP ็‚บ 10.10.10.22,ไน‹ๅพŒๅฐ‡ๆœƒๅœจ้€™ๅ€‹ VM ่ฃก้ข่ท‘ nginx ไปฅๅŠ apache servie,้‚„ๆœ‰ docker

ๆˆ‘ container ไฝฟ็”จ็š„ image ๆ˜ฏ novice/lnmp,ๆˆ‘ๅ…ˆๅ•Ÿๅ‹•ๅ…ฉๅ€‹ web service ๆœๅ‹™

$ sudo docker run -itd --name web1 -p 33333:80 novice/lnmp
$ sudo docker run -itd --name web2 -p 33334:80 novice/lnmp

ๆˆ‘ๅฐ‡ 33333 port ็ตฆ nginx ้€™ๅ€‹ web service ไฝฟ็”จ,33334 ็ตฆ apache ไฝฟ็”จ,็„ถๅพŒๆˆ‘ๅ€‘ๅฐ‡ Windows ็š„ hosts ่จญๅฎšไธ€ไธ‹

10.10.10.22 docker.web1
10.10.10.22 docker.web2

ๆŽฅ่‘—ๆˆ‘้€ฒๅˆฐ apache container ็š„ /var/www ็›ฎ้Œ„,ๅŠ ไธŠไธ€ๅ€‹ hello.php

<?php

echo 'hello I am web1';

ๅŒๆจฃๅœจ nginx ็š„ container /var/www ๅŠ ๅ…ฅ hello.php。

<?php

echo 'hello I am web2';

ๅœจ็ถฒๅ€่ผธๅ…ฅ http://docker.web1:33333/hello.php ไปฅๅŠ http://docker.web2:33334/hello.php ๅ„ๅˆฅๆœƒ็œ‹ๅˆฐ hello I am web1 ไปฅๅŠ hello I am web2,ไธ้Žๅ…ถๅฏฆ็›ฎๅ‰ๅ…ฉๅ€‹็ถฒๅ€ไบ’ๆ›ๅพ—ๅˆฐ็š„็ตๆžœไนŸๆœƒไธ€ๆจฃ,ๅ› ็‚บ็พๅœจไธป่ฆๆ˜ฏๅ› ็‚บไธๅŒ็š„ port ๅธถๅ‡บไธๅŒ็š„ web service,่€Œๆˆ‘ๅ€‘่ฆๅš็š„ไบ‹ๆƒ…ๅฐฑๆ˜ฏๆŠŠ port ๆ‹ฟๆމไปฅๅพŒ้‚„ๆœƒๆˆ็ซ‹

nginx

ๅ…ˆๅœจ้€™ๅ€‹ VM ๅฎ‰่ฃ nginx。

$ sudo apt-get install nginx

ๅฎ‰่ฃๅฅฝไน‹ๅพŒ,ๆˆ‘ๅ€‘ๅœจ /etc/nginx/sites-available็š„็›ฎ้Œ„ๅปบ็ซ‹ไธ€ๅ€‹ proxy.confๅ…งๅฎนๅฆ‚ไธ‹

server {
    listen 80;
    sendfile off;
    server_name docker.web1;

    location / {
        proxy_pass http://127.0.0.1:33333;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }
}

server {
    listen 80;
    sendfile off;
    server_name docker.web2;

    location / {
        proxy_pass http://127.0.0.1:33334;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }
}

ๆŽฅ่‘—่จญๅฎš config,็„ถๅพŒๅฐ‡ๆœๅ‹™้‡ๅ•Ÿ

$ sudo ln -s /etc/nginx/sites-available/proxy.conf /etc/nginx/sites-enabled/proxy.conf
$ sudo service nginx restart

ๆญคๆ™‚ๅฆ‚ๆžœไฝ ๅฐ‡ port ๆ‹ฟๆމ้‚„ๆ˜ฏๅฏไปฅ็œ‹ๅˆฐๆญฃ็ขบ็ตๆžœ็š„่ฉฑ่กจ็คบๆˆๅŠŸไบ†,่ชชๆ˜Žไธ€ไธ‹ sendfile off ไธป่ฆๆ˜ฏๅ› ็‚บๆˆ‘ไฝฟ็”จ VM,้€™ๅ€‹ไธ้—œ็š„่ฉฑๆœƒไธ€็›ด็œ‹ๅˆฐ cache,ๅฆ‚ๆžœไฝ ๆ˜ฏ็›ดๆŽฅๅœจ Linux ็’ฐๅขƒ็š„่ฉฑๆ‡‰่ฉฒๆ˜ฏไธ็”จ็‰นๅˆฅ่จญๅฎš

Apache

้ฆ–ๅ…ˆไพ†ๅฎ‰่ฃ apache,่จ˜ๅพ—ๅ…ˆๅฐ‡ nginx ้—œๆމ,ไธฆไธ”็ขบ่ชๅ…ฉๅ€‹ proxy ๅฟ…่ฆ็š„ module ๆœ‰้–‹

$ sudo service nginx stop
$ sudo apt-get install apache2
$ sudo a2enmod proxy
$ sudo a2enmod proxy_http

ๆŽฅ่‘—ๅœจ /etc/apache2/sites-availableๅŠ ๅ…ฅ proxy.conf,ๅ…งๅฎนๅฆ‚ไธ‹

<VirtualHost *:80>
    ServerName docker.web1
    ProxyPass / http://127.0.0.1:33334/
    ProxyPassReverse  / http://127.0.0.1:33334/
</VirtualHost>

<VirtualHost *:80>
    ServerName docker.web2
    ProxyPass / http://127.0.0.1:33333/
    ProxyPassReverse  / http://127.0.0.1:33333/
</VirtualHost>

ๆŽฅ่‘—ๆˆ‘ๅ€‘่ฃฝไฝœ link,ไปฅๅŠ้‡่ตท apache

$ sudo a2ensite proxy.conf
$ sudo service apache2 restart

ๅฆ‚ๆžœ้‚ฃๅ…ฉๅ€‹็ถฒๅ€้‡ๆ•ดไปฅๅพŒไธ€ๆจฃๅฏไปฅ็œ‹ๅˆฐๅฐๆ‡‰็š„ๆ–‡ๅญ—,่กจ็คบ apache reserse proxy ไนŸๆˆๅŠŸไบ†