2022/12/05

From Docker Virtual Network Connect To Solid MySQL Server

ๅ‡่จญๆˆ‘ๅ€‘็š„ server ๅชๆœ‰ไธ€ๅฐ,PHP ็’ฐๅขƒ็‚บ docker container,่€Œ่ณ‡ๆ–™ๅบซ็‚บๅฏฆ้ซ”ๆฉŸๅฎ‰่ฃ,่ณ‡ๆ–™ๅบซ 3306 ้˜ฒ็ซ็‰†้˜ฒๅ ตไธๅฐๅค–,ไฝ†่ฆๅ…่จฑๅพž Docker ็’ฐๅขƒๅ…ง็š„ๆœๅ‹™ๅญ˜ๅ–,ๆœ€็ฐกๅ–ฎ็š„ๆ–นๅผๅฐฑๆ˜ฏ้˜ฒ็ซ็‰†่ฎ“ Docker ็”ข็”Ÿ็š„ network interface ้€š้Ž,ไธ‹้ขๆˆ‘ๅ€‘ไพ†ๅฏฆๅš้€™ไปถไบ‹。

ไปŠๅคฉ็คบ็ฏ„็’ฐๅขƒ็‚บไฝฟ็”จ Vagrant ่ตทไธ€ๅฐ Ubuntu jammy64,็„ถๅพŒๅœจ่ฃก้ข้€ฒ่กŒ้˜ฒ็ซ็‰†็š„ๆธฌ่ฉฆ。

sudo service ufw start
sudo ufw enable
sudo ufw allow ssh

่จ˜ๅพ—้–‹ๅ•Ÿ้˜ฒ็ซ็‰†่ฆๅ…ˆๆŠŠ ssh ๆ‰“้–‹,ไธ็„ถๅฐฑๆ‚ฒๅЇไบ†,ๆŽฅ่‘—ๅฎ‰่ฃ MySQL,docker ไปฅๅŠ docker compose ้ƒจๅˆ†ๅฐฑ้บป็…ฉ่‡ช่กŒๅฎ‰่ฃ。

sudo apt install -y mariadb-server mariadb-client

ๅฎ‰่ฃๅฎŒ็•ขๅพŒ,ๆˆ‘ๅ€‘ไพ†ๅปบ็ซ‹ๆธฌ่ฉฆ็”จ่ณ‡ๆ–™ๅบซไปฅๅŠๅธณ่™Ÿ。

sudo mysql
create database laravel;
GRANT ALL PRIVILEGES ON `laravel`.* TO 'homestead'@'%' IDENTIFIED BY 'secret' WITH GRANT OPTION;

้€™้‚ŠๆฌŠ้™้–‹ % ๆฒ’้—œไฟ‚,ๅ› ็‚บๆˆ‘ๅ€‘ๅชๆœƒๅ…่จฑ docker network ๆŒ‡ๅฎš็š„ network interface ้€ฒๅ…ฅ่€Œๅทฒ。

docker-compose.yml
version: "3.9"

services:
  fpm:
    container_name: fpm
    build: .
    restart: always
    working_dir: /www
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks:
      - front_end
    volumes:
      - ./www/:/www

networks:
  front_end:
    driver_opts:
      com.docker.network.bridge.name: br-fpm
Dockerfile
FROM php:8.1-fpm

RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions @composer pdo_mysql mbstring exif pcntl bcmath gd

WORKDIR /www

้€™ๆ˜ฏ่ฆๅฎ‰่ฃ Laravel ็š„ๆœ€ๅฐ็’ฐๅขƒ,ๆˆ‘ๅ€‘ไปŠๅคฉๅชๆ˜ฏ่ฆๆธฌ่ฉฆ้€ฃ็ทšๆ‰€ไปฅๅ…ถไป–็š„่จญๅฎšๅฐฑๅ…ˆไธ็†ๆœƒไบ†。

sudo docker compose up -d

็ญ‰ image build ๅฎŒไปฅๅŠ service ่ตทไพ†ไปฅๅพŒ,ๆˆ‘ๅ€‘ไพ†ๅˆฉ็”จๆŒ‡ไปคๅฎ‰่ฃไธ€ๅ€‹ Laravel ็š„็’ฐๅขƒ。

sudo docker compose exec fpm composer create-project laravel/laravel .

ๆŽฅ่‘—ๆˆ‘ๅ€‘ๆ‰“้–‹ .env ๆช”ๆกˆ่จญๅฎš่ณ‡ๆ–™ๅบซ้€ฃ็ทšๅ…งๅฎน。

DB_CONNECTION=mysql
DB_HOST=host.docker.internal
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=homestead
DB_PASSWORD=secret

้€™ๆ™‚ไพ†ๆธฌ่ฉฆไธ€ไธ‹้€ฃ็ทš。

sudo docker compose exec fpm php artisan migrate

ๅ› ็‚บ้˜ฒ็ซ็‰†ๆ“‹ไฝไบ†,ๆ‰€ไปฅ connection ๆœƒ hang ไฝ,ๆœ€็ต‚ๅคฑๆ•—,ๆŽฅ่‘—ไพ†้–‹ๅง‹้˜ฒ็ซ็‰†่จญๅฎš。

sudo ufw allow in on br-fpm to any port 3306

ๆŽฅ่‘—ๅ†ๅŸท่กŒไธ€ๆฌก migrate ็š„ๆŒ‡ไปค。

sudo docker compose exec fpm php artisan migrate

   INFO  Preparing database.

  Creating migration table ............................................................................................................... 10ms DONE

   INFO  Running migrations.

  2014_10_12_000000_create_users_table .................................................................................................... 9ms DONE
  2014_10_12_100000_create_password_resets_table .......................................................................................... 2ms DONE
  2019_08_19_000000_create_failed_jobs_table .............................................................................................. 3ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ................................................................................... 4ms DONE

ๅฏไปฅ็œ‹ๅˆฐ migration ้ †ๅˆฉๆˆๅŠŸไบ†,ๅฆ‚ๆžœ็™ผ็”Ÿ connection refuse ็š„้Œฏ่ชค็š„่ฉฑ,่จ˜ๅพ—ๆŠŠ MySQL ็š„ bind-address ่จญๅฎš็‚บ 0.0.0.0。

iptable ็š„่ฉฑ็”จไธ‹ๆ–น็š„ๆŒ‡ไปคๆ‡‰ๅฏ้”ๅˆฐไธ€ๆจฃ็š„ๆ•ˆๆžœ。

iptables -A INPUT -i br-fpm -p tcp --dport 3306 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP

2022/11/21

Test API Server With Express.js

ๆธฌ่ฉฆ API ็ตๆžœๆ™‚ๅธธๅธธ้œ€่ฆ fake data ๅšๅฏฆ้š›ๆธฌ่ฉฆ,ๆžถ่จญ http server ๆœ‰้ปžๅฐ้กŒๅคงไฝœ,ๆˆ‘ๅ€‘ๅฏไปฅๅˆฉ็”จ express.js ่ฟ…้€Ÿๆญๅปบๆธฌ่ฉฆ็”จ็š„ API server。

้ฆ–ๅ…ˆๅฎ‰่ฃ็›ธ้—œๅฅ—ไปถ:

npm i --save express cors

ๆŽฅ่‘—ๅปบ็ซ‹ไธ€ๆ”ฏ server.js,็จ‹ๅผๅฆ‚ไธ‹:

const express = require('express');
const app = express();
const port = 8080;
const fs = require("fs");
const path = require("path");
const cors = require("cors");

app.use(cors());

function getFile(fileName) {
    return JSON.parse(fs.readFileSync(path.join(__dirname, "data", `${fileName}.json`)));
}

app.get('/api', (req, res) => {
    res.json(getFile('index'));
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`);
})

index.json ๅ…งๅฎนๅฆ‚ไธ‹:

{
    "status": true,
    "data": "hello world"
}

ๆœ‰ๆ™‚ๅ€™ json ็š„ๅ…งๅฎนๅพˆ้พๅคง,ๆˆ‘ๅ€‘ไธๅธŒๆœ›่ฒ ่ฒฌ route ็š„ๆช”ๆกˆ้•ทๅบฆ้Ž้•ทไธๅฅฝ็ถญ่ญท,ๆ‰€ไปฅๆˆ‘้€™้‚Š parse ไบ† data ็›ฎ้Œ„่ฃกๆŒ‡ๅฎš็š„ .json ๆช”ๆกˆ่ฟ”ๅ›ž,ๆŽฅ่‘—ๅช่ฆๅŸท่กŒ node server.js ๅพŒๅฐฑๅฏไปฅๅœจ http://localhost:8080/api ็œ‹ๅˆฐๆญฃ็ขบ็š„ๅ…งๅฎนไบ†。

2022/11/17

CSS Practice - Learn CSS Grid

ๆœ€่ฟ‘ๅœจๅคง้‡็ทด็ฟ’ CSS 3 ๅˆ‡็‰ˆ,็œ‹ๅˆฐๆœ‰่ถฃ็š„็‰ˆ้ขๅฐฑๆ‹ฟไพ†ๆ™‚ๅš็ทด็ฟ’,Learn CSS Grid ้€™ๅ€‹็‚ธ้–‹็š„ๆ•ˆๆžœๆŒบๆœ‰่ถฃ็š„,่ค‡่ฃฝไบ† html ๆžถๆง‹ๅพŒ,้™คไบ†ไธ€ไบ›้ก่‰ฒ่ทŸ้–“่ทๆ•ธๅ€ผไปฅๅค–็š„ css ๅฐฑๆ†‘ๆƒณๅƒๆ‰‹ๅˆปไบ†,้™คไบ†็‚ธ่ฃ‚็š„ไฝ็ฝฎๅชๆŠ“ๅ€‹ๅคงๆฆ‚ไปฅๅค–,ๆŽ’็‰ˆ็š„้ƒจๅˆ†็›ก้‡ไธ€่‡ด。

2022/10/18

Reload Nginx And Apache Config In Docker

ๅฆ‚ๆžœๅœจๆœฌๆฉŸๅฎ‰่ฃ apache2 ๆˆ– nginx ๆ™‚ๆƒณ่ฆ reload config ็š„่ฉฑๆˆ‘ๅ€‘้€šๅธธๆœƒไฝฟ็”จไธ‹ๅˆ—็š„ๆŒ‡ไปค:

# nginx
nginx -s reload

# apache2
service httpd reload

ไฝ†ๅœจ docker ็š„็’ฐๅขƒไธ‹ๅฐฑๆฒ’ๆœ‰้‚ฃ้บผ็ฐกๅ–ฎ,ๅƒ apache ็š„ reload ๆ˜ฏๆŠŠ script ๅฏซๅœจ service ็š„ config ่ฃก้ข,ไธไธ€ๅฎšๆฏๅ€‹ image ่ฃก้ข้ƒฝๆœ‰ๅŒ…ๅซ้€™ๅ€‹ๆŒ‡ไปค,CentOS ็š„ๆˆ‘่จ˜ๅพ—ๅฅฝๅƒๅฐฑๆฒ’ๆœ‰,็‚บไบ†่ฆๅ…ผๅฎนๆ‰€ๆœ‰็š„็’ฐๅขƒ,ๆˆ‘ๅ€‘ๅฏไปฅๅˆฉ็”จ kill -USR1 ้€™ๅ€‹ๆ–นๅผๅฅ—็”จๅˆฐๆฏๅ€‹ๆˆ‘ๅ€‘ๆƒณ่ฆ reload without restart ็š„ๅฐˆๆกˆไธŠ้ข。

apache2

้ฆ–ๅ…ˆๅœจๆ น็›ฎ้Œ„ๅปบ็ซ‹ๅ…ฉๅ€‹็ถฒ้ ๆช”:

  • ./www/1/index.html
  • ./www/2/index.html

ๅ„ๅˆฅๆœƒๅœจ็ถฒ้ ๅฐๅ‡บ 1 ไปฅๅŠ 2 ็š„ๅ…งๅฎน่ญ˜ๅˆฅ。

docker-compose.yml

version: "3.9"

services:
  http:
    ports:
      - "80:80"
    container_name: httpd
    image: httpd
    restart: always
    volumes:
      - "./www:/www"
      - "./config/httpd:/usr/local/apache2/conf:ro"

ๆˆ‘ๅ€‘ๅปบ็ซ‹ไบ†ไธ€ๅ€‹ๅ็‚บ http ็š„ container,ไฝฟ็”จ httpd ็š„ image,ไธฆไธ”ๆŠŠ www ็›ฎ้Œ„ๆŽ›้€ฒๅŽป,ๆˆ‘ไบ‹ๅ…ˆๆŠŠ httpd ็š„ config ่ค‡่ฃฝๅ‡บไพ†ไปฅไพฟๅœจๅค–้ขไฟฎๆ”นๅ…งๅฎน,ๆŽฅไธ‹ไพ†่ท‘ docker compose up -d,ๆ‰“ไธŠ IP ๅพŒๅฏไปฅ็œ‹ๅˆฐ httpd ๅ‚ณ็ตฑ็š„้ฆ–้ ๅ…งๅฎน It works!。

ๆŽฅ่‘—ๆˆ‘ๅ€‘ไฟฎๆ”น httpd.conf:

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">

# ๆ”น็‚บ

DocumentRoot "/www/1"
<Directory "/www/1">

ๆˆ‘ๅ€‘ๅ…ˆไฝฟ็”จ docker compose restart ๆธฌ่ฉฆ,ๅŸท่กŒๅพŒ refresh ็ถฒ้ ๅฏไปฅ็œ‹ๅˆฐๆˆ‘ๅ€‘่ฆ็š„ 1,ๆŽฅ่‘—ๅ†ๆŠŠ config ๆ”นๆˆ:

DocumentRoot "/www/2"
<Directory "/www/2">

้€™ๆฌกๆˆ‘ๅ€‘ไฝฟ็”จ docker compose kill -s USR1 http ,ๆญคๆ™‚ๆˆ‘ๅ€‘ๅฏไปฅ็œ‹ๅˆฐ container ๆดปๅพ—ๅฅฝๅฅฝ็š„,ไฝ† refresh ้ ้ขๅพŒๅพ—ๅˆฐๆˆ‘ๅ€‘ๆƒณ่ฆ็š„ 2 ไบ†,ไป–็š„ๅŽŸ็†ๅฐฑๆ˜ฏๅ‘ผๅซ container ๅ…ง็š„ httpd daemon ๅŽป้‡ๆ–ฐๆ•ด็†ไป–็š„ service。

nginx

docker-compose.yml

version: "3.9"

services:
  http:
    ports:
      - "80:80"
    container_name: nginx
    image: nginx
    restart: always
    volumes:
      - "./www:/www"
      - "./config/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro"

ไธ€ๆจฃ,ๆˆ‘ๅ€‘ไบ‹ๅ‰่ค‡่ฃฝไบ† nginx ็š„ config ๅ‡บไพ†้‡ๆ–ฐๆŽ›่ผ‰ๅ›žๅŽป,ๅ•Ÿๅ‹• container ไปฅๅพŒ็œ‹่ฆ‹ nginx ็š„้ ่จญๅญ—ๆจฃ Welcome to nginx!。

ๆŽฅ่‘—ๆˆ‘ๅ€‘ไฟฎๆ”น defalut.conf

root   /usr/share/nginx/html;

# ๆ”น็‚บ

root   /www/1;

่ทŸ apache2 ไธ€ๆจฃๆˆ‘ๅ€‘ๅ…ˆไฝฟ็”จ docker compose restart,ๅฏไปฅ้ †ๅˆฉ็œ‹ๅˆฐ 1 ็š„็ตๆžœ,ๆŽฅ่‘—ๆˆ‘ๅ€‘ๅฐ‡ config ไฟฎๆ”น็‚บ:

root   /www/2;

ไฝฟ็”จ docker compose exec http nginx -s reload,ๆˆ‘ๅ€‘ๅฐฑๅฏไปฅ้ †ๅˆฉ็œ‹ๅˆฐ 2 ไบ†,nginx ่ทŸ apache2 ็š„ container ๅ•Ÿๅ‹•ๆ–นๅผไธๅคชไธ€ๆจฃ,ๆ‰€ไปฅไธ่ƒฝ็”จ kill ็š„ๆ–นๆณ•,ไฝ†ไบ‹ๅฏฆไธŠๅœจ nginx ็š„ logrotate config ่ฃก้ขไป–ไฝฟ็”จ็š„ๆ–นๅผไนŸๆ˜ฏ:

kill -USR1 `cat /var/run/nginx.pid`

ไธŠ้ขไป‹็ดนไบ†ๅ…ฉๅ€‹ไธปๆต็ถฒ้ ไผบๆœๅ™จ็š„้‡ๅ– config ็š„็›ฎ็š„ๆ˜ฏๅœจ็•ถๆˆ‘ๅ€‘ๆŠŠ server ็š„ log ๆŽ›ๅˆฐๆˆ‘ๅ€‘ๆœฌๆฉŸ็›ฎ้Œ„ๆ™‚,ๆˆ‘ๅ€‘ๅฏ่ƒฝๆœƒ้œ€่ฆไพ็…งๆƒ…ๆณๅœจๆœฌๆฉŸๅŸท่กŒ logrotate ็š„ๅ‹•ไฝœ,้€™ๆจฃๅฐฑๅฏไปฅๆŠŠ reload ่…ณๆœฌๅฏซๅœจๆœฌๆฉŸ็š„ logrotate config ็š„ postrotate/endscript ่ฃก้ขไบ†。

้€™้‚Š้™„ไธŠ logrotate for docker ๅŸบๆœฌ็š„้…็ฝฎ:

nginx-docker

/YOUR_PATH_TO_LOG/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 nginx adm
        sharedscripts
        postrotate
            sudo docker exec YOUR_CONTAINER_NAME nginx -s reload
        endscript
}

httpd-docker

/YOUR_PATH_TO_LOG/*.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        sharedscripts
        postrotate
            sudo docker kill -s USR1 YOUR_CONTAINER_NAME 
        endscript
}

2022/10/10

Delete None Keeping Files By Command find

ไน‹ๅ‰ๅฏซ้Ž Delete None Keeping File ้€™็ฏ‡,็”จไพ†ๅˆช้™คๅ›บๅฎšๆ•ธ้‡็š„ไฟๅญ˜ๆช”ๆกˆ,ไฝฟ็”จ็š„ๆ˜ฏ ls,ไปŠๅคฉไฝฟ็”จ find ไพ†้”ๆˆไธ€ๆจฃ็š„ๆ•ˆๆžœ。

้ฆ–ๅ…ˆๆˆ‘ๅ€‘ไพ†ๅปบไบŒๅๅ€‹ไปฅ 0.05 ็ง’้–“่ทไธๅŒ็š„ๆช”ๆกˆ:

for i in {1..20}; do touch "${i}.log"; sleep 0.05; done

ๅˆ—ๅ‡บๆช”ๆกˆไธ€ไธ‹็›ฎๅ‰ๆŽ’ๅบๆ˜ฏ้€™ๆจฃ。

0 Oct 15 23:56 1.log
0 Oct 15 23:56 10.log
0 Oct 15 23:56 11.log
0 Oct 15 23:56 12.log
0 Oct 15 23:56 13.log
0 Oct 15 23:56 14.log
0 Oct 15 23:56 15.log
0 Oct 15 23:56 16.log
0 Oct 15 23:56 17.log
0 Oct 15 23:56 18.log
0 Oct 15 23:56 19.log
0 Oct 15 23:56 2.log
0 Oct 15 23:56 20.log
0 Oct 15 23:56 3.log
0 Oct 15 23:56 4.log
0 Oct 15 23:56 5.log
0 Oct 15 23:56 6.log
0 Oct 15 23:56 7.log
0 Oct 15 23:56 8.log
0 Oct 15 23:56 9.log

็”จ้ ่จญ็š„ find ๆŸฅ่ฉขไธ€ไธ‹ๆช”ๆกˆๅ…งๅฎน。

find . -type f -name "*.log"

./10.log
./8.log
./19.log
./14.log
./13.log
./9.log
./2.log
./20.log
./1.log
./17.log
./6.log
./15.log
./12.log
./16.log
./4.log
./5.log
./11.log
./7.log
./18.log
./3.log

ๆœ‰ไบบๅฏ่ƒฝๆœƒ่ชช้‚ฃๅˆฉ็”จ sort ไธๅฐฑๅฏไปฅ่‡ช็„ถๆŽ’ๅบๆญฃ็ขบ็š„ๆ•ธๅญ—,ๆฒ’้Œฏ,ไฝ†้‚ฃๆ˜ฏๅ› ็‚บๆˆ‘ๅ€‘็š„ๆช”ๅ่‡ช็„ถๆœ‰ๅบ,่‹ฅ้‡ๅˆฐๅฏฆ้š›ไธŠๆฒ’ๆœ‰้€™ๆจฃ็ตๆง‹็š„ๆช”ๆกˆ,ๆˆ–่€…่ฌไธ€่‡จๆ™‚ไฟฎๆ”นไบ†ๆช”ๅ้€ ๆˆๆ–ฐๆŽ’ๅบไธๆ˜ฏๆˆ‘ๅ€‘่ฆ็š„็ตๆžœๅฐฑๆœƒๅ‡บๅ•้กŒ,ๆ‰€ไปฅๆœ€ไฟ้šช็š„ๆ–นๆณ•้‚„ๆ˜ฏๅˆฉ็”จๆ—ฅๆœŸๅŽป่‡ช็„ถๆŽ’ๅบ。

ๆˆ‘ๅ€‘ๅˆฉ็”จ -printf ้€™ๅ€‹ๆŒ‡ไปคไพ†้”ๅˆฐ็›ฎ็š„。

find . -type f -name "*.log" -printf "%T+ %p\n" | sort
2022-10-15+23:56:20.1777708840 ./1.log
2022-10-15+23:56:20.2297708850 ./2.log
2022-10-15+23:56:20.2817708850 ./3.log
2022-10-15+23:56:20.3337708860 ./4.log
2022-10-15+23:56:20.3857708860 ./5.log
2022-10-15+23:56:20.4457708870 ./6.log
2022-10-15+23:56:20.5057708880 ./7.log
2022-10-15+23:56:20.5697708880 ./8.log
2022-10-15+23:56:20.6337708890 ./9.log
2022-10-15+23:56:20.6817708890 ./10.log
2022-10-15+23:56:20.7377708900 ./11.log
2022-10-15+23:56:20.7977708910 ./12.log
2022-10-15+23:56:20.8457708910 ./13.log
2022-10-15+23:56:20.9057708920 ./14.log
2022-10-15+23:56:20.9577708920 ./15.log
2022-10-15+23:56:21.0217708930 ./16.log
2022-10-15+23:56:21.0977708940 ./17.log
2022-10-15+23:56:21.1617708950 ./18.log
2022-10-15+23:56:21.2137708950 ./19.log
2022-10-15+23:56:21.2617708960 ./20.log

printf ่ฉณ็ดฐ็”จๆณ•ๅฏไปฅๅƒ่€ƒ How to Use find -printf in Linux?,้žๅธธ็š„ๅผทๅคง,ๅ…ถไป–ๅทฅไฝœๅฐฑๅฏไปฅๆŽฅ็บŒ Delete None Keeping File ่™•็†。

find . -type f -name "*.log" -printf "%T+ %p\n" | sort -r | awk '{print $NF}' | tail -n +11
./10.log
./9.log
./8.log
./7.log
./6.log
./5.log
./4.log
./3.log
./2.log
./1.log

ไธŠๆ–นๆŒ‡ไปคๆญ้… xargs rm -f ๅฐฑๅฏไปฅๅชไฟ็•™ๆœ€ๆ–ฐ็š„ๅไปฝๆช”ๆกˆ。

2022/09/18

Supervisord

Supervisord ๆ˜ฏ Linux ไธŠๅธธ็”จ็š„ daemon ็›ฃๆŽงๅทฅๅ…ท,ๅฏไปฅไฝฟ็”จไป–ไพ†ๅ•Ÿๅ‹•ไธฆ็›ฃๆŽง daemon,่‹ฅ daemon ๅ› ็‚บไปปไฝ•็š„็‹€ๆณๆถˆๅคฑ,ๅฏไปฅ้€ฒ่กŒ้‡ๅ•Ÿไธฆไธ”่จ˜้Œ„。

้ฆ–ๅ…ˆๆˆ‘ๅ€‘ๆ‰‹ๅ‹•ๅปบ็ซ‹ไธ€ๅ€‹ daemon。

/tmp/test.sh
while true
do
    echo test
    sleep 1
done

ๆŽฅ่‘—ไพ†ๅปบ็ซ‹้…็ฝฎๆช”ๆกˆ。

/etc/supervisor/conf.d/test.conf
[program:test]
command = bash /tmp/test.sh
autostart = true
autorestart = true

้…็ฝฎๅฎŒๆˆๅพŒ,ๅŸท่กŒ็›ธ้—œๆŒ‡ไปคไฝฟ็”จ。

$ sudo supervisorctl status
# ้€™ๅ€‹ๆ™‚ๅ€™ๆ˜ฏ็ฉบ็š„

$ sudo supervisorctl reload
Restarted supervisord

$sudo supervisorctl status
test                             STARTING
# ๆˆ‘ๅ€‘ๅฏไปฅ็œ‹ๅˆฐๅ‰›ๆ‰้…็ฝฎ็š„ test.conf ๅทฒ็ถ“็”Ÿๆ•ˆไบ†

$ ps -aux | grep test
root        5884  0.0  0.3   8696  3352 ?        S    01:27   0:00 bash /tmp/test.sh
# ๅˆฉ็”จ ps ไพ†็œ‹ไป–ไนŸ็ขบๅฏฆๅœจ่ƒŒๆ™ฏ้‹ไฝœ

$ sudo kill -9 5884
# ๆˆ‘ๅ€‘ๆŠŠ้€™ๅ€‹ daemon process ็ ๆމ

$ ps -aux | grep test
root        6107  0.0  0.3   8696  3468 ?        S    01:29   0:00 bash /tmp/test.sh
# ๅฏไปฅ็œ‹ๅˆฐ้€™ๅ€‹ daemon ้‡ๆ–ฐๅ•Ÿๅ‹•ไธฆไธ”ๆ›ดๆ›ไบ† process id,่กจ็คบๅŽŸๆœฌ็š„ process ๆœ‰่ขซๆˆ‘ๅ€‘็ ๆމ,'ไฝ† supervisord ๅˆๆŠŠไป–ๅ•Ÿๅ‹•ไบ†

ๅฆ‚ๆžœๆœ‰ๆทปๅŠ ๆ–ฐ็š„ config,ๅปบ่ญฐไฝฟ็”จ sudo supervisorctl update ๅšๅ–ฎไธ€้…็ฝฎๆ›ดๆ–ฐ,ไฝฟ็”จ reload ็š„่ฉฑๆ‰€ๆœ‰็š„ process ๆœƒ้‡ๆ–ฐๅ•Ÿๅ‹•,ๅฆ‚ๆžœๆ˜ฏ่ท‘ queue ไน‹้กž็š„ process ๆ€•้€ ๆˆไธๅฏ้ ๆœŸ็š„ๅพŒๆžœ。

้…็ฝฎๅ…งๅฎน

command=/bin/cat    ; ็จ‹ๅผๅŸท่กŒๅ‘ฝไปค,ๅปบ่ญฐไฝฟ็”จ็ต•ๅฐ่ทฏๅพ‘。
process_name=%(program_name)s ; ็จ‹ๅผๅ็จฑ,ๅฏ็”จ็š„่ฎŠๆ•ธๆœ‰ `group_name`, `host_node_name`, `process_num`, `program_name`, `here`(้…็ฝฎๆช”ๆกˆ็›ฎ้Œ„)。 ไธ€่ˆฌ็จ‹ๅผ้œ€่ฆๅŸท่กŒๅคšๅ€‹ๅ‰ฏๆœฌ็š„ๆƒ…ๆณๆœƒไฝฟ็”จ。ๅพŒ้ขๆœƒๆœ‰ไพ‹ๅญ。
numprocs=1     ; ็จ‹ๅผๅŸท่กŒ็š„ๅ‰ฏๆœฌๅ€‹ๆ•ธ,้ ่จญ็‚บ1,ๅฆ‚ๆžœๅ€ผๅคงๆ–ผ1,ๅ‰‡`process_name` ๅฟ…้ ˆๅŒ…ๅซ `%(process_num)s`
numprocs_start=0    ; `%(process_num)s`่ตทๅง‹ๆ•ธๅญ—,้ ่จญ็‚บ0
00=/tmp    ; ็จ‹ๅผๅŸท่กŒ็š„ๆ‰€ๅœจ็›ฎ้Œ„,็›ธ็•ถๆ–ผๅ…ˆcdๅˆฐๆŒ‡ๅฎš็›ฎ้Œ„,็„ถๅพŒๅŸท่กŒ็จ‹ๅผ。
umask=022      ; umask for process (default None)
priority=999     ; ็จ‹ๅผๆ“ไฝœ็š„็š„ๅ„ชๅ…ˆ้ †ๅบ,ไพ‹ๅฆ‚ๅœจstart all/stop all,้ซ˜ๅ„ชๅ…ˆ้ †ๅบ็š„็จ‹ๅผๆœƒๅ…ˆ้—œ้–‰ๅ’Œ้‡ๅ•Ÿ。
autostart=true    ; ๅœจsupervisordๅ•Ÿๅ‹•ๆ™‚่‡ชๅ‹•ๅ•Ÿๅ‹•,้ ่จญ็‚บtrue
startsecs=1     ; ็จ‹ๅผๅ•Ÿๅ‹•ๅ‰็ญ‰ๅพ…ๆ™‚้–“็ญ‰ๅพ…ๆ™‚้–“。้ ่จญ็‚บ1。
startretries=3    ; ๅ˜—่ฉฆ้‡ๅ•Ÿๆœ€ๅคงๆฌกๆ•ธ。้ ่จญ็‚บ3。
autorestart=unexpected  ; ๆ˜ฏๅฆ่‡ชๅ‹•้‡ๅ•Ÿ,ๅฏ้ธๅผ•ๆ•ธ็‚บ false, unexpected, true。ๅฆ‚ๆžœ็‚บfalseๅ‰‡ไธ่‡ชๅ‹•้‡ๅ•Ÿ,ๅฆ‚ๆžœ็‚บunexpected่กจ็คบๅฆ‚ๆžœ็จ‹ๅผ้€€ๅ‡บ่จŠ่™Ÿไธๅœจ `exitcodes` ไธญ,ๅ‰‡่‡ชๅ‹•้‡ๅ•Ÿ。้ ่จญ็‚บunexpected
exitcodes=0,2     ; ็จ‹ๅผ้€€ๅ‡บ็ขผ。้…ๅˆ`autorestart`ไฝฟ็”จ。้ ่จญ็‚บ 0,2
stopsignal=QUIT    ; ๆฎบๆญป็จ‹ๅบๆ˜ฏๅ‚ณ้€็š„่จŠ่™Ÿ,้ ่จญ็‚บTREM。
stopwaitsecs=10    ; ๅ‚ณ้€SIGKILL่จŠ่™Ÿๅ‰ๆœ€ๅคง็ญ‰ๅพ…ๆ™‚้–“。้ ่จญ็‚บ10。
user       ; ไปฅๆŒ‡ๅฎšไฝฟ็”จ่€…่บซไปฝๅ•Ÿๅ‹•็จ‹ๅผ。้ ่จญ็‚บ็•ถๅ‰ไฝฟ็”จ่€…。
stopasgroup=false    ; ๆ˜ฏๅฆๅ‘ๅญ็จ‹ๅบๅ‚ณ้€ๅœๆญข่จŠ่™Ÿ,้€™ๅฐๆ–ผFlask็š„debugๆจกๅผๅพˆๆœ‰็”จ่™•,ๅฆ‚ๆžœ่จญๅฎš็‚บtrue,ๅ‰‡ไธๅ‘ๅญ็จ‹ๅบๅ‚ณ้€ๅœๆญข่จŠ่™Ÿ。้ ่จญ็‚บfalse
killasgroup=false    ; ๆ˜ฏๅฆๅ‘ๅญ็จ‹ๅบๅ‚ณ้€kill่จŠ่™Ÿ,้ ่จญ็‚บfalse
redirect_stderr=false   ; ๅฐ‡้Œฏ่ชค่ผธๅ‡บๅฎšๅ‘ๅˆฐๆจ™ๆบ–่ผธๅ‡บ,้ ่จญ็‚บfalse
stdout_logfile=/a/path  ; ๆจ™ๆบ–่ผธๅ‡บๆ—ฅ่ชŒ่ทฏๅพ‘,ๅฏ้ธๅผ•ๆ•ธ็‚บ `่‡ชๅฎš็พฉ` `AUTO` `NONE`,`่‡ชๅฎš็พฉ`ๅฐ‡ๆ—ฅ่ชŒๅฏซๅˆฐ่‡ชๅฎš็พฉ่ทฏๅพ‘,ๅฏ็”จ็š„่ฎŠๆ•ธๆœ‰`group_name`, `host_node_name`, `process_num`, `program_name`, `here`(้…็ฝฎๆช”ๆกˆ็›ฎ้Œ„);`NONE`ไธๅปบ็ซ‹ๆ—ฅ่ชŒ;`AUTO` ๅˆsupervisord่‡ชๅ‹•้ธๆ“‡่ทฏๅพ‘,ไธฆไธ”็•ถsupervisordๆœๅ‹™้‡ๆ–ฐๅ•Ÿๅ‹•ๆ™‚ๅŽŸไพ†่‡ชๅ‹•ๅปบ็ซ‹็š„ๆ—ฅ่ชŒไปฅๅŠๆ—ฅ่ชŒ็š„ๅ‚™ไปฝๆช”ๆกˆๆœƒ่ขซๅˆช้™ค。้ ่จญ็‚บAUTO
stdout_logfile_maxbytes=1MB ; ๆจ™ๆบ–่ผธๅ‡บๆ—ฅ่ชŒๅ–ฎๅ€‹ๆช”ๆกˆๆœ€ๅคงๅคงๅฐ,ๅฆ‚ๆžœ่ถ…้ŽๆŒ‡ๅฎšๅคงๅฐๆœƒๅฐ‡ๆ—ฅ่ชŒๆช”ๆกˆๅ‚™ไปฝ,ๅฏ็”จ็š„ๅ–ฎไฝ KB MB GB。ๅฆ‚ๆžœ่จญๅฎš็‚บ0ๅ‰‡่กจ็คบไธ้™ๅˆถๆช”ๆกˆๅคงๅฐ。้ ่จญ็‚บ50MB
stdout_logfile_backups=10  ; ๆจ™ๆบ–่ผธๅ‡บๆ—ฅ่ชŒๆช”ๆกˆๆœ€ๅคงๅ‚™ไปฝๆ•ธ。้ ่จญ็‚บ10
stdout_capture_maxbytes=1MB ; ็•ถ็จ‹ๅบ่™•ๆ–ผ“stdout capture mode”ๆจกๅผไธ‹ๅฏซๅ…ฅๅˆฐFIFOไฝ‡ๅˆ—ๆœ€ๅคงไฝๅ…ƒ็ต„ๆ•ธ,ๅฏ็”จๅ–ฎไฝ KB MB GB。้ ่จญ็‚บ0,่ฉณ็ดฐ่ชชๆ˜Ž่ฆ‹[capture-mode](http://supervisord.org/logging.html#capture-mode)
stdout_events_enabled=false ; 
;ไปฅไธ‹้…็ฝฎ้ …้…็ฝฎ้Œฏ่ชค่ผธๅ‡บ็š„ๆ—ฅ่ชŒๅผ•ๆ•ธ。ๅ’ŒไธŠ้ขๆจ™ๆบ–่ผธๅ‡บ้…็ฝฎ็›ธๅŒ。
stderr_logfile=/a/path  ;
stderr_logfile_maxbytes=1MB ;
stderr_logfile_backups=10  ;
stderr_capture_maxbytes=1MB ;
stderr_events_enabled=false ;
environment=A="1",B="2"  ; ็’ฐๅขƒ่ฎŠๆ•ธ่จญๅฎš,ๅฏ็”จ็š„่ฎŠๆ•ธๆœ‰ `group_name`, `host_node_name`, `process_num`, `program_name`, `here`。 ้ ่จญ็‚บ็ฉบ。
serverurl=AUTO    ; override serverurl computation (childutils)

ๅƒ่€ƒ็ถฒ็ซ™

2022/07/15

vagrant mount issue

Vagrant ๅœจๅฎ‰่ฃๅนพๅ€‹ redhat ็›ธ้—œ box ๆ™‚ๅธธๅธธๆœƒ้‡ๅˆฐ mount ๅ‡บๅ•้กŒ,ๅƒๆ˜ฏ:

Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem “vboxsf” is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attempted was:

mount -t vboxsf -o uid=1000,gid=1000 keys /keys

้€™ๆ™‚ๅ€™้€šๅธธๅœจ Vagrantfile ๆทปๅŠ  vbguest ็š„ๆŒ‡ไปคๅฐฑๅฏไปฅ่งฃไบ†。

if Vagrant.has_plugin?("vagrant-vbguest")
    config.vbguest.installer_options = {
        enablerepo: true,
        allow_kernel_upgrade: true
    }
end

่‹ฅๆœ‰ๆ™‚ๅ€™ๆœ‰ๅ…ถไป–ๅ•้กŒ,ๅ‰‡ๅฏไปฅๅŠ ๅ…ฅไธ‹ๅˆ—่ชžๆณ•:

if Vagrant.has_plugin?("vagrant-vbguest")
    config.vbguest.auto_update = false
    config.vbguest.no_remote = true
end

ๆˆ‘ๅœจ ubuntu 21 ไธŠไฝฟ็”จ NFS ๆ™‚้‡ๅˆฐ็„กๆณ• mount ็š„ๅ•้กŒ,ๅŠ ไธŠไบ† nfs_udp: false ๆ‰่งฃๆฑบ,ๅคง้ƒจๅˆ†็‹€ๆณๅœจๅฎ˜ๆ–นๆ–‡ไปถ้ƒฝๆ‰พ็š„ๅˆฐ solution。

ๅฆ‚ๆžœไฝฟ็”จ vagrant ๅš็‚บๆธฌ่ฉฆ็”จ้€”,่€Œ็•ถๅ‰็‰ˆๆœฌๆœ‰ไบ›ๅ•้กŒ็„กๆณ•ๆปฟ่ถณ,ๅฏไปฅ้ธๆ“‡ๅ…ถไป–ไบบ่ฃฝไฝœ็š„็‰ˆๆœฌไพ†็”จ,ไพ‹ๅฆ‚่ชชๆˆ‘ไฝฟ็”จ rockylinux8 ๆธฌ่ฉฆๆ™‚,ๅ› ็‚บไป–็š„ไธป็ฃ็ขŸๅช่ฆๅŠƒ 5GB,ๅฐŽ่‡ดๆ นๆœฌ็„กๆณ•ๅฎŒๆˆๆธฌ่ฉฆ่ฆ็”จ็š„ๅฅ—ไปถๅฎ‰่ฃ,่ฉฆไบ†ๅพˆๅคšๆ–นๅผ่ชฟๆ•ด partition ้ƒฝๅคฑๆ•—,ๆœ€ๅพŒๆ”น็”จ bento/rockylinux-8 ่งฃๆฑบ,ๆ›ๅ€‹้ขจๆ™ฏ็œ‹ไธ–็•Œๅฐฑ้–‹้—Šไบ† QQ。

2022/07/12

multiple checkbox filter for datatables

ๆœ€่ฟ‘ๅœจ็ทด็ฟ’ datatables ้€™ๅ€‹ๅฅฝ็”จ็š„ๅทฅๅ…ท,ไป–ๅฏไปฅๆญ้… jQuery ่ทŸ Bootstrap ่ฎ“ไฝ ๅฏไปฅๅพˆๅฟซ็š„ๅปบ็ซ‹ๆœ‰ RWD ็š„่กจๆ ผ,ไป–ๆœ‰้žๅธธ่ฑๅฏŒ็š„ API ๅฏไปฅๅฎข่ฃฝๅ…งๅฎน,ไปŠๅคฉ็คบ็ฏ„ไธ€ไธ‹ๆ€Ž้บผไฝฟ็”จ checkbox ๅŽป filter ๅ‡บ็ฌฆๅˆ็š„ๅ…งๅฎน。

ไฝฟ็”จๆญฃ่ฆๅผ

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.21/css/jquery.dataTables.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.21/js/jquery.dataTables.min.js"></script>
    <script>
        $(function() {
            let data = [
                [
                    "Chan",
                    "basketball"
                ],
                [
                    "Phoebe",
                    "volleyball"
                ],
                [
                    "Lucas",
                    "tennis"
                ],
                [
                    "LBJ",
                    "basketball"
                ],
            ];
            let table = $('#data_table').DataTable({
                data: data
            });

            $(':checkbox').on('click', function() {
                let options = $(':checkbox:checked').map(function() {
                    return $(this).val()
                }).get();

                if (options.length === 0) {
                    table.columns(1).search('').draw();
                } else {
                    let regex = options.join('|');

                    table.columns(1).search(regex, true, false).draw();
                }
            });
        });
    </script>
</head>
<body>
<div>
    <label> basketball
        <input type="checkbox" value="basketball">
    </label>
    <label> volleyball
        <input type="checkbox" value="volleyball">
    </label>
    <label> tennis
        <input type="checkbox" value="tennis">
    </label>
</div>
<table id="data_table">
    <thead>
    <tr>
        <th>name</th>
        <th>hobby</th>
    </tr>
    </thead>
    <tbody></tbody>
</table>
</body>
</html>

ไฝฟ็”จ extension

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.21/css/jquery.dataTables.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.21/js/jquery.dataTables.min.js"></script>
    <script>
        $(function() {
            let data = [
                [
                    "Chan",
                    "basketball"
                ],
                [
                    "Phoebe",
                    "volleyball"
                ],
                [
                    "Lucas",
                    "tennis"
                ],
                [
                    "LBJ",
                    "basketball"
                ],
            ];
            let table = $('#data_table').DataTable({
                data: data
            });

            $.fn.dataTable.ext.search.push(function(settings, data) {
                let options = $(':checkbox:checked').map(function() {
                    return $(this).val()
                }).get();

                if (options.length === 0) {
                    return true;
                }

                return options.indexOf(data[1]) !== -1;
            });

            $(':checkbox').on('click', function() {
                table.draw();
            });
        });
    </script>
</head>
<body>
<div>
    <label> basketball
        <input type="checkbox" value="basketball">
    </label>
    <label> volleyball
        <input type="checkbox" value="volleyball">
    </label>
    <label> tennis
        <input type="checkbox" value="tennis">
    </label>
</div>
<table id="data_table">
    <thead>
    <tr>
        <th>name</th>
        <th>hobby</th>
    </tr>
    </thead>
    <tbody></tbody>
</table>
</body>
</html>

ไธŠ้ขไฝฟ็”จไบค้›†็š„ๆ–นๆณ•็ฏฉ้ธ,็จๅพฎๆ”นๅฏซไธ€ไธ‹ๅฐฑๅฏไปฅ่ฎŠ่ฏ้›†ไบ†。

2022/07/06

learning latteart opening

่‡ชๅญธๆ‹‰่ŠฑไนŸๅฅฝๅนพๅนดไบ†,่ฒท้Žๅพˆๅคš่ทŸๆ‹‰่Šฑ็›ธ้—œ็š„ๅ™จๆ,ไฝ†็ฉฉๅฎšๆ€งไธ€็›ดไธๆ˜ฏๅพˆๅฅฝ。

https://www.instagram.com/p/Ce-TmSGh8np/

้€™ๆฌก็ต‚ๆ–ผ็—›ไธ‹ๆฑบๅฟƒๅŽปๆ‰พ่€ๅธซไธŠ่ชฒ,survery ไบ†ๅŠ้–“ๅพˆไน…,ๆฒ’ๆƒณๅˆฐ่‡ชๅทฑ็š„ๆ„›ๅบ— Shawn Coffee ๅฐฑๆœ‰้–‹่ชฒ,้€้Ž IG ่ทŸ่€ๅธซ่ฏ็นซไน‹ๅพŒ,็ด„ๅฎšๅฅฝไบ†ไธŠ่ชฒๆ—ฅๆœŸ(ไธๅคชๅฅฝๆŽ’,ๅคช็ด…ไบ†),ไปŠๅคฉๆ˜ฏๆˆ‘็š„็ฌฌไธ€ๅคฉไธŠ่ชฒ,ๆˆ‘้‚„่ ป็ทŠๅผต็š„,้ƒฝๅ››ๅๅนพๆญฒไบ†้‚„่ƒฝๅค ้‡ๅ›žๅญธ็”Ÿๆ™‚ไปฃ็š„ๅญธ็ฟ’ๅˆๅฟƒ่ทŸ้‡ๆบซๆœช็Ÿฅ็š„ๅฟๅฟ‘ๆ„Ÿๆˆ‘่ฆบๅพ—ๅพˆๆฃ’,Wish myself luck。

Compile Apache and OpenSSL

OS: CentOS 8
Target:

  • PHP 7.4
  • PHP-FPM
  • Apache 2.4.46
  • OpenSSL 1.1.1g

Start compile

sudo dnf install install -y gcc gcc-c++ make prec-devel libtool perl-core zlib-devel

# Compile apr
sudo ./configure --prefix=/usr/local/apr
sudo make
sudo make install

# Compile apr-util
sudo ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr
sudo make
sudo make install

# Compile openssl
sudo ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared zlib
sudo make
sudo make install

# Compile apache
sudo ./configure --prefix=/usr/local/apache2446 --enable-so --enable-ssl --enable-cgi --enable-rewrite --enable-modules=most --enable-mpms-shared=all --with-mpm=prefork --with-zlib --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --with-ssl=/usr/local/openssl
sudo make
sudo make install

# Apache system configuration
# Add Apache executables to PATH ( You can use nano instead of vi)
# create and open the following file
sudo vim /etc/profile.d/httpd.sh
  
# paste the following content, save and exit
pathmunge /usr/local/apache2446/bin

# Add Systemd entry

# create and open the following file
sudo vim /etc/systemd/system/httpd.service

paste the following content, save and exit.

[Unit]
Description=The Apache HTTP Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/apache2446/bin/apachectl -k start
ExecReload=/usr/local/apache2446/bin/apachectl -k graceful
ExecStop=/usr/local/apache2446/bin/apachectl -k graceful-stop
PIDFile=/usr/local/apache2446/logs/httpd.pid
PrivateTmp=true

[Install]
WantedBy=multi-user.target
# reload the systemctl daemon
sudo systemctl daemon-reload

# start Apache httpd server (ignore the warnings at this stage)
sudo systemctl start httpd
sudo systemctl enable httpd

# Install PHP
sudo dnf install -y http://rpms.remirepo.net/enterprise/remi-release-8.rpm
dnf module reset php
dnf module install -y php:remi-7.4

sudo service php-fpm start
sudo systemctl enable php-fpm

Reference

2022/07/04

JsRender Sample

็พๅœจๅ‰็ซฏ framework ็™พๅฎถ็ˆญ้ณด,ไธ้Žๅ’ŒๅพŒ็ซฏ framework
้‡ๅˆฐไธ€ๆจฃ็š„ๅ•้กŒ,็‰ˆๆœฌๆ›ดๅ‹•่ถ…ๅฟซๅฏซๆณ•ไธ็›ธๅฎน,ไปŠๆ—ฅ็š„็•ถ็ด…็‚ธๅญๆฉŸ็›ธ้š”ไธ€ๅนดๅพŒๆถˆๅคฑ็š„็„กๅฝฑ็„ก่นคๆ™‚ๆœ‰ๆ‰€่ž,ๅพŒ็ซฏๆฏ”่ผƒๆฒ’ๆœ‰่ฟ…้€Ÿ้š•่ฝ็š„ๅ•้กŒ,็•ข็ซŸไป–็š„้–‹็™ผ้–€ๆชป้‚„ๆ˜ฏ็จ้ซ˜ไธ€ไบ›,ไนŸๅ› ๆญค้™คไบ†ๆœ‰็Žฉไธ€ไธ‹ vuejs ไปฅๅค–ๅฐ
front-end framework ็œŸ็š„ๆฒ’ๆœ‰ๅคช้ซ˜็š„่ˆˆ่ถฃ,ๅณไพฟๆˆ‘่ถ…ๆ„›ๅฏซ JavaScript,ไธ้Žๅฐˆๆกˆ่ฃก้ข้‚„ๆปฟๅธธ็”จๅˆฐ js render template ้€™ไปถไบ‹ๆƒ…,ๆ‰€ไปฅๆˆ‘้‚„ๆ˜ฏๆœ‰ไฝฟ็”จไธ€ไบ› plugin
ไพ†ๅฏฆ็พ้€™ไปถไบ‹ๆƒ…,ไปŠๅคฉๅฐฑไพ†ไป‹็ดน JsRender ้€™ๅ€‹ plugin

ๅŸบ็คŽๅŠŸ่ƒฝ

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JsRender</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.88/jsrender.min.js"></script>
    <script>
        $(function() {
            var data = {name: 'chan'};
            var html = $.templates('<strong>{{:name}}</strong>').render(data);
            $('#app').append(html);
        });
    </script>
</head>
<body>
<div id="app"></div>
</body>
</html>

JsRender ๅ‚ณ้€ฒๅŽป็š„่ณ‡ๆ–™็‚บ JSON,ไธŠ้ข็š„ๅ…งๅฎนๆœƒๅพ—ๅˆฐ:

<div id="app"><strong>chan</strong></div>

ๅฆ‚ๆžœๆ˜ฏ่ค‡ๆ•ธ็š„ๅ…งๅฎน,JsRender ๆœƒ่‡ชๅ‹•ๅนซไฝ ๅš repeat。

script
$(function() {
    let data = [
        {name: 'chan'},
        {name: 'phoebe'}
    ];
    let html = $.templates('<strong>{{:name}}</strong>').render(data);
    $('#app').append(html);
});
output

<div id="app"><strong>chan</strong><strong>phoebe</strong></div>

ๆจฃๆฟ

ๅฆ‚ๆžœๅฐ‡ๆจฃๆฟๅฏซๅœจ script ่ฃก้ข,ๅพ€ๅพ€ๆœƒ่ฎŠๅพ—ๅพˆ้›ฃ็ถญ่ญท,ๅญ—ไธฒไนŸ่ถ…้•ท,ๆ‰€ไปฅ JsRender ๆœ‰ไธ€ๅ€‹ๆŠŠๆจฃๆฟ้š”้›ขๅ‡บไพ†็š„ๅฅ—็”จๆ–นๅผ。

script
$(function() {
    let data = [
        {name: 'chan'},
        {name: 'phoebe'}
    ];
    let html = $.templates('#info').render(data);

    $('#app').append(html);
});
html
<script id="info" type="text/html">
    <strong>{{:name}}</strong>
</script>
output
<div id="app">
    <strong>chan</strong>
    <strong>phoebe</strong>
</div>

ๅ…งๅปบๅ‡ฝๅผ

{{> …}} (HTML-encode)

ๅฐ‡่ผธๅ‡บ็š„้ƒจไปฝๅŠ ไธŠ > ็š„่ฉฑๆœƒๅฐ‡็จ‹ๅผ็ขผ encode。

script
$(function() {
    let data = {link: '<a href="http://www.google.com">google</a>'};
    let html = $.templates('#info').render(data);

    $('#app').append(html);
});
html
<script id="info" type="text/html">
    {{>link}}
</script>
output
&lt;a href&#61;&#34;http://www.google.com&#34;&gt;google&lt;/a&gt;

{{if …}}

ๅˆคๆ–ทๅผ。

script
$(function() {
    let data = [
        {name: 'chan', age: 37},
        {name: 'phoebe', age: 38},
        {name: 'lucas', age: 2}
    ];
    let html = $.templates('#info').render(data);

    $('#app').append(html);
});
html
<script id="info" type="text/html">
    {{:name}} is {{if age > 10}}adult{{else}}children{{/if}}<br>
</script>
output
<div id="app">
    chan is adult<br>
    phoebe is adult<br>
    lucas is children<br>
</div>

{{for …}}

่ฟดๅœˆ。

script
$(function() {
    let data = [{
        name: 'chan',
        likes: [{
            name: 'music'
        }, {
            name: 'movie'
        }]
    }, {
        name: 'phoebe'
    }, {
        name: 'lucas',
        likes: [{
            name: 'cartoon'
        }]
    }];
    let html = $.templates('#info').render(data);

    $('#app').append(html);
});
html
<script id="info" type="text/html">
    <div>
        {{:#index}}: {{:name}}
        {{if likes}}
        <ul>
            {{for likes}}
            <li>{{:#index + 1}}: {{:name}}</li>
            {{/for}}
        </ul>
        {{else}}
        <div>no likes</div>
        {{/if}}
    </div>
</script>
output
<div id="app">
    <div>
        0: chan
        <ul>
            <li>1: music</li>
            <li>2: movie</li>
        </ul>
    </div>
    <div>
        1: phoebe
        <div>no likes</div>
    </div>
    <div>
        2: lucas
        <ul>
            <li>1: cartoon</li>
        </ul>
    </div>
</div>

{{include …}}

ๅผ•ๅ…ฅๅ…ถไป–็š„ๆจฃๆฟ。

script
$(function() {
    let data = {
        name: 'test',
        data: [
            {
                name: 'chan',
                age: 42
            },
            {
                name: 'phoebe',
                age: 42
            }
        ]
    };

    console.log($('#main').render(data));
});

html
<script id="main" type="text/html">
    <h3>{{:name}}</h3>
    {{include tmpl="#sub"/}}
</script>
<script id="sub" type="text/html">
    <ol>
        {{for data}}
        <li>{{:name}}: {{:age}}</li>
        {{/for}}
    </ol>
</script>

output
<h3>test</h3>
<ol>
    <li>chan: 42</li>
    <li>phoebe: 42</li>
</ol>

{{props …}}

key value ๅผ็š„่ฟดๅœˆ。

script
$(function() {
    var data = [
        {
            name: 'Chan',
            likes: {
                name: 'a1',
                age: 33
            }
        },
        {
            name: 'Phoebe',
            likes: {
                food: 'steak',
                drink: 'juice'
            }
        }
    ]
    var html = $.templates('#info').render(data);

    $('#app').html(html);
});
html
<script id="info" type="text/html">
    <div>
        {{:name}}
        <ul>
            {{props likes}}
            <li>{{:key}}: {{:prop}}</li>
            {{/props}}
        </ul>
    </div>
</script>
output
<div id="app">
    <div>
        Chan
        <ul>
            <li>name: a1</li>
            <li>age: 33</li>
        </ul>
    </div>
    <div>
        Phoebe
        <ul>
            <li>food: steak</li>
            <li>drink: juice</li>
        </ul>
    </div>
</div>

helper

helper ไนŸๆ˜ฏไธ€็จฎ JSON ๆ ผๅผ,ๅฏไปฅๆ˜ฏๆ–‡ๅญ—ๆˆ–่€…ๆ˜ฏ function。

script
$(function() {
    var helpers = {
        prefix: function(val) {
            return val + ' is cool';
        },
        version: '1.1'
    };
    var data = {
        name: 'chan'
    };
    var html = $.templates('#info').render(data, helpers);

    $('#app').append(html);
});
html

<script id="info" type="text/html">
    version: {{:~version}} name: {{:~prefix(name)}}
</script>
output
<div id="app">
    version: 1.1 name: chan is cool
</div>

helper ้‚„ๆœ‰ๅฆไธ€็จฎ global ็š„ๅ‚ณๅ…ฅๆ–นๅผ。

script
$(function() {
    let helpers = {
        prefix: function(val) {
            return val + ' is cool';
        },
        version: '1.1'
    };
    $.views.helpers(helpers);
    let data = {
        name: 'chan'
    };
    let html = $.templates('#info').render(data);

    $('#app').append(html);
});

ไปฅไธŠๆ˜ฏๆœ€ๅŸบๆœฌ็š„็ฏ„ไพ‹,ไธ้Žๆ‡‰่ฉฒๅฏไปฅๆ‡‰ไป˜ๅคง้ƒจๅˆ†็š„ๆƒ…ๆณไบ†,่‹ฅ่ฆไฝฟ็”จๅ…ถไป–ๆ›ด่ฉณ็›ก็š„ๅŠŸ่ƒฝๅฏไปฅๅ‰ๅพ€ไป–็š„ๅฎ˜็ถฒ https://www.jsviews.com/