2026-08-17

2026 Gmail SMTP 寄信設定教學

markdown 2026年 Gmail SMTP 寄信設定,兩步驟驗證與應用程式密碼指南 完整教學如何設定 Gmail SMTP 讓網站自動寄信,含 2026 最新 UI 路徑、應用程式密碼產生、OAuth 2.0 與 App Password 選擇 --- ### 📌 1. 為什麼網站要設定 Gmail SMTP? * **解決核心痛點**:網站若使用主機預設 `mail()` 函式發信,極易被判定為垃圾郵件甚至漏信。 * **主要好處**:透過 Google 伺服器寄信能大幅提升**信件送達率**,免費享有穩定安全的可靠基礎設施。 * **適用場景**:網站表單通知、訂單確認信、密碼重設信等。 --- ### ⏳ 2. 重要背景:舊版認證已全面停用 * **歷史轉折**:Google 已於 2025 年 3 月 14 日全面禁用傳統基本驗證(Basic Authentication)與低安全性應用程式(LSA)。 * **現行機制**:2026 年起,網站寄信**僅能使用 OAuth 2.0 或「應用程式密碼(App Password)」**。 --- ### 🛠️ 3. Gmail SMTP 三步驟設定流程 ``` [步驟 1] 啟用兩步驟驗證 ↓ [步驟 2] 產生 16 位數應用程式密碼 ↓ [步驟 3] 於網站後台填入 SMTP 參數 ``` #### **第一步:開啟 Google 兩步驟驗證** 1. 登入 Google 帳戶 ➔ 進入「管理你的 Google 帳戶」。 2. 點選左側「安全性」 ➔ 找到「兩步驟驗證」並完成開啟。 #### **第二步:取得應用程式密碼** 1. 前往 `[myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords)`(或在安全性頁面搜尋「應用程式密碼」)。 2. 輸入名稱(例如:`官網 SMTP`)並點選「建立」。 3. 複製產生的 **16 位數密碼**(⚠️ 此密碼僅顯示一次,請妥善保存)。 #### **第三步:填入網站後台設定** * **SMTP 主機**:`smtp.gmail.com` * **SMTP 埠號 (Port)**:`587` (TLS) 或 `465` (SSL) * **安全性加密**:`TLS` 或 `SSL` * **登入帳號**:你的完整 Gmail 信箱 * **登入密碼**:剛產生的 **16 位數應用程式密碼**(請勿填寫一般 Gmail 登入密碼) --- ### ⚠️ 4. 發信限制與常見問題排查 #### 📊 每日發信上限 (Rolling 24 Hours) * **免費版 Gmail**:約 **500 封/日**(寄給 20 人即扣 20 封額度)。 * **Google Workspace(付費版)**:高達 **2,000 封/日**。 #### 🔍 常見寄信失敗原因與檢查點 1. **密碼填錯**:錯填成 Gmail 帳戶登入密碼,而非 16 位數應用程式密碼。 2. **埠號與加密不搭配**:Port 587 需搭 TLS;Port 465 需搭 SSL。 3. **主機防火牆阻擋**:虛擬主機封鎖了 465 或 587 連線埠。 4. **超過發信上限**:當日觸及發信張數限制被暫時鎖定。

2022-01-26

Seq 硬碟滿了處理方式

markdown # Seq 硬碟滿了處理方式 硬碟使用量100%時無法使用Web介面刪除紀錄 1.停止Seq container ``` docker stop seq ``` 2.將docker掛載的data目錄過舊的資料刪除 ``` cd /data/Stream ls -lah|grep M|grep Dec|awk {'system("rm -f " $NF)'} ``` 3.啟動Seq container 此時連線web會產生錯誤 ``` // 20220126100328 // http://192.168.30.245:5880/#/events { "Error": "Seq is unavailable. Failed to initialize storage: Flare native storage failed (InternalError), data file `\"/data/Stream/stream.08d9cbdb889e7c00_08d9cbe81b451800.eeb0800c543741ada6e 96cbebcfc30a4.span\"` is missing." } ``` 4.在docker中執行 修復 ``` /bin/seq-server/Native/flaretl repair /data/Stream/ #可能需要執行多次 #直到出現 No storage problems were found ``` 5.重啟Seq 修復完成後沒重啟頁面依然會顯示錯誤 ## 參考: https://github.com/datalust/seq-tickets/discussions/1354 https://docs.datalust.co/docs/exporting-log-data

2021-09-30

React Custom hook - useQueryString

markdown Custom hook for simple get and set QueryString [demo](tps://codesandbox.io/s/react-custom-hooks-usequerystring-vmc2g) install package `react-router-dom` ```js // src/index.js import { StrictMode } from "react"; import ReactDOM from "react-dom"; + import { BrowserRouter as Router } from "react-router-dom"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( + + , rootElement ); ``` Add useQueryString.js ``` // src/hooks/useQueryString.js import React from "react"; import { useLocation, useHistory } from "react-router-dom"; import { shallowEqual } from "react-redux"; const useQueryString = () => { const location = useLocation(); const history = useHistory(); const { search } = location; const [value, setValue] = React.useState({}); React.useEffect(() => { let qs = Object.fromEntries(new URLSearchParams(search)); if (!shallowEqual(qs, value)) { setValue(qs); } }, [search]); // eslint-disable-line react-hooks/exhaustive-deps return { value, set: (params) => history.push({ pathname: location.pathname, search: new URLSearchParams({ ...value, ...params }).toString() }) }; }; export default useQueryString; ``` I use shallowEqual function check queryString is changed. ``` // shallowEqual.js in react-redux function is(x, y) { if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { return x !== x && y !== y; } } export default function shallowEqual(objA, objB) { if (is(objA, objB)) return true; if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } var keysA = Object.keys(objA); var keysB = Object.keys(objB); if (keysA.length !== keysB.length) return false; for (var i = 0; i < keysA.length; i++) { if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { return false; } } return true; } ``` ## How to use useQueryString() ``` const qs = useQueryString(); ``` #### set value to url ``` qs.set({"name": "value"}); qs.set({"age": "19"}); ``` #### get value ``` console.log(qs.value.name); console.log(qs.value.age); ``` ## References * [QueryString - Wikipedia](https://en.wikipedia.org/wiki/Query_string) * [React 中優雅使用網址參數 Query String - Jul 7, 2020 · Ryan Hsu](https://medium.com/itsoktomakemistakes/react-%E4%B8%AD%E5%84%AA%E9%9B%85%E4%BD%BF%E7%94%A8%E7%B6%B2%E5%9D%80%E5%8F%83%E6%95%B8-query-string-540bacd08486) * [React Router, why useLocation and useHistory might return undefined](https://flaviocopes.com/react-router-uselocation-usehistory-undefined/) * [React Router](https://reactrouter.com/web/guides/quick-start) * [React Redux](https://react-redux.js.org/)

2021-04-07

ValueObject as Entity ID, EntityTypeConfiguration Sample

markdown 問題:https://stackoverflow.com/questions/53328201/ef-core-non-primitive-type-value-object-as-primary-key 參考來源:https://github.com/dotnet/efcore/issues/13669 ``` public class User { public UserId Id { get; set; } public Credentials Credentials { get; set; } } public class UserId { private UserId() { } private UserId(long id) { Id = id; } public long Id { get; private set; } public static implicit operator long(UserId beaconId) { return beaconId.Id; } public static implicit operator UserId(long id) { return new UserId(id); } public override bool Equals(object obj) { if (obj == null) { return false; } var userId = (UserId) obj; return this.Id == userId.Id; } public override int GetHashCode() { return Id.GetHashCode(); } } public class TestDbContext: DbContext { public TestDbContext(DbContextOptions options): base(options) { } public DbSet Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { var userModelBuilder = modelBuilder.Entity(); userModelBuilder.Property(x => x.Id).HasConversion(x => x.Id, value => new UserId(value)).HasColumnName("Id").IsRequired(); userModelBuilder.OwnsOne(x => x.Credentials, c => { c.Property(x => x.Email); c.Property(x => x.Password); }); } } ```

2021-03-31

dotnet core get k8s self POD name

markdown k8s 中的 dotnet core 應用取得 POD name 三種方式取得的結果相同 ``` class Program { static async Task Main(string[] args) { Console.WriteLine("MachineName:"+Environment.MachineName); Console.WriteLine("HostName:"+ System.Net.Dns.GetHostName()); Console.WriteLine("ENV K8S_POD_NAME:"+Environment.GetEnvironmentVariable("K8S_POD_NAME")); await Task.Run(() =〉 Thread.Sleep(Timeout.Infinite)); } } ``` result on dotnet 3.1.11-alpine3.12 ``` MachineName:mqsend-7d9579cc47-kld6v HostName:mqsend-7d9579cc47-kld6v ENV K8S_POD_NAME:mqsend-7d9579cc47-kld6v ``` ENV需要在deployment時額外加入參考 ``` spec: containers: - env: - name: K8S_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name ``` ref - https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/#use-container-fields-as-values-for-environment-variablesƒ

2021-01-29

Adding Git-Bash to the new Windows Terminal

markdown 在 Terminal 按下 `ctrl` + `,`,會開啟 `settings.json` ``` { "$schema": "https://aka.ms/terminal-profiles-schema", "defaultProfile": "{00000000-0000-0000-ba54-000000000001}", "profiles": { "defaults": { // Put settings here that you want to apply to all profiles }, "list": [ // put one of the configuration below right here ] } } ``` 在 list 的區段中加入,依據正確git安裝路徑取消`commandline`和`icon`的註解 ``` { "guid": "{00000000-0000-0000-ba54-000000000002}", //"commandline": "%PROGRAMFILES%/git/usr/bin/bash.exe -i -l", //"commandline": "%USERPROFILE%/AppData/Local/Programs/Git/git-bash.exe -l -i", "commandline": "%USERPROFILE%/AppData/Local/Programs/Git/bin/sh.exe --login", //"commandline": "%USERPROFILE%/AppData/Local/Programs/Git/bin/bash.exe -l -i", //"commandline": "%USERPROFILE%/scoop/apps/git/current/usr/bin/bash.exe -l -i", //"icon": "%PROGRAMFILES%/Git/mingw64/share/git/git-for-windows.ico", "icon": "%USERPROFILE%/AppData/Local/Programs/Git/mingw64/share/git/git-for-windows.ico", // "icon": "%USERPROFILE%/apps/git/current/usr/share/git/git-for-windows.ico", "name": "Bash", "startingDirectory": "%USERPROFILE%" } ``` # Reference # [Adding Git-Bash to the new Windows Terminal](https://stackoverflow.com/questions/56839307/adding-git-bash-to-the-new-windows-terminal)

2020-10-20

新的 Windows Terminal 視窗分割

markdown # New Windows Terminal Windows 終端機是一種現代化、快速、高效、功能強大且具生產力的終端應用程式,適合命令列工具和 Shell (像是命令提示字元、PowerShell 和 WSL) 的使用者。主要功能包括多個索引標籤、窗格、Unicode 和 UTF-8 字元支援、GPU 加速的文字呈現引擎,以及自訂主題、樣式和設定。 可以直接在 Window 10 的 Microsoft Store 下載。 * 下載安裝[Windows Terminal](https://www.microsoft.com/zh-tw/p/windows-terminal/9n0dx20hk701?activetab=pivot:overviewtab) * [Source Code](https://github.com/microsoft/terminal) # 視窗分割 * 自動分割視窗:Alt + Shift + D * 建立一個新的水平分割:Alt + Shift + -(英文鍵上排的減號) * 建立一個新的垂直分割:Alt + Shift + +(英文鍵上排的加號) * 移動遊標至其它視窗:Alt + Left, Alt + Right, Alt + Up, Alt + Down * 調整遊標所在視窗的大小:Alt + Shift + Left, Alt + Shift + Right, Alt + Shift + Up, Alt + Shift + Down ## 或是 Ctrl + Shift + P 開啟輸入命令名稱的快捷輸入框 1. 選擇 Split Pane... 2. 選擇要開啟的Shell環境「Azure Cloud Shell、Windows PowerShell、命令提示字元、或是自己安裝的 WSL 子系統(Linux)」 3. 選擇要分割的視窗方向,「自動,水平、垂直」 * 自動:依視窗大小自動做水平或垂直分割 * 水平:分割成上和下 * 垂直:分割成左和右 # 關閉分割 * 將遊標所在的視窗關閉:Ctrl + Shift + W # 透過指令啟動一個視窗,裡面已經分割成三個 ``` wt -p "Command Prompt" `; split-pane -p "Windows PowerShell" `; split-pane -H wsl.exe ``` # 參考 * [Using command-line arguments for Windows Terminal](https://github.com/MicrosoftDocs/terminal/blob/master/TerminalDocs/command-line-arguments.md) * [The New Windows Terminal Is Ready; Here’s Why It’s Amazing](https://www.howtogeek.com/673729/heres-why-the-new-windows-10-terminal-is-amazing/) - Chris Hoffman, May 19, 2020

adsense