` 中的每一個 Class Name,其代表的 CSS 屬性如下
22 |
23 | ```css
24 | .rounded-xl {
25 | border-radius: var(--radius-xl);
26 | }
27 |
28 | .bg-gray-50 {
29 | background-color: var(--color-gray-50);
30 | }
31 |
32 | .p-4 {
33 | padding: calc(var(--spacing) * 4);
34 | }
35 | ```
36 |
37 | 可以看到每一個 Class Name 都只提供很簡單的 CSS 樣式。
38 | 因此使用者可以從名稱很直觀的知道這個 Class Name 提供了什麼樣的 CSS 效果。
39 |
--------------------------------------------------------------------------------
/pest/09-groups.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Pest
4 | nav_order: 9
5 | ---
6 |
7 | # 使用 Group 幫測試建立群組
8 |
9 | 你可以使用 `group()` 將多個測試放進一個群組中
10 |
11 | ```php
12 | it('can validate an email', function () {
13 | $rule = new IsValidEmailAddress();
14 |
15 | expect($rule->passes('email', 'allen@hello.com'))->toBeTrue();
16 | })->group('validation');
17 | ```
18 |
19 | 這樣執行測試時就可以使用 `--group` 來執行指定群組的測試
20 |
21 | > group() 是可以跨檔案的,你可以把位於不同檔案的測試放進同一個群組中
22 |
23 | ```bash
24 | php artisan test --group=validation
25 | ```
26 |
27 | 如果檔案內全部測試都屬於同一個群組,我們在檔案的上方使用下面的語法,這樣該檔案中的全部測試都會被加入 `validation` 群組中
28 |
29 | ```php
30 | uses()->group('validation');
31 | ```
32 |
33 | 如果不想要執行某群組的測試,可以在指令中使用 `--exclude`
34 |
35 | ```bash
36 | php artisan test --exclude=validation
37 | ```
38 |
--------------------------------------------------------------------------------
/network/06-static-route.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Network
4 | nav_order: 6
5 | ---
6 |
7 | # Static Route
8 |
9 | 靜態路由是一種手動設定的路由,通常用於小型網路或是網路設備。靜態路由的優點是簡單、容易設定,但缺點是不具備自動更新的功能,需要手動維護。
10 |
11 | 你可以使用 `ip route` 指令來設定靜態路由,例如:
12 |
13 | ```bash
14 | ip route add 172.16.11.13/32 via 192.168.0.1
15 | ```
16 |
17 | 也可以透過 `dev` 參數來指定網路介面:
18 |
19 | ```bash
20 | ip route add 172.16.11.13/32 dev eth0
21 | ```
22 |
23 | ## DHCP Lease 會清除靜態路由
24 |
25 | 跟 DHCP 租借而來的 IP 地址有一定的期限。**時間一到,伺服器會重新向 DHCP 重新取得 IP 位址,並重新啟動網卡,這個過程會讓原本設定的靜態路由被清除**。如果你希望靜態路由能夠在 IP 位址變更後仍然保留,可以寫增加靜態路由的一個腳本,並放入 `/usr/lib/networkd-dispatcher/routable.d` 目錄中。
26 |
27 | ```bash
28 | #!/bin/bash
29 |
30 | # 撰寫一個簡單的腳本,用來設定靜態路由,並放入 /usr/lib/networkd-dispatcher/routable.d 目錄
31 | ip route add 172.16.11.13/32 via 192.168.0.1
32 | ```
33 |
34 | 當網卡重新啟動時,這個腳本會被執行,並重新設定靜態路由。
35 |
--------------------------------------------------------------------------------
/php/04-shell-exec.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: PHP
4 | nav_order: 4
5 | ---
6 |
7 | # Shell Exec
8 |
9 | 在 PHP 中,你可以使用 `exec()` 來執行外部指令。
10 |
11 | ```php
12 | $output=null;
13 | $exitCode=null;
14 | // 在可以執行 whoami 指令的機器上,執行 whoami 指令
15 | exec('whoami', $output, $exitCode);
16 | echo "Returned with status $exitCode and output:\n";
17 | print_r($output);
18 |
19 | // 結果
20 | // Returned with status 0 and output:
21 | // Array
22 | // (
23 | // [0] => allen
24 | // )
25 | ```
26 |
27 | PHP 其實提供另外一種更方便的方式來執行外部指令 - 反引號。
28 |
29 | ```php
30 | $output = `whoami`;
31 |
32 | echo $output; // allen
33 | ```
34 |
35 | 如果你擔心安全問題,想要關閉這個功能,可以在 `php.ini` 加入以下這行
36 |
37 | ```ini
38 | disable_functions = "shell_exec"
39 | ```
40 |
41 | ## 參考資料
42 |
43 | - [Execution Operators](https://www.php.net/manual/en/language.operators.execution.php)
44 |
--------------------------------------------------------------------------------
/svelte/15-context-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 15
5 | ---
6 |
7 | # Context API
8 |
9 | ## `setContext` and `getContext`
10 |
11 | Context API 為元件提供了一種相互「對話」的機制,無需將資料和函數作為 props 傳遞,也無需調度大量事件。
12 |
13 | ```svelte
14 |
15 |
26 | ```
27 |
28 | ```svelte
29 |
30 |
37 |
38 |
41 | ```
42 |
--------------------------------------------------------------------------------
/aws/08-certificate.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 8
5 | ---
6 |
7 | # AWS Certificate Service
8 |
9 | AWS 相關的憑證管理服務。
10 |
11 | ## AWS Certificate Manager (ACM)
12 |
13 | - AWS Certificate Manager (ACM) 主要用來管理 SSL/TLS 憑證,憑證可以用在下面這些服務:
14 | - Elastic Load Balancer (ELB)
15 | - Cloudfront
16 | - API Gateway
17 | - ACM 支援自動更新憑證。
18 | - 根據安全考量,ACM 中的憑證無法匯出。
19 | - 你可以自己與 CA 申請憑證,然後匯入 ACM 中,但這樣就不支援自動更新憑證。
20 |
21 | ## AWS Private CA
22 |
23 | AWS Private CA 是一種高度可用的多功能 CA (Certificate Authority),可協助組織使用私有憑證來保護其應用程式和裝置的安全。
24 |
25 | ## AWS CloudHSM
26 |
27 | - AWS CloudHSM 可讓你在經過 FIPS (Federal Information Processing Standard) 驗證的硬體上管理和存取您的金鑰。可用來與 KMS 一起使用。
28 | - 如果 HSM 出現問題導致金鑰遺失,AWS 也無法幫用戶找回金鑰。因此 AWS 建議使用 Multi-AZ 來避免這種情況發生。
29 |
30 | > [!NOTE]
31 | >
32 | > HSM (Hardware security module) 又稱硬體安全模組,是一種用於保障和管理強認證系統所使用的數字金鑰,並同時提供相關密碼學操作的電腦硬體裝置。
33 |
--------------------------------------------------------------------------------
/cloudflare/01-november-2025-outage.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Cloudflare
4 | nav_order: 1
5 | ---
6 |
7 | # Cloudflare 2025-11 全球性故障案例研究
8 |
9 | 台灣時間 2025 年 11 月 18 日,Cloudflare 發生了一次全球性的服務中斷事件,影響了數百萬網站和應用程式的可用性。這次故障持續了數個小時,導致大量用戶訪問受 Cloudflare 保護的網站時遇到 500 錯誤。
10 |
11 | ## 故障原因總結
12 |
13 | 1. 想修改權限,結果分散式資料庫的查詢語法有誤,導致從多個資料庫來源讀取資料。
14 | 2. 根據資料庫查詢產出的特徵檔案(feature file)有多個重複的值(內容相同但資料庫來源不同的重複資料)。
15 | 3. 機器人管理服務(使用 Rust 編寫)在讀取這些特徵檔案時,撞到特徵載入數量的上限(最多 200 個)。
16 | 4. 機器人管理服務某段程式碼沒寫好,並沒有處理這個例外,導致程式 Panic 並引發程式崩潰。
17 | 5. 機器人程式是 Cloudflare Proxy 的核心功能,結果程式崩潰導致整個 Proxy 服務無法運作。
18 | 6. Proxy 服務無法運作,導致所有受 Cloudflare 保護的網站都無法訪問,並顯示 500 錯誤。
19 |
20 | ## 後續影響
21 |
22 | - 機器人管理服務崩潰,每個訪問網站的用戶 0 分,判定每個用戶都是機器人。
23 | - 用戶訪問受 Cloudflare 保護的網站以及他們的 CDN 節點,都會看到 500 錯誤。
24 | - 他們內部服務也會使用自己的 Proxy,導致內部服務無法運作。
25 |
26 | ## 參考資料
27 |
28 | - [Cloudflare 2025-11-18 Global Outage Case Study](https://blog.cloudflare.com/18-november-2025-outage/)
29 |
--------------------------------------------------------------------------------
/aws/21-cloudfront.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 21
5 | ---
6 |
7 | # CloudFront (AWS CDN Service)
8 |
9 | CloudFront 是 AWS 的 CDN (Content Delivery Network) 服務,可以用來加速靜態和動態的 Web 內容分發。CloudFront 會將你的內容複製到多個位於全球各地的 Edge Location,當用戶訪問你的網站時,會自動將用戶導向到離用戶最近的 Edge Location,這樣可以減少網頁載入的時間。
10 |
11 | ## SAA 筆記
12 |
13 | - Amazon CloudFront 是一項可加快向使用者分發靜態和動態 Web 內容(例如 .html、.css、.js 和映像檔)的服務
14 | - 可以設定多個 origin group,並且設定 failover,當 origin group 1 掛掉時,會自動切換到 origin group 2
15 | - 如果想在 CloudFront 限制用戶訪問你的內容,可以使用
16 | - Signed URLs:支援 Adobe's Real-Time Message Protocol (RTMP)
17 | - Signed cookies:不想更改 URL 可以用 cookie
18 | - Origin Access Identity (OAI)
19 | - Origin Access Control (OAC),官方建議使用 OAC,其支援所有地區
20 | - CloudFront geo-restriction 是用來避免部分地區的用戶訪問你的 CDN 內容
21 | - Lambda@Edge 可以讓你在各個 CDN 端點使用程式做一些小任務,例如登入
22 | - Lambda@Edge 後面可接 Kinesis 來即時處理資料
23 | - 可以透過設置 `Cache-Control max-age` 的值來設定 CloudFront 的 cache 時間
24 |
--------------------------------------------------------------------------------
/database/05-languages.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Database
4 | nav_order: 5
5 | ---
6 |
7 | # 資料庫中的各種語句
8 |
9 | 簡單記錄一下資料庫中常看到的 DDL、DML、DQL、DCL 與 TCL,這些簡寫是什麼意思?
10 |
11 | ## DDL (Data Definition Language)
12 |
13 | 資料定義語言,用於定義和管理 SQL 資料庫中的所有物件的語句。
14 |
15 | - `CREATE`,建立一個物件,如資料庫與資料表。
16 | - `ALTER`,修改資料表結構。
17 | - `DROP`,刪除資料表。
18 | - `TRUNCATE`,清除資料表所有資料。
19 |
20 | ## DML (Data Manipulation Language)
21 |
22 | 資料操作語言,分別是 `UPDATE`、`INSERT` 與 `DELETE` 這些用來修改資料的語句。
23 |
24 | ## DQL (Data Query Language)
25 |
26 | 資料查詢語言,指的是 `SELECT` 這個用來查詢資料的語句,不會對資料進行修改。
27 |
28 | ## DCL (Data Control Language)
29 |
30 | 資料控制語言,是用來設置或更改資料庫使用者或角色許可權的語句。
31 | 包括 `GRANT`、`DENY` 與 `REVOKE`。
32 | 在預設狀態下,只有 sysadmin、dbcreator、db_owner 或 db_securityadmin 等人員才有權力執行。
33 |
34 | ## TCL (Transaction Control Language)
35 |
36 | 事務控制語言,有以下幾種語句。
37 |
38 | - `SAVEPOINT`,設置保存點。
39 | - `ROLLBACK`,回滾。
40 | - `SET TRANSACTION`,開始事務。
41 | - `COMMIT`,提交事務。
42 |
--------------------------------------------------------------------------------
/docker/03-prune.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Docker
4 | nav_order: 3
5 | ---
6 |
7 | # 清理用不到的 Docker 資源 (容器、映像檔與資料存儲)
8 |
9 | 在使用 `docker build` 多次建立 image 時,即使 `image:tag` 相同,先前 build 出來的 image 也不會被刪除,而是呈現 untag 的狀態
10 |
11 | 這時我們可以用下方的指令查看 dangling images
12 |
13 | ```shell
14 | docker images --filter "dangling=true"
15 | ```
16 |
17 | 移除 dangling 的 images
18 |
19 | ```shell
20 | docker image prune
21 | ```
22 |
23 | 剛 pull 下來但沒有建立容器的 image 不算是 dangling images,而是 unused images,如果也想清除這些 images 的話,可以使用下面的指令
24 |
25 | ```shell
26 | docker image prune -a
27 | ```
28 |
29 | 移除所有沒用到的 Docker 物件。會連同停用的容器、無用的網路、無用的映像檔案與無用的建置快取全部刪除
30 |
31 | ```shell
32 | docker system prune -a
33 | ```
34 |
35 | 當 Docker 資料存儲不再被容器使用時,可以使用下方的指令清理這些不再使用的 Docker 資料存儲
36 |
37 | ```shell
38 | docker volume prune
39 | ```
40 |
41 | ## 參考資料
42 |
43 | - [如何快速刪除所有已經無用的 Docker 資源 (容器,容器映像,網路)](https://blog.miniasp.com/post/2022/11/18/docker-image-prune-notes)
44 |
--------------------------------------------------------------------------------
/git/02-stash.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Git
4 | nav_order: 2
5 | ---
6 |
7 | # 使用 Stash 來暫存目前的變更
8 |
9 | 將目前所有修改加入暫存。
10 |
11 | ```bash
12 | git stash
13 | ```
14 |
15 | 暫存可以使用註解,方便查找。
16 |
17 | ```bash
18 | git stash save "save message"
19 | ```
20 |
21 | 查看所有暫存。
22 |
23 | ```bash
24 | git stash list
25 | ```
26 |
27 | 顯示最新的暫存。
28 |
29 | ```bash
30 | git stash show
31 | ```
32 |
33 | 顯示第二新的暫存,根據 `{}` 中的數字以此類推。
34 |
35 | ```bash
36 | git stash show stash@{1}
37 | ```
38 |
39 | 顯示最新暫存的改動。
40 |
41 | ```bash
42 | git stash show -p
43 | ```
44 |
45 | 顯示第二新的暫存的改動,根據 `{}` 中的數字以此類推。
46 |
47 | ```bash
48 | git stash show -p stash@{1}
49 | ```
50 |
51 | 套用暫存,並將此暫存從暫存列表中刪除。
52 |
53 | ```bash
54 | git stash pop
55 | ```
56 |
57 | 套用暫存,但是不將此暫存從暫存列表中移除。
58 |
59 | ```bash
60 | git stash apply
61 | ```
62 |
63 | 丟棄暫存。
64 |
65 | ```bash
66 | git stash drop
67 | ```
68 |
69 | 刪除全部暫存。
70 |
71 | ```bash
72 | git stash clear
73 | ```
74 |
--------------------------------------------------------------------------------
/svelte/20-global-state.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 20
5 | ---
6 |
7 | # Global state
8 |
9 | Svelte 5 的 `$state` 除了可以在元件內部使用之外,還可以用來創建全域狀態 (global state),讓你可以在多個元件之間共享狀態。
10 |
11 | 首先建立一個 `shared.svelte.js` 檔案,並在其中使用 `$state` 來定義全域狀態:
12 |
13 | ```javascript
14 | // shared.svelte.js
15 | export const counter = $state({
16 | count: 0,
17 | });
18 | ```
19 |
20 | 在其他元件中,你可以匯入這個全域狀態並使用它:
21 |
22 | ```svelte
23 |
30 |
31 |
34 | ```
35 |
36 | ## 參考資料
37 |
38 | - [Universal Reactivity - Basic Svelte](https://svelte.dev/tutorial/svelte/universal-reactivity)
39 | - [Runes and Global state: do's and don'ts](https://mainmatter.com/blog/2025/03/11/global-state-in-svelte-5/)
40 |
--------------------------------------------------------------------------------
/aws/14-auto-scaling-group.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 14
5 | ---
6 |
7 | # Auto Scaling Group (ASG)
8 |
9 | - ASG 的 Policy 種類:
10 | - **Target tracking scaling policy**:允許你指定目標值,ASG 將盡力保持 EC2 實例數量以滿足這個目標值。例如平均 CPU 使用率維持在 50%,或是平均網路流量維持在 100MB。
11 | - **Simple scaling policy**:最基本的自動擴展策略。它根據 CloudWatch 指標的閾值進行擴展和縮減。例如 CPU 使用率超過 50% 時,擴展 2 台機器,CPU 使用率低於 20% 時,縮減 1 台機器。
12 | - **Step scaling policy**:允許你在多個指標範圍定義的區域進行擴展和縮減,以應對不同負載情境。例如 CPU 50% 時,擴展 2 台機器,CPU 80% 時,擴展 4 台機器。
13 | - **Scheduled scaling policy**:如果你早就知道什麼時候會有較大的流量,可以使用此策略,在大流量的時間點前擴展機器。
14 | - **Predictive scaling policy**:如果你想讓 AWS 使用機器學習,根據過去幫你預測未來流量,可以使用此策略。
15 | - ASG 在做 scale in 時,會優先關掉**啟動設定檔最舊**的機器,如果是在 Multi-AZ 的架構下,會優先選擇擁有最多機器的 AZ。
16 | - ASG 在一次 scale in/out 活動之後會有一段冷卻期 (cool down period),冷卻期內即使達到 scale in/out 條件,ASG 也不會有任何動作。冷卻期的預設時間是 300 秒。
17 | - 可以使用 CloudWatch Alarm 來觸發 ASG 的 scale in/out。
18 | - ASG 定義好 launch template 後就無法在修改,只能新增一個新的。
19 |
--------------------------------------------------------------------------------
/rust/02-variable.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Rust
4 | nav_order: 2
5 | ---
6 |
7 | # 變數
8 |
9 | ## 變數與常量
10 |
11 | Rust 的變數默認為不可變 (immutable)。
12 |
13 | ```rust
14 | let x = 10;
15 | x = 5; // cannot assign twice to immutable variable
16 | ```
17 |
18 | 想使變數可變可以加上 `mut` 關鍵字。
19 |
20 | ```rust
21 | let mut x = 10;
22 | x = 5;
23 | ```
24 |
25 | 可以使用 `const` 聲明常量,常量不允許改變。
26 |
27 | 在宣告的作用域中,常量在整個程式的生命週期都有效,因此常用來做全局範圍的固定值。
28 |
29 | ```rust
30 | const MAX_POINT: u32 = 100_000;
31 | ```
32 |
33 | ## 隱藏 (Shadowing)
34 |
35 | 可以使用 `let` 重複宣告同一個變數,此時第一個變數會被**隱藏**。
36 |
37 | 與 `mut` 不同的地方是,隱藏其實是建立一個新的變數,只是變數名稱與別人相同而已,因此類型上也可以是全新的。
38 |
39 | 隱藏的好處在於對於意思類似但類型不同的變數,我們可以不用特地再去想一個新的變數名稱。
40 |
41 | ```rust
42 | let spaces = " ";
43 | let spaces = spaces.len()
44 | ```
45 |
46 | 如果改用可變變數就會有問題,因為第二次賦值時,變數類型並不一樣。
47 |
48 | ```rust
49 | let mut spaces = " ";
50 | spaces = spaces.len(); // expected type `&str` found type `usize`
51 | ```
52 |
--------------------------------------------------------------------------------
/aws/12-api-gateway.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 12
5 | ---
6 |
7 | # API Gateway
8 |
9 | - 可以將回傳結果做 cache,減少後面 server 或是 Lambda 的負擔
10 | - API Gateway 可以很輕鬆的做到金絲雀部署 (canary deployment),金絲雀部署是指先讓一小部分的用戶測試新功能,如果沒問題再逐步擴大至全部用戶
11 | - 藍綠部署是指同時有新版本與舊版本,並且可以很方面的快速切換,但同一時間只會有一個環境在線上
12 | - API Gateway 可以調整流量限制 (throttling)
13 | - API gateway 只收資料傳輸量的費用,即 pay as you go
14 | - 可以根據 path 將請求送到不同的 backend
15 | - 可以根據 query string 將請求送到不同的 backend
16 |
17 | ## Burst Limit VS Rate Limit
18 |
19 | API Gate 可以調整流量限制 (Throttling),可以在左側選單中選擇 Protect 底下的 Throttling,其中又分為 Burst Limit 與 Rate Limit。
20 |
21 | - Burst Limit 代表你的 API 在同一時間最多可以處理幾個請求。
22 | - Rate Limit 代表你的 API 在一秒鐘內最多可以處理幾個請求。
23 |
24 | > [!NOTE]
25 | >
26 | > 所謂的 API Throttling,其實是一種用來限制用戶 API 請求數量的方式,用以保護後端不被大量請求癱瘓
27 | >
28 | > API Throttling 有多種策略,最常見的策略是限制使用者在一定時間內可以發出的 API 請求數量。
29 | > 除此之外還有令牌桶策略,設定一個有上限數量的令牌桶,令牌重新生成的速率固定,
30 | > 用戶每發一次請求都會消耗一個令牌,令牌沒了就要等令牌重新生成才能發請求。
31 | > 令牌桶策略可以允許偶發性的大量請求出現。
32 |
--------------------------------------------------------------------------------
/pest/02-ide-plugin.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Pest
4 | nav_order: 2
5 | ---
6 |
7 | # IDE Plugin
8 |
9 | Pest 官方有提供多種 IDE 的 plugin,詳細可以上官方網站查看 : [Pest - IDE plugins](https://pestphp.com/docs/ide-plugins)
10 |
11 | ## PhpStorm 的功能
12 |
13 | - 安裝好 plugin 之後,如果你測試檔案是用 Pest 寫的,那麼 icon 就會變成 Pest 的 icon
14 | - 每個 Pest 測試檔案的最上方都會有一個按鈕可以讓你直接執行該檔案的測試
15 | - 此外點選 Menu -> Navigate -> File Structure 就可以查看該測試檔案所包含的所有測試
16 |
17 | - Mac 快捷鍵為 `Cmd + F12`
18 | - Windows 快捷鍵為 `Ctrl + F12`
19 |
20 | - 可以使用 `Shift x 2` 直接搜尋測試名稱並跳轉
21 | - 支援 Pest 的 `expect()` API,如果你在 `Pest.php` 中有客製的 `expect` 方法,這個 plugin 會在 autocompletion 中顯示
22 | - 在 Laravel 的 `tests` 資料夾上,可以點選右鍵選擇 More Run/Debug -> Run 'tests(Pest) with Coverage' 執行測試並顯示覆蓋度
23 | - 測試執行完畢之後,會在檔案列表中顯示每個檔案的測試覆蓋度
24 | - 在 Menu -> Run -> Hide coverage 中可以關閉檔案列表測試覆蓋度的顯示
25 |
26 | ## 參考資料
27 |
28 | [](https://www.youtube.com/watch?v=umVUEq4yGIQ)
29 |
--------------------------------------------------------------------------------
/k8s/03-roloader.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Kubernetes (K8s)
4 | nav_order: 3
5 | ---
6 |
7 | # Reloader
8 |
9 | 為開源專案,當更新 `ConfigMap` 與 `Secret` 時,會重新建立與之相依的 `Pod`,讓 `Pod` 使用最新的設定。
10 |
11 | ## 概念
12 |
13 | Reloader 會在叢集中執行一個 controller,這個 controller 會不斷的檢查 `ConfigMap` 與 `Secret` 是否有更新,
14 | 如果有的話,就會滾動更新 (rolling update) 與之相依的 Pod。
15 |
16 | ## 部署 Reloader 到 K3s 中
17 |
18 | 可以使用官方的配置清單部署。
19 |
20 | ```shell
21 | kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
22 | ```
23 |
24 | ## 修改服務的配置清單,啟動 Reloader 的檢查機制
25 |
26 | 如果你有一個 `Deployment`,只要在他的配置清單加上下方的 `annotations`,當這個 `Deployment` 使用的 `ConfigMap` 與 `Secret` 有更新時,
27 | 就會觸發這個 `Deployment` 的滾動更新。
28 |
29 | ```yaml
30 | kind: Deployment
31 | metadata:
32 | annotations:
33 | reloader.stakater.com/auto: "true"
34 | spec:
35 | template:
36 | metadata:
37 | ```
38 |
39 | ## 參考資料
40 |
41 | - [GitHub - stakater/Reloader](https://github.com/stakater/Reloader)
42 |
--------------------------------------------------------------------------------
/network/08-data-plane-and-control-plane.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Network
4 | nav_order: 8
5 | ---
6 |
7 | # Data Plane 與 Control Plane
8 |
9 | TL;DR。Data Plane (資料平面) 講的是資料傳輸的部分,讓封包從一個點傳送到另外一個點。
10 | Control Plane (控制平面) 講的是資料該如何傳輸的部分,它決定了封包在傳輸過程應該如何被轉發,以及如何根據網路變化做出反應。
11 |
12 | 當 Control Plane 制定好路由表,Data Plane 就是考慮該如何根據路由表將封包傳送到目的地。
13 |
14 | ## Control Plane
15 |
16 | 舉個例子,Border Gateway Protocol (BGP)、Intermediate System-to-Intermediate System (IS-IS) 與 Open Shortest Path First (OSPF) 都是很常見的 Control Plane 協定。
17 |
18 | ## Data Plane
19 |
20 | 俺就只負責送資料,看你要用實體線路送,還是用 Wi-Fi。我會依照 Control Plane 制定的規則來送資料。
21 |
22 | ## Data Plane 與 Control Plane 應該分開
23 |
24 | 在設計上,Data Plane 與 Control Plane 應該分開,並分別實作 HA 與保持彈性。
25 |
26 | 當 Control Plane 發生問題時,Data Plane 依然能夠正常運作,反之亦然。
27 |
28 | 舉例來說,當有個 Load Balancer (Control Plane) 出問題,設計上應該確保這不會影響到資料的傳輸,
29 | 因為傳輸可能會轉給另外一個 Load Balancer 來處理。
30 |
31 | ## 參考資料
32 |
33 | - [Control plane vs. data plane](https://www.ibm.com/think/topics/control-plane-vs-data-plane)
34 |
--------------------------------------------------------------------------------
/aws/01-aws-cli.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 1
5 | ---
6 |
7 | # AWS CLI
8 |
9 | AWS CLI 是一個用來操作 AWS 的指令列工具,可以用來建立還有管理 EC2、S3、IAM 等服務。
10 |
11 | ## 安裝
12 |
13 | 在 Mac 上可以透過 Homebrew 安裝:
14 |
15 | ```bash
16 | brew install awscli
17 | ```
18 |
19 | ## 設定權限
20 |
21 | 要使用 AWS CLI 操作 AWS 服務,必須要有一組 Access Key 跟 Secret Key,可以在 AWS Console 的 IAM 服務裡面建立。然後在本機上執行下面的指令設定 Key:
22 |
23 | ```bash
24 | aws configure
25 | ```
26 |
27 | 這個指令會建立 `~/.aws/credentials` 跟 `~/.aws/config` 兩個檔案,分別用來存放 Key 跟其他設定。
28 |
29 | ## S3
30 |
31 | 列出所有 S3 bucket:
32 |
33 | ```bash
34 | aws s3 ls
35 | ```
36 |
37 | 從雲端上下載檔案可以使用 `cp` 指令,例如下載檔案到本機上:
38 |
39 | ```bash
40 | aws s3 cp s3://bucket-name/file-name .
41 | ```
42 |
43 | 上傳檔案到雲端:
44 |
45 | ```bash
46 | aws s3 cp
s3://bucket-name
47 | ```
48 |
49 | 可以使用 sync 指令同步本機跟雲端的檔案,例如下面的指令會把本機上的檔案同步到雲端:
50 |
51 | ```bash
52 | aws s3 sync s3://bucket-name
53 | ```
54 |
55 | 也可以同步雲端上面的兩個 bucket:
56 |
57 | ```bash
58 | aws s3 sync s3://bucket-name1 s3://bucket-name2
59 | ```
60 |
--------------------------------------------------------------------------------
/aws/07-route53.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 7
5 | ---
6 |
7 | # Route53
8 |
9 | 為 AWS 販售並管理域名的服務。
10 |
11 | ## SAA 筆記
12 |
13 | - 這是個全球服務。
14 | - Route 53 Alias Record 可以將流量導向至 AWS 資源,背後使用 A Record (讓 FQDN 指向 IP)。
15 | - Route 53 可以藉由 Health Check 來設定 Failover Routing Policy Configuration:
16 | - 要設定 NACL (Network Access Control List) 允許 Route 53 對目標發送 Health Check 的請求。
17 | - Route 53 的 Evaluate Target Health 要設定為 yes。
18 | - AWS Route53 Health Checking 提供兩種 Failover 設定
19 | - Active-Active Failover:確保所有資源都是可行的,如果發現某資源不健康,就會停止將流量轉過去該資源
20 | - Active-Passive Failover:有一個 Primary 與 Secondary 資源,Secondary 資源會是 Stand By 的狀態,只有當 Primary 無法,才會將流量轉送至 Secondary 的資源。
21 | - Route 53 有幾種 Routing:
22 | - Latency Routing:根據最低 Latency 提供服務,注意最低延遲不一定來自於同一區。
23 | - Geoproximity Routing:根據"**用戶與服務的地理位置**提供服務,且提供調整 bias,將較多或較少的流量引導到你想要的地區。
24 | - Geolocation Routing:根據"**用戶的地理位置**提供服務,不提供調整 bias。
25 | - Weighted Routing:主要用在單一域名但需要指向多個資源的情況,可以設定流量轉發的權重。可以做到跨 Region LB 或是測試新功能。
26 | - CloudWatch Events 無法直接監控 Route 53 端點的狀態。
27 |
--------------------------------------------------------------------------------
/pest/10-exception.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Pest
4 | nav_order: 10
5 | ---
6 |
7 | # 斷定是否會拋出例外 (Exception)
8 |
9 | 如果要測試一個流程在遭遇問題時是否會拋出例外 (Exception),斷定中也有 `expectException()` 的方法可以使用
10 |
11 | ```php
12 | it('can validate an email', function () {
13 | // 如果預計這個測試會拋出例外,我們可以使用這個方法來進行斷定
14 | // 注意這個方法必須放在測試中的最上方
15 | $this->expectException(InvalidArgumentException::class);
16 |
17 | $rule = new IsValidEmailAddress();
18 |
19 | $rule->passes('email', 123);
20 | });
21 | ```
22 |
23 | Pest 為這種情況提供一個更直覺的方法 `throws()`
24 |
25 | ```php
26 | it('can validate an email', function () {
27 | $rule = new IsValidEmailAddress();
28 |
29 | $rule->passes('email', 123);
30 | })
31 | ->throws(InvalidArgumentException::class);
32 | ```
33 |
34 | 除了斷定丟出哪一種例外,也可以對例外的錯誤訊息進行斷定,就算例外的類型對上了,只要訊息不對,斷定還是會失敗
35 |
36 | ```php
37 | it('can validate an email', function () {
38 | $rule = new IsValidEmailAddress();
39 |
40 | $rule->passes('email', 123);
41 | })
42 | ->throws(InvalidArgumentException::class, 'The value must be a string');
43 | ```
44 |
--------------------------------------------------------------------------------
/php/05-install-php.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: PHP
4 | nav_order: 5
5 | ---
6 |
7 | # 安裝 PHP
8 |
9 | 記錄如何在各個系統上安裝 PHP 開發環境。
10 |
11 | ## MacOS
12 |
13 | Mac 上最推薦使用 Homebrew 安裝 PHP,並使用 Laravel Valet 來設定 Laravel 的開發環境。
14 |
15 | ```shell
16 | # 使用 homebrew 安裝 php
17 | brew install php
18 |
19 | # 使用 composer 安裝 laravel valet
20 | composer global require laravel/valet
21 |
22 | # 想使用 valet 指令需要先確保 ~/.composer/vendor/bin 有加入環境變數中
23 | # valet 會使用 homebrew 來安裝 nginx 與 dnsmasq,因此需要 sudo 權限
24 | sudo valet install
25 |
26 | # 如果不想要每次執行 valet 指令都使用 sudo,可以將其加入 sudoers.d 中
27 | # valet 會在 /private/etc/sudoers.d 加入 valet 與 brew 兩個使用者
28 | sudo valet trust
29 | ```
30 |
31 | 建議可以使用 [PHP Monitor](https://phpmon.app/) 這個工具來查看 Valet 的狀態、
32 | 調整 PHP 的設定與**管理 PHP Extension**。
33 |
34 | ```shell
35 | # 安裝 php monitor
36 | brew tap nicoverbruggen/homebrew-cask
37 | brew install phpmon --cask
38 |
39 | # 如果想透過 php monitor 來安裝 php extension,需要先引入第三方的 php extension 庫
40 | brew tap shivammathur/extensions
41 | ```
42 |
43 | 啟動 PHP Monitor 之後,就可以很清楚地看到各種 PHP 相關的設定,也能很方便的進行調整。
44 |
--------------------------------------------------------------------------------
/k8s/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | has_children: true
4 | nav_order: 7
5 | ---
6 |
7 | # Kubernetes (K8s)
8 |
9 | 一直想學習如何使用 k8s,因此計畫自己在雲端上面開一些機器然後搭建一個 k3s 叢集,
10 | 並將自己的部落格 (Laravel App) 轉移到 k3s 叢集上。
11 |
12 | > k3s 為輕量版的 k8s,也是 CNCF 的專案之一,在使用上與 k8s 幾乎相同。
13 |
14 | ## 計畫
15 |
16 | 搭建好叢集之後預計做一些項目來練習如何使用 k8s。
17 |
18 | - 將自己的部落格搬移到 k8s 上。
19 | - 在 k8s 上面部署 Argo CD,可以透過 Argo CD 更新與部署部落格。
20 |
21 | ## 架構
22 |
23 | 與前輩討論後,便規劃了下面的網路架構。
24 |
25 | 
26 |
27 | - 在一個 VPC 底下,有兩個 subnet,一個是可以與外部直接連線的 public subnet,一個是只能透過 NAT 與外部連線的 private subnet。
28 | - Public subnet 底下有一個用 nginx 做的 proxy。
29 | - 想使用 SSH 遠端連線到 private subnet 底下的機器時,需要透過 proxy 當作 SSH 的跳板。
30 | - 因為 k3s 叢集放在 private subnet,所以外部無法直接訪問,如果想訪問叢集上的服務,會透過 proxy 做請求上的轉發。
31 | - 想在本地端使用 `kubectl` 操作 k3s 叢集時,也會透過 proxy 做請求上的轉發。
32 |
33 | ```nginx
34 | stream {
35 | server {
36 | listen 6443;
37 | proxy_pass 10.0.1.10:6443;
38 | }
39 | }
40 | ```
41 |
42 | > Kubectl 本身就是使用 TLS 加密,因此不需要在 nginx 上額外設定憑證,只需要單純轉發請求就好。
43 |
--------------------------------------------------------------------------------
/rust/32-control-how-tests-are-run.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Rust
4 | nav_order: 32
5 | ---
6 |
7 | # 控制程式如何執行
8 |
9 | 就跟 `cargo run` 一樣,`cargo test` 也會產生用於測試的執行檔,並執行它。
10 |
11 | ## 平行或接續執行測試
12 |
13 | 預設情況下,測試會並行執行,這樣可以加快測試速度。如果你的測試需要使用到共享資源,例如檔案或網路連線,並行執行可能會導致測試失敗。你可以使用 `--test-threads` 參數來控制測試執行緒數量。
14 |
15 | ```bash
16 | cargo test -- --test-threads=1
17 | ```
18 |
19 | ## 顯示輸出
20 |
21 | 在測試中,如果測試是成功的,是無法看到 `println!` 輸出的,只有失敗的測試會顯示。
22 |
23 | 如果你想要在成功的測試看到輸出,可以使用 `--show-output` 參數。
24 |
25 | ```bash
26 | cargo test -- --show-output
27 | ```
28 |
29 | ## 透過名稱來執行部分測試
30 |
31 | 如果你想要執行特定名稱的測試,可以在參數 `cargo test` 後面加上測試的名稱。
32 |
33 | ```bash
34 | cargo test add
35 | ```
36 |
37 | 這樣測試只會執行函式名稱包含 `add` 的測試。
38 |
39 | ## 忽略測試
40 |
41 | 對於想忽略不執行的測試,可以在測試函式上加上 `#[ignore]`。
42 |
43 | 這樣當執行 `cargo test` 時,`expensive_test` 這個測試就不會被執行。
44 |
45 | ```rust
46 | #[test]
47 | fn it_works() {
48 | assert_eq!(2 + 2, 4);
49 | }
50 |
51 | #[test]
52 | #[ignore]
53 | fn expensive_test() {
54 | // 會執行一小時的程式碼
55 | }
56 | ```
57 |
58 | 如果想執行被忽略的測試,可以加上參數 `--ignored`。
59 |
60 | ```bash
61 | cargo test -- --ignored
62 | ```
63 |
--------------------------------------------------------------------------------
/ansible/05-handlers.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Ansible
4 | nav_order: 5
5 | ---
6 |
7 | # Handlers
8 |
9 | 在 Ansible 中,你可以根據狀態是否變化,來觸發一些額外的操作。
10 |
11 | ## 範例
12 |
13 | ```yaml
14 | ---
15 | - name: Example of no-handler rule
16 | hosts: localhost
17 | tasks:
18 | - name: Register result of a task
19 | ansible.builtin.lineinfile:
20 | dest: "/tmp/example.txt"
21 | line: "Hi from Ansible!"
22 | check_mode: true # 根據檔案是否有 Hi from Ansible! 字串回傳 changed 屬性(沒有的話回傳 true)
23 | notify: # 如果 changed 屬性為 true 則依序執行下面兩個操作
24 | - Second command to run
25 | - Third command to run
26 |
27 | handlers:
28 | - name: Second command to run
29 | ansible.builtin.debug:
30 | msg: The placeholder file was modified!
31 |
32 | - name: Third command to run
33 | ansible.builtin.debug:
34 | msg: The placeholder file was modified!
35 | ```
36 |
37 | ## 參考資料
38 |
39 | - [Handlers: running operations on change](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html#handlers)
40 | - [Ansible Lint - no-handler](https://ansible.readthedocs.io/projects/lint/rules/no-handler/)
41 |
--------------------------------------------------------------------------------
/python/02-serial-number.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Python
4 | nav_order: 2
5 | ---
6 |
7 | # 從憑證取得 Serial Number
8 |
9 | 你可以使用 Python 從公有憑證中取得 Serial Number。
10 |
11 | 簡單的範例程式如下:
12 |
13 | ```python
14 | import re
15 | from cryptography import x509
16 |
17 |
18 | def is_pem_format(cert: str):
19 | return re.search(
20 | r"^(-----BEGIN CERTIFICATE-----)[\S\s]*(-----END CERTIFICATE-----)$", cert
21 | )
22 |
23 |
24 | def get_serial_number_from_certificate(cert: str):
25 | """
26 | calculate the certificate serial number
27 | ref: https://github.com/Azure/azure-sdk-for-python/issues/16965
28 | """
29 | if is_pem_format(cert) is False:
30 | raise Exception("Sorry, certificate is not correct pem format.")
31 |
32 | cert = x509.load_pem_x509_certificates(bytes(cert, "utf-8"))
33 | serial = cert[0].serial_number
34 | serial_number = f"{serial:x}".lower()
35 |
36 | return serial_number
37 |
38 |
39 | with open("cert.pem", "r") as cert:
40 | serial_number = get_serial_number_from_certificate(cert.read())
41 | print(serial_number)
42 | ```
43 |
44 | > 為什麼特地寫這一段,因為 Azure Key Vault 的 Certificate API 居然沒有回傳 Serial Number!
45 |
--------------------------------------------------------------------------------
/rust/37-box.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Rust
4 | nav_order: 37
5 | ---
6 |
7 | # 使用 `Box` 指向堆積上的資料
8 |
9 | Rust 中有一個智慧指標功能叫做 Box。
10 |
11 | **Box 允許你將資料儲存到堆積 (Heap) 上。同時會有一個堆疊 (Stack) 儲存指向堆積資料的指標 (Pointer)**。
12 |
13 | ```rust
14 | fn main() {
15 | let b = Box::new(5);
16 | println!("b = {}", b);
17 | } // b 在這裡被釋放。堆積上的值會被釋放,同時指向堆積的指標也會被釋放。
18 | ```
19 |
20 | Box 在遞迴資料結構中非常有用,因為它允許你的資料有一個已知的大小 (因為資料是一個固定的指標)。
21 |
22 | 一般的遞迴資料結構會有不確定的大小,因為它的大小取決於遞迴的深度。
23 |
24 | ```rust
25 | enum List {
26 | Cons(i32, List),
27 | Nil,
28 | }
29 |
30 | use List::{Cons, Nil};
31 |
32 | fn main() {
33 | // 這樣的寫法會造成編譯錯誤
34 | // 每個 Cons 的大小是不確定的,因為每個 Cons 都包含另一個 List,而這個 List 又包含另一個 Cons,以此類推
35 | // 因此編譯器無法判別出它需要多少空間才能儲存一個 List 的數值
36 | let list = Cons(1, Cons(2, Cons(3, Nil)));
37 | }
38 | ```
39 |
40 | 我們可以使用 `Box` 來解決這個問題,因為 `Box` 的指標有一個已知的大小
41 |
42 | ```rust
43 | enum List {
44 | Cons(i32, Box),
45 | Nil,
46 | }
47 |
48 | use List::{Cons, Nil};
49 |
50 | fn main() {
51 | // Cons 變體需要的大小為 i32 加上儲存 Box 指標的空間
52 | // 這樣的寫法就不會造成編譯錯誤,因為 Cons 變體的大小是已知的
53 | let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))));
54 | }
55 | ```
56 |
--------------------------------------------------------------------------------
/docker/05-proxy.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Docker
4 | nav_order: 5
5 | ---
6 |
7 | # 使用自己的 Proxy
8 |
9 | 部分公司較為嚴謹,內部伺服器會要求使用公司的 proxy 來對外連線,這時候就需要修改伺服器系統預設的 proxy。
10 |
11 | 我們可以在 `.bashrc` 或是 `/etc/environment` 中設定環境變數來修改 proxy,例如:
12 |
13 | ```bash
14 | export HTTP_PROXY=http://proxy.example.com:8888
15 | export HTTPS_PROXY=http://proxy.example.com:8888
16 | ```
17 |
18 | 但這麼做你會發現 `docker pull` 還是會失敗,**因為 Docker 的 Daemon 並不會去讀取系統的環境變數**。
19 |
20 | 我們需要在 `/etc/systemd/system/docker.service.d/` 中建立一個 `http-proxy.conf` 檔案,內容如下:
21 |
22 | ```conf
23 | [Service]
24 | Environment="HTTP_PROXY=http://proxy.example.com:8888"
25 | Environment="HTTPS_PROXY=http://proxy.example.com:8888"
26 | ```
27 |
28 | 接著重新啟動 Docker Daemon:
29 |
30 | ```bash
31 | sudo systemctl daemon-reload
32 | sudo systemctl restart docker
33 | ```
34 |
35 | 別忘記啟動的 container 也要使用 `--env` 參數來設定環境變數,否則 container 內無法對外連線:
36 |
37 | ```bash
38 | docker run \
39 | --env HTTP_PROXY=http://proxy.example.com:8888 \
40 | --env HTTPS_PROXY=http://proxy.example.com:8888 \
41 | -it ubuntu:20.04 bash
42 | ```
43 |
44 | ## 參考資料
45 |
46 | - [How to use proxy in Dockerfile](https://stackoverflow.com/questions/23111631/how-to-use-proxy-in-dockerfile)
47 |
--------------------------------------------------------------------------------
/azure/05-service-principal.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Azure
4 | nav_order: 5
5 | ---
6 |
7 | # Service Principal
8 |
9 | ## 使用 Azure CLI 建立一個 Service Principal 給 Terraform 部署資源
10 |
11 | > 必須先安裝 Azure CLI
12 |
13 | ```bash
14 | az ad sp create-for-rbac --name terraform --role Owner --scopes /subscriptions/
15 | ```
16 |
17 | 之後將產生的 app id 與 secret key 放到環境變數中,可以在 `.bashrc` 中設定
18 |
19 | ```bash
20 | export ARM_SUBSCRIPTION_ID=""
21 | export ARM_TENANT_ID=""
22 | export ARM_CLIENT_ID=""
23 | export ARM_CLIENT_SECRET=""
24 | ```
25 |
26 | 要刪除 service principal,可以使用下方的指令
27 |
28 | ```bash
29 | az ad sp delete --id
30 | ```
31 |
32 | ## 參考資料
33 |
34 | - [Configure Terraform in Azure Cloud Shell with Bash](https://learn.microsoft.com/en-us/azure/developer/terraform/get-started-cloud-shell-bash?tabs=bash)
35 | - [Azure | Creating an Azure Service Principal using Azure Portal](https://www.youtube.com/watch?v=Kf1Tai_BkWU)
36 | - [Application and service principal objects in Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals)
37 |
--------------------------------------------------------------------------------
/git/06-update-from-original-repo.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Git
4 | nav_order: 6
5 | ---
6 |
7 | # 跟上原始專案的更新
8 |
9 | 如果你的專案是從某一個專案 fork 出來的,當原始專案有更新時,你可以使用 `git pull` 來跟上原始專案的更新。
10 |
11 | 首先列出專案的遠端節點,通常只有一個 `origin`,也就是你 fork 出來的專案。
12 |
13 | ```bash
14 | git remote -v
15 |
16 | # origin git@github.com:yilanboy/starter-kit.git (fetch)
17 | # origin git@github.com:yilanboy/starter-kit.git (push)
18 | ```
19 |
20 | 然後加入原始專案的遠端節點,遠端節點的名稱可以自行命名,我這裡使用 `upstream`。
21 |
22 | ```bash
23 | git remote add upstream https://github.com/original-repo/starter-kit.git
24 | ```
25 |
26 | 再次列出專案的遠端節點,可以看到多了一個 `upstream`。
27 |
28 | ```bash
29 | git remote -v
30 |
31 | # upstream https://github.com/original-repo/starter-kit.git (fetch)
32 | # upstream https://github.com/original-repo/starter-kit.git (push)
33 | # origin git@github.com:yilanboy/starter-kit.git (fetch)
34 | # origin git@github.com:yilanboy/starter-kit.git (push)
35 | ```
36 |
37 | 接下來就可以使用 `gut fetch` 來下載原始專案的更新了。
38 |
39 | ```bash
40 | git fetch upstream
41 |
42 | # 合併原始專案的更新到當前分支
43 | git merge upstream/main
44 |
45 | # 推送到自己的遠端專案
46 | git push origin main
47 | ```
48 |
49 | ## 參考資料
50 |
51 | - [為你自己學 Git - 怎麼跟上當初 fork 專案的進度?](https://gitbook.tw/chapters/github/syncing-a-fork)
52 |
--------------------------------------------------------------------------------
/docker/08-logging-drivers.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Docker
4 | nav_order: 8
5 | ---
6 |
7 | # Logging Drivers
8 |
9 | Docker 本身可以使用多種不同的 Logging Drivers 來記錄容器輸出的日誌。
10 |
11 | Docker 預設的 Logging Driver 是 `json-file`。也就是簡單的將日誌以 JSON 格式儲存在容器內部。
12 |
13 | 你可以用下面的指令查看目前的 Logging Driver:
14 |
15 | ```bash
16 | docker info --format '{{.LoggingDriver}}'
17 | ```
18 |
19 | ## Local Logging Driver
20 |
21 | 你可以使用 `local` Logging Driver 來避免容器日誌量過大的問題,因為容器預設不會執行 Log Rotation,所以日誌會隨著時間越來越肥大。
22 |
23 | `local` Logging Driver 會自動執行 Log Rotation,所以會比 `json-file` 快更適合當作預設的 Logging Driver。
24 |
25 | > [!NOTE]
26 | >
27 | > Q:為什麼 Docker 不使用 `local` 當作預設的 Logging Driver?
28 | >
29 | > A:主要是為了與舊版本兼容。
30 |
31 | 你可以編輯 `~/.docker/daemon.json` 來預設使用 `local` Logging Driver。
32 |
33 | ```json
34 | {
35 | "log-driver": "local",
36 | "log-opts": {
37 | "max-size": "10m"
38 | }
39 | }
40 | ```
41 |
42 | 也可以在啟動容器時透過參數指定 Logging Driver。
43 |
44 | ```bash
45 | docker run \
46 | --log-driver local --log-opt max-size=10m \
47 | alpine echo hello world
48 | ```
49 |
50 | ## 參考資料
51 |
52 | - [Configure logging drivers](https://docs.docker.com/engine/logging/configure/)
53 | - [Local file logging driver](https://docs.docker.com/engine/logging/drivers/local/)
54 |
--------------------------------------------------------------------------------
/svelte/19-webstorm-settings.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 19
5 | ---
6 |
7 | # WebStorm IDE 設定
8 |
9 | 目前寫 SvelteKit 是用 JetBrains 的 WebStorm 來進行開發。這裡記錄一些對於 WebStorm 的設定。
10 |
11 | ## 調整重複程式碼的偵測範圍
12 |
13 | 在寫 SvelteKit 中的 `+page.server.ts` 時,發現會莫名其妙的出現 _重複程式碼行數過多_ 的提醒。
14 | 看了一下才發現原來 WebStorm 也將 `.svelte-kit` 資料夾底下的檔案納入檢查的範圍。
15 | 但這個資料夾是 SvelteKit 自動產生的,照理說不應該納入檢查的範圍才對。
16 |
17 | 這個檢查的範圍可以進行調整。
18 |
19 | 首先在設定中找到 _Editor -> Inspections -> General -> Duplicate code fragment_。
20 |
21 | 點開 _Scope_ 下拉選單,並選擇 _Edit Scopes Order..._ 來新增自己想要的檢查範圍
22 |
23 | 
24 |
25 | 點擊鉛筆圖示來新增檢查範圍。
26 |
27 | 
28 |
29 | 新增一個叫做 `SvelteKit` 的範圍,並輸入 `file:src/*`。指定只檢查 `src` 資料夾底下的檔案。
30 |
31 | 
32 |
33 | 在 _Scope_ 選擇剛剛建立的範圍 `SvelteKit`。並取消 `Everywhere else` 這個範圍。
34 |
35 | 
36 |
37 | ## 參考資料
38 |
39 | - [WebStorm Document - Locate duplicates](https://www.jetbrains.com/help/webstorm/analyzing-duplicates.html#configure-inspection)
40 |
--------------------------------------------------------------------------------
/github/02-outputs.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: GitHub
4 | nav_order: 2
5 | ---
6 |
7 | # Defining Outputs for Jobs
8 |
9 | 如果你想設定一個變數讓後續的 Job 使用,可以使用 GitHub Action 提供的 `GITHUB_OUTPUT` 環境變數。
10 |
11 | ## 範例
12 |
13 | ```yaml
14 | jobs:
15 | get-the-version-number-from-tag-name:
16 | name: Get the version number from tag name
17 | runs-on: ubuntu-latest
18 | # 3. 根據 step id 抓取 output
19 | outputs:
20 | version: ${{ steps.get-the-version-number.outputs.version }}
21 | steps:
22 | - name: Get the version number from tag name
23 | # 1. 需要在 step 上設定 id
24 | id: get-the-version-number
25 | # 2. 將變數設定放入 GITHUB_OUTPUT
26 | run: |
27 | echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
28 |
29 | print-the-version-number:
30 | needs: get-the-version-number-from-tag-name
31 | name: Print the version number
32 | runs-on: ubuntu-latest
33 | env:
34 | APP_VERSION: ${{ needs.get-the-version-number-from-tag-name.outputs.version }}
35 | steps:
36 | - name: Print the version number
37 | run: |
38 | echo ${{ env.APP_VERSION }}
39 | ```
40 |
41 | ## 參考資料
42 |
43 | - [Defining Outputs for Jobs](https://docs.github.com/en/actions/using-jobs/defining-outputs-for-jobs)
44 |
--------------------------------------------------------------------------------
/pest/04-refreash-database-trait.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Pest
4 | nav_order: 4
5 | ---
6 |
7 | # 使用 RefreshDatabase Trait 重置資料庫
8 |
9 | 通常在測試時,我們很常使用 `RefreshDatabase` 這個 Trait 來重置整個資料庫,方便進行測試
10 |
11 | 在 Pest 中,因為已經沒有 `class` 宣告,我們無法使用 trait 的方式引入 `RefreshDatabase`,所以我們必須換個方式,使用 uses()
12 |
13 | ```php
14 | use Illuminate\Foundation\Testing\RefreshDatabase;
15 |
16 | uses(RefreshDatabase::class);
17 |
18 | test('can view contents', function () {
19 | // ...
20 | });
21 | ```
22 |
23 | 如果你的每個測試都需要 `uses(RefreshDatabase::class)`,你可以考慮在 `tests/TestCase.php` 中引入 `RefreshDatabase`
24 |
25 | ```php
26 | namespace Tests;
27 |
28 | use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
29 | use Illuminate\Foundation\Testing\RefreshDatabase;
30 |
31 | abstract class TestCase extends BaseTestCase
32 | {
33 | use CreatesApplication;
34 |
35 | use RefreshDatabase;
36 | }
37 | ```
38 |
39 | 為了有更好的測試速度,你可以使用 `LazilyRefreshDatabase`,當測試動到資料庫時,才會重置資料庫
40 |
41 | ```php
42 | namespace Tests;
43 |
44 | use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
45 | use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
46 |
47 | abstract class TestCase extends BaseTestCase
48 | {
49 | use CreatesApplication;
50 |
51 | use LazilyRefreshDatabase;
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/svelte/08-motion.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 8
5 | ---
6 |
7 | # Motion (動態效果)
8 |
9 | ## Tweens
10 |
11 | Svelte 提供了 `tweened` 這個函式,可以簡單的建立一個由 A 轉變至 B 的平滑動畫。
12 |
13 | 以下方的 progress bar 為例,使用點擊按鈕來改變 progress bar 的進度。可以發現進度條的變化有一個簡易的動畫效果。
14 |
15 | ```svelte
16 |
21 |
22 |
23 |
24 |
27 |
28 |
31 | ```
32 |
33 | 還可以使用 `svelte/easing` 來增加其他動畫效果。
34 |
35 | ```svelte
36 |
45 | ```
46 |
47 | ## Spring
48 |
49 | `spring` 功能與 `tweened` 類似,提供了一種 Q 彈的效果動畫,`spring` 更適合時常變更值的場景。
50 |
51 | ```svelte
52 |
62 | ```
63 |
--------------------------------------------------------------------------------
/linux/07-sockets.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Linux
4 | nav_order: 7
5 | ---
6 |
7 | # 什麼是 Sockets
8 |
9 | Sockets 是作業系統為了不同進程之間的溝通而提供的一層抽象層,不論是在同一台機器上還是透過網路跨越多台機器。
10 |
11 | Socket = IP Address + Port Number,可以用來標識網路上的一個通訊端點。
12 |
13 | > 所以 192.168.1.5:22 就是一個 Socket。
14 |
15 | Socket 運作在 OSI 模型的傳輸層(Layer 4),會將資料打包成 TCP(Transmission Control Protocol) 或 UDP(User Datagram Protocol)封包,並透過網路層(Layer 3)傳送到目的地。
16 | 所以 TCP 與 UDP 也是 Socket 的主要通訊協定。
17 |
18 | 除了網路,Socket 也可以用於同一台機器上的進程間通訊(Inter-Process Communication, IPC),例如 Unix Domain Sockets(簡稱 UDS)。UDS 的速度通常比透過網路的 Socket 快,例如 PostgreSQL 與 Redis 的客戶端指令工具都是使用 UDS。
19 |
20 | UDS 不使用 IP Address 與 Port Number,而是使用檔案系統中的路徑來標識通訊端點,例如 `/var/run/postgresql/.s.PGSQL.5432` 或者是 `/tmp/app.sock`,所以速度會比透過網路的 Socket 快很多。
21 |
22 | ## 什麼是 Anonymous Pipes?
23 |
24 | Anonymous Pipes(匿名管道)是一種用於在同一台機器上的不同進程之間進行通信的機制。它們通常用於父子進程之間的數據傳輸。匿名管道是單向的,意味著數據只能從一個端點流向另一個端點。它們不需要命名,因此只能在創建它們的進程及其子進程之間使用。
25 |
26 | ## 什麼是 Ephemeral Ports?
27 |
28 | Ephemeral Ports(臨時端口)是操作系統在需要時動態分配給應用程序的短暫端口號。這些端口號通常用於客戶端應用程序在與服務器建立連接時使用。Ephemeral Ports 的範圍通常在 49152 到 65535 之間,但具體範圍可能因操作系統而異。當應用程序關閉連接後,這些端口號會被釋放並可供其他應用程序使用。
29 |
30 | ## 什麼是 File Descriptor?
31 |
32 | File Descriptor(文件描述符)是操作系統用來表示打開的文件、Socket 或其他輸入/輸出資源的整數標識符。在 Unix 和類 Unix 系統中,文件描述符是非負整數,通常從 0 開始分配。標準輸入、標準輸出和標準錯誤分別對應文件描述符 0、1 和 2。文件描述符允許程序通過統一的接口來讀寫不同類型的資源。
33 |
--------------------------------------------------------------------------------
/linux/05-last.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Linux
4 | nav_order: 5
5 | ---
6 |
7 | # last 指令
8 |
9 | last 指令可以用來檢查系統用戶的登入記錄。
10 |
11 | ```bash
12 | last
13 | ```
14 |
15 | 指令結果如下:
16 |
17 | ```text
18 | ubuntu pts/2 61.220.176.180 Fri Aug 9 06:56 still logged in
19 | ubuntu pts/2 61.220.176.180 Fri Aug 9 06:08 - 06:09 (00:00)
20 | ubuntu pts/2 61.220.176.180 Fri Aug 9 05:42 - 05:43 (00:01)
21 | ubuntu pts/2 61.220.176.180 Fri Aug 9 05:40 - 05:41 (00:00)
22 | ubuntu pts/0 3.114.176.187 Fri Aug 9 05:38 still logged in
23 | reboot system boot 6.8.0-1010-azure Fri Aug 9 04:04 still running
24 | reboot system boot 6.8.0-1001-azure Fri Aug 9 03:53 - 04:04 (00:11)
25 |
26 | wtmp begins Fri Aug 9 03:53:14 2024
27 | ```
28 |
29 | 你可以指定查看某一用戶的登入記錄:
30 |
31 | ```bash
32 | last ubuntu
33 | ```
34 |
35 | 或是指定只想看前幾筆的紀錄:
36 |
37 | ```bash
38 | last -n 5
39 | ```
40 |
41 | last 也可以用來檢查開機的時間:
42 |
43 | ```bash
44 | last reboot
45 | ```
46 |
47 | `last` 預設是從 `/var/log/wtmp` 這個記錄檔案取得使用者登入的資料。
48 |
49 | 因為系統都會有 logrotate 服務定期清空記錄檔案並將舊紀錄移到其他檔案存放。這會導致 `last` 看不到比較舊的資料,此時可以用 `-f` 參數指定要查詢的記錄檔案路徑:
50 |
51 | ```bash
52 | last -f /var/log/wtmp.1
53 | ```
54 |
55 | ## 參考資料
56 |
57 | - [Linux 使用 last 指令檢查使用者登入記錄教學與範例](https://officeguide.cc/linux-last-command-tutorial-examples/)
58 |
--------------------------------------------------------------------------------
/docker/06-custom-command.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Docker
4 | nav_order: 6
5 | ---
6 |
7 | # 使用 Docker Container 來製作自己的指令
8 |
9 | 我不太想為了 `pg_dump` 安裝 `PostgreSQL` 資料庫 ,所以就打算使用 Docker Container 來製作自己的 `pg_dump` 指令。首先新增一個檔案 `pg_dump.sh`
10 |
11 | ```bash
12 | #!/bin/bash
13 |
14 | # -it:指示 Docker 容器在互動模式下執行並分配一個偽終端
15 | # --rm:指示 Docker 容器在退出時自動刪除
16 | # "$@":表示命令列參數。"$@" 會展開傳遞給 shell script 檔案的所有參數
17 | docker run -it --rm postgres:16.3 pg_dump "$@"
18 | ```
19 |
20 | 幫 `pg_dump.sh` 加上執行權限。
21 |
22 | ```bash
23 | chmod a+x pg_dump.sh
24 | ```
25 |
26 | 試著執行 `pg_dump.sh`。
27 |
28 | ```bash
29 | ./pg_dump.sh --version
30 |
31 | # 指令輸出結果如下
32 | # pg_dump (PostgreSQL) 16.3 (Debian 16.3-1.pgdg120+1)
33 | ```
34 |
35 | 看能不能用 `pg_dump.sh` 來備份資料庫中的資料,找一個 PostgreSQL 資料庫試試看。
36 |
37 | ```bash
38 | ./pg_dump.sh --host='my-postgres-database.net' -d my_db --username='db_owner' -f db.sql
39 | ```
40 |
41 | 輸入密碼之後,會發現 `db.sql` 並沒有出現,這是因為檔案是下載到容器中,而不是本地環境上。可以透過 Docker Volume 與 Work Dir 來解決這個問題。
42 |
43 | 修改一下 `pg_dump.sh` 的內容。
44 |
45 | ```bash
46 | #!/bin/bash
47 |
48 | # -v ${PWD}:/app:此參數將當前工作目錄掛載到容器內部的 /app 目錄。這意味著容器內部的 pg_dump 命令將能夠訪問你的當前工作目錄中的檔案。
49 | # -w /app:此參數將容器內部的工作目錄設定為 /app。這意味著 pg_dump 命令將在 /app 目錄中執行。
50 | docker run -it --rm -v ${PWD}:/app -w /app postgres:16.3 pg_dump "$@"
51 | ```
52 |
53 | 再次使用 `pg_dump.sh` 來備份資料庫中的資料,就可以看到 `db.sql` 出現囉。
54 |
--------------------------------------------------------------------------------
/ansible/07-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Ansible
4 | nav_order: 7
5 | ---
6 |
7 | # Template Module
8 |
9 | Ansible 的 Template 套件支援使用 Jinja Template,可以動態生成配置檔案。
10 |
11 | ## 基本用法
12 |
13 | Template 模組將 Jinja2 模板檔案轉換為目標主機上的檔案。
14 |
15 | ### 簡單範例
16 |
17 | 新增一個 `templates` 資料夾,並在其底下建立模板檔案 `nginx.conf.j2`:
18 |
19 | ```jinja2
20 | server {
21 | listen {{ nginx_port | default(80) }};
22 | server_name {{ server_name }};
23 |
24 | location / {
25 | root {{ web_root }};
26 | index index.html;
27 | }
28 | }
29 | ```
30 |
31 | 在 Playbook 中使用 template 模組:
32 |
33 | ```yaml
34 | - name: Deploy website nginx config
35 | ansible.builtin.template:
36 | src: ./templates/nginx.conf.j2
37 | dest: /etc/nginx/sites-available/{{ server_name }}
38 | owner: root
39 | group: root
40 | mode: "u=rw,g=r,o=r"
41 | vars:
42 | nginx_port: 8080
43 | server_name: example.com
44 | web_root: /var/www/html
45 | notify: restart nginx
46 | ```
47 |
48 | ## 常用參數
49 |
50 | - `src`: 模板檔案路徑(相對於 templates/ 目錄)
51 | - `dest`: 目標檔案路徑
52 | - `owner`: 檔案擁有者
53 | - `group`: 檔案群組
54 | - `mode`: 檔案權限
55 | - `backup`: 是否備份原檔案(true/false)
56 |
57 | ## Jinja2 語法重點
58 |
59 | - `{{ variable }}`: 輸出變數值
60 | - `{% if condition %}...{% endif %}`: 條件判斷
61 | - `{% for item in list %}...{% endfor %}`: 迴圈
62 | - `{{ variable | default('default_value') }}`: 設定預設值
63 |
--------------------------------------------------------------------------------
/vim/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | has_children: true
4 | nav_order: 21
5 | ---
6 |
7 | # Vim
8 |
9 | Vim 是一個很強大的編輯器,搭配多種快捷鍵,可以讓你的快速的編寫任何文件或是撰寫程式碼
10 |
11 | ## 模式 (Mode)
12 |
13 | Vim 有三種模式
14 |
15 | - Normal Mode (Keep Mode)
16 | - Insert Mode
17 | - Visual Mode
18 |
19 | ### Normal Mode
20 |
21 | 在 normal mode 底下無法編輯檔案,但可以輸入各種 vim 的快捷鍵
22 |
23 | ### Insert Mode
24 |
25 | 在 normal mode 底下輸入 `i` 或是 `a`,即可進入編輯模式,開始修改檔案的內容
26 |
27 | ### Visual Mode
28 |
29 | 在 normal mode 底下輸入 `v`,即可進入 visual mode,可以選取你想要的範圍已進行複製或是剪下
30 |
31 | ## 指令模式
32 |
33 | 在 normal mode 輸入 `:` 進入指令模式,可以輸入指令或修改 vim 的設定
34 |
35 | 例如,我想搜尋檔案的話,可以在輸入 `:/keyword`,就可以搜尋當前檔案的內容
36 |
37 | 如果我想要修改 vim 的設定,例如在編輯器中顯示行的數字,可以輸入 `:set number`
38 |
39 | 設定預設不會保留,如果想要儲存 vim 的設定可以建立一個檔案 `~/.vimrc`,然後將你要的檔案設定寫上
40 |
41 | ```vim
42 | syntax on
43 | colorscheme onedark
44 |
45 | set clipboard=unnamedplus
46 | set clipboard+=unnamed
47 | set number
48 | set rnu
49 | set smartindent
50 | set tabstop=4
51 | set shiftwidth=4
52 | set expandtab
53 | set backspace=indent,eol,start
54 | set scrolloff=8
55 |
56 | nmap zh ^
57 | nmap zl $
58 | nmap ,p "0p
59 |
60 | vmap < >gv
62 | ```
63 |
64 | ## 參考資料
65 |
66 | - [Why doesn't the backspace key work in insert mode?](https://vi.stackexchange.com/questions/2162/why-doesnt-the-backspace-key-work-in-insert-mode)
67 | - [onedark.vim](https://github.com/joshdick/onedark.vim)
68 |
--------------------------------------------------------------------------------
/google-cloud-platform/02-projects.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Google Cloud Platform
4 | nav_order: 2
5 | ---
6 |
7 | # Project
8 |
9 | 在 GCP 中,如果要建立資源,就需要先建立一個 project (專案),你可以將你的資源建立在專案中
10 |
11 | ## 建立專案
12 |
13 | 專案的名稱可以隨意自訂,不過 project id 必須是全球唯一性 (globally unique),建立之後還會獲得一組 project number
14 |
15 | > project id 在建立後不能修改
16 |
17 | 可以使用 gcloud CLI 來建立 project
18 |
19 | ```shell
20 | gcloud projects create my-project
21 | ```
22 |
23 | 建立完成之後可以嘗試使用 `gcloud projects list` 指令查看目前建立了哪些 project
24 |
25 | ```shell
26 | $ gcloud projects list
27 |
28 | PROJECT_ID NAME PROJECT_NUMBER
29 | container-platform-123 container-platform 123456789012
30 | ```
31 |
32 | ## 切換專案
33 |
34 | 你可以透過 gcloud CLI 來切換專案。
35 |
36 | ```shell
37 | gcloud config set project $MY_PROJECT_ID
38 | ```
39 |
40 | ## 刪除專案
41 |
42 | 你可以使用 gcloud CLI 刪除專案
43 |
44 | ```shell
45 | gcloud projects delete my-project
46 | ```
47 |
48 | 但要注意刪除專案為軟性刪除 (soft deleting),在**30 天之內**你都可以恢復 (restore) 已刪除的專案,可以上 GCP 主控台操作,也可以使用 gcloud CLI 的 restore 指令
49 |
50 | ```shell
51 | gcloud projects restore my-project
52 | ```
53 |
54 | 專案在刪除後需要等待 30 天之後才會完全刪除,並沒有辦法立即刪除
55 |
56 | 此外 project id,**即使是在系統刪除專案之後,也無法重新使用**,假設你想保留 project id,你應該避免將專案刪除,而是只刪除 project 底下的資源
57 |
58 | ## 參考資料
59 |
60 | - [Creating and managing projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects)
61 |
--------------------------------------------------------------------------------
/network/01-ca-certificate.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Network
4 | nav_order: 1
5 | ---
6 |
7 | # CA 憑證
8 |
9 | 紀錄使用 certbot 向 Let's Encrypt 申請網域 `example.com` 憑證的流程
10 |
11 | ## 證明網域所有權
12 |
13 | 1. 憑證管理軟體 (certbot) 會向 Let's Encrypt CA 發起申請
14 | 2. 憑證管理軟體會產生一組公私鑰,並將公鑰交給 CA
15 | 3. CA 會提供兩種挑戰,擇一即可,用來讓 Server 證明其確實擁有網域所有權
16 | - 在 `example.com` 下設定 DNS 紀錄 (dns-01)
17 | - 在 `http://example.com` 的特定路徑底下提供 HTTP 文件 (http-01)
18 | 4. 以 http-01 挑戰為例,CA 產生一組隨機數 (nonce) 交給憑證管理軟體,請它將這組隨機數使用私鑰簽名後寫入文件,並將文件放在網站上的特定路徑
19 | 5. 憑證管理軟體完成後會通知 CA 進行檢查
20 | 6. CA 會從網站上的特定路徑下載文件並確認內容
21 | 7. 驗證通過,剛剛的公私鑰會被用作「授權金鑰對」,並開始頒發憑證
22 |
23 | ## 頒發憑證
24 |
25 | 1. 憑證管理軟體發出憑證管理請求並用授權金鑰對簽名
26 | 2. 憑證管理軟體建立一個憑證簽署請求 (Certificate Signing Request, CSR),要求 CA 頒發憑證
27 | 3. CSR 通常會包含由另外一組私鑰加密的資料與其對應的公鑰,此外 CSR 會由授權金鑰進行簽名
28 | 4. 當 CA 收到請求後會檢查兩組簽名
29 | 5. 如果驗證成功,CA 會用 CSR 中的公鑰替 `example.com` 的憑證簽名,再將文件回傳給憑證管理軟體
30 |
31 | ## 註銷憑證
32 |
33 | 1. 憑證管理軟體使用授權金鑰幫助消請求簽名並發送給 CA
34 | 2. CA 確認過簽名後,便將註銷消息發布到 OCSP (Online Certificate Status Protocol) 伺服器,以便讓瀏覽器等有關程式知道該憑證已被註銷
35 |
36 | ## 筆記
37 |
38 | - CA 不會產生與保管私鑰,公私鑰皆由 Server 上的憑證管理軟體產生,避免惡意 CA 將私鑰另做它途
39 |
40 | ## 參考資料
41 |
42 | - [certbot - frequently asked questions](https://certbot.eff.org/faq)
43 | - [Let's encrypt 運作原理](https://letsencrypt.org/zh-tw/how-it-works/)
44 | - [What harm can a malicious CA do?](https://security.stackexchange.com/questions/61730/what-harm-can-a-malicious-ca-do)
45 |
--------------------------------------------------------------------------------
/azure/06-data-explorer.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Azure
4 | nav_order: 6
5 | ---
6 |
7 | # Azure Data Explorer
8 |
9 | 記錄一下使用 Azure Data Explorer (以下簡稱 ADX)的過程。
10 |
11 | 前陣子公司在研究如何將地端的 Log 系統上雲,做了一些研究之後,發現 ADX 或許是我們可以考慮的解決方案之一。理由如下:
12 |
13 | - ADX 可接收的資料量很大。端看你要使用 streaming ingestion 或是 batch ingestion。
14 | - 公司內部的認證與 Azure AD 深度整合,因此在訪問 ADX 的權限控管上會比較好設定。
15 | - ADX 有提供 REST API 與 SDK,因此可以自己寫工具來存取資料。
16 | - ADX 內部的資料可以設定 retention policy,時間一到可以自動刪除資料,節省成本。
17 | - ADX 提供 Kusto Query Language (KQL) 讓用戶對資料進行操作,可以用來查詢資料,也可以結合 Power BI 來做資料視覺化。
18 |
19 | 雖然 ADX 看起來貌似不錯,但由於 ADX 會啟動一組 cluster 處理所有的 ingestion、data markup 與 user query。**所以即使你不使用,也需要負擔 cluster 運行的成本**。但另外一方面來說,即使 user query 的次數很頻繁,也不會因此增加費用。
20 |
21 | 總結來說,ADX 很適合用在存放大量的資料,且 query 次數相當頻繁的場景。
22 |
23 | ## Log 系統架構圖
24 |
25 | 
26 |
27 | 簡單說明架構的設計:
28 |
29 | - Log 的來源大多為地端的 server。並由 fluent bit 送往 Azure。
30 | - 跟據合規性的要求,需要將 log 備份一段時間。因此 log 會先送至 blob storage 進行備份。
31 | - Blob storage 可以設定資料物件的 policy。根據保存的時間自動轉 tier,例如從 cool 轉成 archive,也可以根據時間自動刪除資料,以節省成本。
32 | - 當 log 被上傳至 blob storage,會觸發 event grid 通知 ADX 進行 ingestion。將資料倒入 ADX 中。
33 | - ADX 上也會設定 retention policy,根據時間自動刪除資料。我這裡設計是保存一個月,超過一個月的資料會自動刪除,以節省成本。
34 |
35 | > [!NOTE]
36 | >
37 | > Fluent bit 本身支援將資料送往 blob storage 或是 ADX,我之前嘗試過在 fluent bit 將資料複製一份同時送往 blog storage 與 ADX,但非常不穩定,時常發生掉資料的情況。
38 |
--------------------------------------------------------------------------------
/ansible/03-variables.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Ansible
4 | nav_order: 3
5 | ---
6 |
7 | # Variables
8 |
9 | 在 playbook 中可以使用 `vars` 來定義變數,例如:
10 |
11 | ```yaml
12 | # playbook.yml
13 | - name: update apt packages
14 | hosts: web_servers
15 | become: true
16 | vars:
17 | package_name: nginx
18 | tasks:
19 | - name: install nginx
20 | ansible.builtin.apt:
21 | name: "{{ package_name }}"
22 | state: present
23 | ```
24 |
25 | 你也可以在 inventory 中設定要給 host 使用的變數,例如:
26 |
27 | ```ini
28 | web_server http_port=80 https_port=443
29 | ```
30 |
31 | 然後在 playbook 中使用 `package_name` 來取得變數,例如:
32 |
33 | ```yaml
34 | # playbook.yml
35 | - name: Set Firewall Configurations
36 | hosts: web_server
37 | tasks:
38 | - name: Allow HTTP
39 | community.general.ufw:
40 | rule: allow
41 | port: "{{ http_port }}"
42 | proto: tcp
43 |
44 | - name: Allow HTTPS
45 | community.general.ufw:
46 | rule: allow
47 | port: "{{ https_port }}"
48 | proto: tcp
49 | ```
50 |
51 | 更好的方式,你也可以創建一個與 host 名稱相同的檔案,例如 `web_server.yaml`,然後在檔案中定義變數,例如:
52 |
53 | ```yaml
54 | # web_server.yaml
55 | http_port: 80
56 | https_port: 443
57 | ```
58 |
59 | 變數是可以與其他字串結合的,例如:
60 |
61 | ```yaml
62 | # playbook.yml
63 | - name: do not permit traffic in default zone on port 80/tcp
64 | ansible.posix.firewalld:
65 | port: "{{ http_port }}/tcp"
66 | permanent: true
67 | state: disabled
68 | ```
69 |
--------------------------------------------------------------------------------
/php/08-phpstorm.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: PHP
4 | nav_order: 8
5 | ---
6 |
7 | # PHPStorm
8 |
9 | PHPStorm 為 Jetbrains 推出的 PHP IDE,是寫 PHP 的好幫手,這裡記錄一些我習慣在 PHPStorm 中調整的設定。
10 |
11 | ## 程式碼樣式
12 |
13 | ### 使用 Laravel Pint 的程式碼風格
14 |
15 | 在 Settings -> Editor -> Code Style -> PHP 中。
16 |
17 | 點選右上角的 Set from... 然後選擇 **Laravel**。
18 |
19 | ### 對齊陣列的值
20 |
21 | 在 Settings -> Editor -> Code Style -> PHP -> Wrappings and Braces 底下。
22 |
23 | 勾選 Array Initializer 底下的 **Align key-value pairs**。
24 |
25 | ## 快捷鍵(Shortcuts)
26 |
27 | | Shortcut | Description |
28 | | --------------- | ---------------------- |
29 | | `Ctrl` + `G` | 選取相同的字段 |
30 | | `Ctrl` + `Ctrl` | Run anythings |
31 | | `Shift` + `F4` | 開啟當前頁面的浮動視窗 |
32 | | `Cmd` + `[` | 跳回之前的位置 |
33 | | `F3` | 將該行加入書籤 |
34 |
35 | ## 系統設定
36 |
37 | ### 衝突的按鍵
38 |
39 | PHPStorm 中的快捷鍵 `Cmd` + `Shift` + `A` 與 MacOS 的快捷鍵會產生衝突,建議在系統中將其關閉。
40 |
41 | 在 設定 -> 鍵盤 -> 鍵盤快速鍵 -> 服務 -> 文字 底下。
42 |
43 | 取消勾選「在終端機裡搜尋 man 頁面索引」。
44 |
45 | ## 參考資料
46 |
47 | - [Formatting code: getting aligned array setups](https://www.reddit.com/r/phpstorm/comments/17apa05/formatting_code_getting_aligned_array_setups/)
48 | - [Cmd+Shift+A hotkey opens Terminal with "apropos" search instead of the Find Action dialog](https://intellij-support.jetbrains.com/hc/en-us/articles/360005137400-Cmd-Shift-A-hotkey-opens-Terminal-with-apropos-search-instead-of-the-Find-Action-dialog)
49 |
--------------------------------------------------------------------------------
/github/03-markdown-syntax.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: GitHub
4 | nav_order: 3
5 | ---
6 |
7 | # GitHub Markdown Syntax
8 |
9 | 記錄一些在 Github 上撰寫文件時會用到的特殊 Markdown 語法。
10 |
11 | ## Alerts
12 |
13 | Github 上提供了幾種不同的警告訊息樣式,可以用來強調文件中的重要資訊。
14 |
15 | ```markdown
16 | > [!NOTE]
17 | > Useful information that users should know, even when skimming content.
18 |
19 | > [!TIP]
20 | > Helpful advice for doing things better or more easily.
21 |
22 | > [!IMPORTANT]
23 | > Key information users need to know to achieve their goal.
24 |
25 | > [!WARNING]
26 | > Urgent info that needs immediate user attention to avoid problems.
27 |
28 | > [!CAUTION]
29 | > Advises about risks or negative outcomes of certain actions.
30 | ```
31 |
32 | 這看起來會長這樣:
33 |
34 | 筆記:
35 |
36 | > [!NOTE]
37 | > Useful information that users should know, even when skimming content.
38 |
39 | 提示:
40 |
41 | > [!TIP]
42 | > Helpful advice for doing things better or more easily.
43 |
44 | 重要:
45 |
46 | > [!IMPORTANT]
47 | > Key information users need to know to achieve their goal.
48 |
49 | 警告:
50 |
51 | > [!WARNING]
52 | > Urgent info that needs immediate user attention to avoid problems.
53 |
54 | 注意:
55 |
56 | > [!CAUTION]
57 | > Advises about risks or negative outcomes of certain actions.
58 |
59 | ## 參考資料
60 |
61 | - [Basic writing and formatting syntax](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
62 |
--------------------------------------------------------------------------------
/aws/16-storage.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: AWS
4 | nav_order: 16
5 | ---
6 |
7 | # Storage Service
8 |
9 | ## Elastic Block Storage (EBS)
10 |
11 | - EBS 預設加密。這個是地區 (regional) 級的功能。
12 | - Amazon EBS 的類型有三種:
13 | - General Purpose (SSD)。
14 | - Provisioned IOPS (SSD)。
15 | - Magnetic (現在稱 Cold HDD)。
16 | - Elastic Block Storage (EBS) io1/io2 支援同時被多個 EC2 掛載,最多 16 台。
17 | - EBS 沒有 lifecycle policy。
18 | - 使用 Amazon Data Lifecycle Manager (DLM) 可以幫 EBS 做到以下功能的自動化:
19 | - 建立 snapshot。
20 | - 設定 snapshot 保留時間。
21 | - 刪除 snapshot。
22 | - EBS snapshot 會送至 S3 上保存。
23 | - Data Lifecycle Manager (DLM) 可以與 CloudWatch 事件進行整合,例如 DLM 的備份數量達到上限,就會觸發 CloudWatch 事件。
24 | - 用 DLM 來備份 EBS 是最快速也最經濟實惠的選擇,透過 AWS CLI 定期執行 `craete-snapshot` 也可以,但需要時間。
25 | - EBS 支援 file lock,但不支援 object lock。object lock 是 S3 的功能。
26 | - EBS 建立後,會自動在**同區域**建立 replica。
27 | - EBS 與 EC2 必須要在同一個區域才能連接。
28 | - 如果想換區域,可以使用 snapshot 來做到。
29 | - EBS 使用 KMS 來加密,無論是 AWS Managed Key 或是匯入的 Custom Key 都可以。
30 | - 未加密的 EBS,其 snapshot 也是未加密的,反之亦然。因此想加密 EBS,可以先建立一個加密的 snapshot,然後再建立 EBS。
31 | - IOPS SSD,在 io1 可以提供 4GB 到 16TB 的存儲空間,根據你的硬碟大小,可以提供的 IOPS 也會提高。
32 | - 1GB 的硬碟大小可以提供 50 IOPS。
33 |
34 | ## Elastic File System (EFS)
35 |
36 | - 支援 POSIX (Portable Operating System Interface) 標準。
37 | - EFS 可以同時被**上千台 EC2** 掛載。
38 | - EFS 的生命週期管理無法刪除檔案,根據時間可以將資料移往。
39 | - 不常存取 (Infrequent Access, Standard-IA)。
40 | - 單區域不常存取 (One-Zone IA)。
41 |
42 | ## Amazon FSx 檔案系統
43 |
44 | - Amazon FSx For Lustre 是熱門開源的 parallel file system,效能非常好。
45 |
--------------------------------------------------------------------------------
/docker/02-build.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Docker
4 | nav_order: 2
5 | ---
6 |
7 | # 建立映像檔
8 |
9 | 寫好 `Dockerfile`,就可以使用 docker 指令開始建立 image
10 |
11 | ```bash
12 | # 確定與 Dockerfile 在同一個目錄底下
13 | docker build -t image:tag .
14 |
15 | # 可以使用 -f 指定 Dockerfile
16 | docker build -t image:tag ./Dockerfile
17 | ```
18 |
19 | ## 使用 Docker Buildx 建立不同架構的映像檔
20 |
21 | Docker Buildx 是一個 Docker 官方的工具,可以用來建構跨平台的 Docker 映像檔
22 |
23 | 使用 Docker Buildx,您可以輕鬆地建構多種不同系統架構的映像檔,例如 x86、ARM、IBM PowerPC 等等
24 |
25 | 可以查看目前系統支援 build 哪幾種架構
26 |
27 | ```bash
28 | docker buildx ls
29 | ```
30 |
31 | 顯示結果如下,`*` 表示預設使用的 builder
32 |
33 | ```text
34 | NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
35 | default * docker default default running 20.10.22 linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
36 | desktop-linux docker desktop-linux desktop-linux running 20.10.22 linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
37 | ```
38 |
39 | 我們可以建立以 docker container 執行的 builder,並設定新建立的 builder 為預設
40 |
41 | ```bash
42 | docker buildx create --name mybuilder --platform linux/amd64,linux/arm64
43 | docker buildx use mybuilder
44 | ```
45 |
46 | 之後我們就可以使用 Buildx 建立不同架構的映像檔案,並推送至 Docker Hub
47 |
48 | ```bash
49 | # 確定與 Dockerfile 在同一個目錄底下
50 | docker buildx build --platform linux/amd64,linux/arm64 --push -t image:tag .
51 | ```
52 |
--------------------------------------------------------------------------------
/svelte/11-actions.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 11
5 | ---
6 |
7 | # Actions
8 |
9 | Actions 本質上是元素級生命週期 (element-level lifecycle) 的函式,會在元素被建立時呼叫。常常用在
10 |
11 | - 第三方庫的整合
12 | - 圖片的惰性載入
13 | - 工具提示 (tooltips)
14 | - 新增自訂事件處理程序
15 |
16 | ## `use:` 語法
17 |
18 | ```svelte
19 |
31 |
32 |
33 |
34 |
35 | ```
36 |
37 | ## 加入變數
38 |
39 | Actions 也可以傳入變數。下方是一個整合 tippy.js 的例子:
40 |
41 | ```svelte
42 |
62 |
63 |
64 |
65 |
68 | ```
69 |
--------------------------------------------------------------------------------
/pest/06-faker.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Pest
4 | nav_order: 6
5 | ---
6 |
7 | # 使用 Faker 產生假資料
8 |
9 | Faker 可以用來生產假資料,除了常被用做 database 的 seeding 之外,測試中也同樣很常被使用
10 |
11 | Pest 有提供一個 faker plugin,讓我們可以在 Pest 中使用 Faker,首先使用下面的方式安裝
12 |
13 | ```bash
14 | composer require pestphp/pest-plugin-faker --dev
15 | ```
16 |
17 | Faker 在 Pest 中的使用範例如下,假設測試能否通過 Post 請求新增一個聯絡人
18 |
19 | ```php
20 | use function Pest\Faker\faker;
21 |
22 | it('can store a contact', function () {
23 | login()->post('/contacts', [
24 | 'first_name' => faker()->firstName,
25 | 'last_name' => faker()->lastName,
26 | 'email' => faker()->email,
27 | 'phone' => faker()->e163PhoneNumber,
28 | 'address' => 'No. 22, Fake Rd',
29 | 'city' => 'Fake City',
30 | 'region' => 'Fake Region',
31 | 'country' => faker()->randomElement(['us', 'tw']),
32 | 'postal_code' => faker()->postcode,
33 | ])
34 | ->assertRedirect('/contacts')
35 | ->assertSessionHas('success', 'Contact created');
36 | });
37 | ```
38 |
39 | 如果不想額外裝套件,可以使用 `WithFaker` 這個 trait
40 |
41 | ```php
42 | use Illuminate\Foundation\Testing\WithFaker;
43 |
44 | uses(WithFaker::class);
45 |
46 | it('can store a contact', function () {
47 | login()->post('/contacts', [
48 | 'first_name' => $this->faker->firstName,
49 | 'last_name' => $this->faker->lastName,
50 | // ...
51 | ])
52 | ->assertRedirect('/contacts')
53 | ->assertSessionHas('success', 'Contact created');
54 | });
55 | ```
56 |
--------------------------------------------------------------------------------
/linux/03-ram-disk.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Linux
4 | nav_order: 3
5 | ---
6 |
7 | # 在 Linux 中建立記憶體硬碟
8 |
9 | 記憶體硬碟 (RAM Disk) 相較於傳統機械硬碟或固態硬碟,其讀寫速度更快,但是資料無法永久保存,只要一重新開機,資料就會消失。
10 |
11 | 在 Linux 中,你有兩種方式可以新增一個記憶體硬碟:
12 |
13 | 1. 使用 `mount` 指令
14 | 2. 修改 `/etc/fstab` 檔案
15 |
16 | ## 使用 `mount` 指令
17 |
18 | 首先使用 `mkdir` 建立要掛載的資料夾:
19 |
20 | ```bash
21 | sudo mkdir /mnt/ramdisk
22 | ```
23 |
24 | 接著使用 `mount` 指令掛載記憶體硬碟:
25 |
26 | ```bash
27 | sudo mount -t tmpfs -o size=512M tmpfs /mnt/ramdisk
28 | ```
29 |
30 | 各項參數說明:
31 |
32 | - `-t tmpfs`:指定檔案系統為 tmpfs
33 | - `-o size=512M`:指定記憶體硬碟大小為 512MB
34 | - `/mnt/ramdisk`:指定掛載的目錄
35 |
36 | ## 修改 `/etc/fstab` 檔案
37 |
38 | 如果想再重開機之後自動設定 RAM Disk,可以修改 `/etc/fstab` 檔案。
39 |
40 | 首先使用 `root` 權限以 `vim` 開啟 `/etc/fstab` 檔案:
41 |
42 | ```bash
43 | sudo vim /etc/fstab
44 | ```
45 |
46 | 在檔案最後加入以下內容:
47 |
48 | ```text
49 | LABEL=cloudimg-rootfs / ext4 discard,commit=30,errors=remount-ro 0 1
50 | LABEL=BOOT /boot ext4 defaults 0 2
51 | LABEL=UEFI /boot/efi vfat umask=0077 0 1
52 | tmpfs /ramdisk tmpfs rw,nodev,nosuid,size=10M 0 0
53 | ```
54 |
55 | 編輯完成之後,按下 `Esc` 鍵,輸入 `:wq` 並按下 `Enter` 儲存並離開。
56 |
57 | 最後使用 `mount -a` 指令重新掛載 `/etc/fstab` 中的檔案系統:
58 |
59 | ```bash
60 | sudo systemctl daemon-reload
61 | sudo mount -a
62 | ```
63 |
64 | ## 參考資料
65 |
66 | - [Linux 中如何建立 RAM Disk,兩個快速簡單的方式,tmpfs、fstab | 適用各式 Linux 系統 | 挨踢實驗室](https://www.youtube.com/watch?v=xnSlYGeqlNA)
67 |
--------------------------------------------------------------------------------
/database/07-foreign-key.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Database
4 | nav_order: 7
5 | ---
6 |
7 | # MySQL、SQLite 與 PostgreSQL 在加上 Foreign Key 上的差異。
8 |
9 | 前幾天在寫 Laravel ORM 時,發現有一句關係查詢語句在 SQLite 中非常慢。
10 |
11 | ```sql
12 | SELECT *,
13 | (SELECT Count(*)
14 | FROM "comments" AS "laravel_reserved_1"
15 | WHERE "comments"."id" = "laravel_reserved_1"."parent_id") AS
16 | "children_count"
17 | FROM "comments"
18 | WHERE "post_id" = 100
19 | AND "parent_id" IS NULL
20 | ORDER BY "children_count" DESC
21 | ```
22 |
23 | 使用 `EXPLAIN` 發現查詢做了全表掃描,這時我才發現 `parent_id` 竟然沒有被加上索引 (Index)。
24 | 在 `parent_id` 加上索引之後,查詢速度就變快非常多,基本上不到一秒就能返回結果。
25 |
26 | 找資料才發現,**只有 MySQL 預設會幫 Foreign Key 加上索引,但是 SQLite 與 PostgreSQL 並不會這麼做**。
27 |
28 | 所以如果你在 Laravel Database Migration 中使用這個 `foreignId` 方法來建立 Foreign Key。
29 |
30 | ```php
31 | $table->foreignId('parent_id')
32 | ->nullable()
33 | ->constrained('comments')
34 | ->onDelete('cascade');
35 | ```
36 |
37 | 那麼就只有 MySQL 會加上索引,可以參考[MySQL 文件](https://dev.mysql.com/doc/refman/9.1/en/constraint-foreign-key.html)的說明。
38 |
39 | > MySQL requires that foreign key columns be indexed; if you create a table with a foreign key constraint but no index on a given column, an index is created.
40 |
41 | 相反的,PostgreSQL 與 SQLite 並不會這麼做,可以參考[PostgreSQL 文件](https://www.postgresql.org/docs/current/ddl-constraints.html)的說明。
42 |
43 | > Because this is not always needed, and there are many choices available on how to index, the declaration of a foreign key constraint does not automatically create an index on the referencing columns.
44 |
--------------------------------------------------------------------------------
/laravel/06-laravel-pennant.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Laravel
4 | nav_order: 6
5 | ---
6 |
7 | # Laravel Pennant
8 |
9 | Laravel Pennant 是一個用來管理功能旗標 (Feature Flag) 的輕量套件。
10 |
11 | 你可以用來管理新功能是否開放給使用者使用,當新功能尚未準備好時,可以先將功能關閉,或是只開放給部分使用者使用。
12 |
13 |
14 | ## 如何使用
15 |
16 | 假設你有一個新的頁面只想開放給管理員使用,你可以在 `AppServiceProvider.php` 的 `boot` 方法中加入以下的程式碼:
17 |
18 | ```php
19 | use Laravel\Pennant\Feature;
20 |
21 | public function boot(): void
22 | {
23 | Feature::define('new-checkout', function (User $user) {
24 | // 回傳一個 boolean 值
25 | return $user->isAdmin();
26 | });
27 | }
28 | ```
29 |
30 | 然後在你的控制器中,你可以使用 `Feature::active('new-checkout')` 來檢查使用者是否有權限訪問該頁面。
31 |
32 | ```php
33 | public function index()
34 | {
35 | if (Feature::active('new-checkout')) {
36 | return view('new-checkout');
37 | }
38 |
39 | return view('checkout');
40 | }
41 | ```
42 |
43 | 在預設情況下,Laravel Pennant 會將功能旗標的值儲存在資料庫中,但也可以儲存在記憶體中。
44 |
45 | 除了單純的 Boolean 值之外,你也能儲存其他型別的值,例如字串。
46 |
47 | ```php
48 | use Laravel\Pennant\Feature;
49 |
50 | public function boot(): void
51 | {
52 | Feature::define('new-button-color', function (User $user) {
53 | return Arr::random(['red', 'green', 'blue']);
54 | });
55 | }
56 | ```
57 |
58 |
59 | 之後就可以在你的控制器中使用 `Feature::value('new-button-color')` 來取得功能旗標的值。
60 |
61 | ```php
62 | public function index()
63 | {
64 | $buttonColor = Feature::value('new-button-color');
65 |
66 | return view('index', ['buttonColor' => $buttonColor]);
67 | }
68 | ```
69 |
70 | ## 參考資料
71 |
72 | - [Laravel Pennant](https://laravel.com/docs/master/pennant)
--------------------------------------------------------------------------------
/svelte/21-dynamic-attributes-component.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 21
5 | ---
6 |
7 | # Dynamic Attributes in Components
8 |
9 | 在 Laravel 中,我們可以使用 `$attributes` 來接收任何寫在 Component 上的屬性。
10 |
11 | ```blade
12 | {{-- button.blade.php --}}
13 |
14 | ```
15 |
16 | 任何寫在 Component 上的屬性,都會被 `$attributes` 接收。
17 |
18 | ```blade
19 |
25 | Click me
26 |
27 | ```
28 |
29 | 在 Svelte 中也有類似的功能,也就是 `HTMLInputAttributes`。
30 |
31 | ```svelte
32 |
45 |
46 |
47 |
50 |
51 |
52 |
58 |
59 |
60 | ```
61 |
--------------------------------------------------------------------------------
/svelte/02-props.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 2
5 | ---
6 |
7 | # Props
8 |
9 | ## Declaring Props
10 |
11 | 如果你需要將變數從父元件傳遞到子元件,你可以在子元件使用 `$props` 關鍵字來宣告子元件的 Props:
12 |
13 | ```svelte
14 |
17 | ```
18 |
19 | 然後你就可以在父元件中使用 `name="world"` 將 `world` 傳入子元件中:
20 |
21 | ```svelte
22 |
25 |
26 |
27 | ```
28 |
29 | ## Default Values
30 |
31 | 你可以在宣告 Props 的時候指定預設值:
32 |
33 | ```svelte
34 |
37 | ```
38 |
39 | ## Spread Props
40 |
41 | 假設我們有一個元件叫做 `PackageInfo`,它有四個 Props:`name`、`speed`、`version`、`website`。
42 |
43 | ```svelte
44 |
49 |
50 |
51 | The {name} package is {speed} fast. Download version {version} from
52 | npm and learn more here
53 |
54 | ```
55 |
56 | 你可以使用下面的方式將這四個 Props 傳入子元件:
57 |
58 | ```svelte
59 |
69 |
70 |
75 | ```
76 |
77 | 也可以使用 `...` 來將物件的所有屬性傳入子元件:
78 |
79 | ```svelte
80 |
81 | ```
82 |
--------------------------------------------------------------------------------
/rust/16-package-and-crates.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Rust
4 | nav_order: 16
5 | ---
6 |
7 | # Packages 與 Crates
8 |
9 | Crate 是 Rust 編譯器能執行成功的最小程式碼量。
10 |
11 | 舉個例子,新增一個 `main.rs` 檔案,內容為一個印出 `Hello World!` 簡單程式碼。
12 |
13 | ```rust
14 | fn main() {
15 | println!("Hello World!");
16 | }
17 | ```
18 |
19 | 使用 `rustc` 編譯這個檔案。
20 |
21 | ```bash
22 | rustc main.rs
23 |
24 | # 編譯成功之後會生成一個可執行檔案
25 | ./main
26 | ```
27 |
28 | 對 Rust 編譯器來說,這個 `main.rs` 檔案,就可以被看成是一個 Crate。
29 |
30 | ## Binary Crate 與 Library Crate
31 |
32 | Crate 有兩種形式,一種為 Binary Crate,另外一種為 Library Crate。
33 |
34 | Binary Crate 可以編譯並產生一個可執行程式,例如我們常用的命令行工具,前面筆記所有寫的程式碼都屬於 Binary Crate。
35 |
36 | Library Crate 不會有 `main` 函式,他們也不會被編譯成可執行程式,他們會定義許多功能,並用在其他專案上,例如之前用來產生一組隨機數的 `rand` 就是一種 Library Crate,通常 Rustaceans 提到 Crate 的時候,更多是指 Library Crate。
37 |
38 | ## Crate Root
39 |
40 | Crate Root 是一個源文件 (source file),Rust 編譯器會從從該源文件開始去建構你的 Crate Root Module。
41 |
42 | ## Package
43 |
44 | Package 是一個包含許多 Crate 並提供一組特定功能或方法的程式碼。
45 |
46 | Package 會包含 `Cargo.toml` 去描述這個 Package 需要依賴哪些 Crate。
47 |
48 | Cargo 實際上是一個軟體包,包含了你用來構建程式碼所需命令行工具的 Binary Crate,當中也包含這些命令行工具所需要的 Library Crate。
49 |
50 | Package 可以包含**許多 Binary Crate,但最多只能有一個 Library Crate**。
51 |
52 | Package 至少要包含一個 Crate,不論是 Binary Crate 或是 Library Crate 都可以。
53 |
54 | ## main.rs 與 lib.rs
55 |
56 | 使用 `cargo new` 新建一個初始 Package 時,Cargo 會自動認定 `src/main.rs` 為該 Package Binary Crate 的 Crate Root。
57 |
58 | > [!IMPORTANT]
59 | >
60 | > 這個 Crate 的名稱會與專案名稱相同。
61 |
62 | 如果是 `src/lib.rs` 就會認定為 Library Crate 的 Crate Root。
63 |
64 | Package 可以有多個 Binary Crate,但需要將其放置於 `src/bin` 目錄底下,每一個檔案都會被視為一個 Binary Crate。
65 |
--------------------------------------------------------------------------------
/svelte/17-module-context.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | parent: Svelte
4 | nav_order: 17
5 | ---
6 |
7 | # Module Context
8 |
9 | ## Sharing Data Between Components
10 |
11 | 在很少的情況下,你需要在不同的元件之間共享數據。
12 |
13 | 假設你有一個音樂播放元件,當你的畫面上有多個播放元件時,你會希望同一時間只有一個元件在播放音樂。
14 | 這就代表當我們在一個元件中點擊播放時,我們需要通知其他元件停止播放。
15 |
16 | 我們可以使用 `
25 |
26 |
27 |
28 |