整合階段 1 · 操作指南 · ← 寫入權矩陣 · 檢核清單 · 路線圖
現在 16 個網站、一支 Windows 桌面程式、還有 68 個 stored procedure, 全都拿著同一把萬能鑰匙在寫資料庫——而那把鑰匙不只開得了 goalu, 它開得了這台機器上的每一個資料庫。 這頁講怎麼把它拆掉:先做哪一步、每站給什麼權限、怎麼驗、出事怎麼退。
權限清單不是人工寫的,是從寫入權矩陣直接算出來的; 矩陣重生,這頁的 SQL 就跟著變。
問題不是「有很多站連同一個資料庫」——那還算正常。問題是每一站都拿到了全部權限, 而且那個「全部」比想像中大得多。
david/tom,
而這兩個帳號實測是 GRANT ALL PRIVILEGES ON *.*——不是「goalu 的全部權限」,
是這台機器上每一個資料庫的全部權限。
嘉南的官網若被打穿,攻擊者拿到的不只是客戶資料,還包括補習班系統、繪本站、學習王的資料庫。
一般會想成「反正都是自己人的站,共用帳號沒差」。但共用帳號的代價不在信任,在事故半徑:
任何一站的 SQL injection、任何一個被猜到的密碼、任何一次手滑的 DELETE,
影響範圍都是全機而不是該站。而 david 的密碼明碼寫在 5 個站的 PHP 原始碼裡。
矩陣顯示 47 張表有兩個以上的寫入者,其中 hcust(客戶主檔)有 7 個。
但真正的問題不是「7 個地方會改客戶資料」,而是每一次改都會觸發一整串連鎖寫入——
而那些連鎖是寫在資料庫裡的,改程式碼看不到。
hcust 上掛了 6 個 AFTER UPDATE trigger,
任何一站改客戶資料,都會自動再寫 hcust_trd(業務異動核)與 call_data(電訪名單)。
這些 trigger 寫在資料庫裡,看程式碼永遠看不到——
也就是說「這支程式只改了客戶地址」在實際上等於「同時動了帳務與電訪」。
這五步有明確的依賴關係:第 0 步隨時可做且立刻降風險, 中間三步可以慢慢推進,但最後一步(停用 david)有兩個前置門檻—— 桌面程式沒處理完、DEFINER 沒換完,就停 david 一定出事。
這一步不動任何程式碼、不改任何 .env、不需要停機, 卻立刻把事故半徑從「整台 MariaDB」縮到「goalu 一個庫」。應該最先做。
-- 先看清楚現在有什麼(一定要先看,下面有雷)
SHOW GRANTS FOR 'david'@'%';
SHOW GRANTS FOR 'david'@'localhost';
SHOW GRANTS FOR 'tom'@'%';
-- 收窄
REVOKE ALL PRIVILEGES, GRANT OPTION ON *.* FROM 'david'@'%';
GRANT ALL PRIVILEGES ON `goalu`.* TO 'david'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION ON *.* FROM 'tom'@'%';
GRANT ALL PRIVILEGES ON `goalu`.* TO 'tom'@'%';
FLUSH PRIVILEGES;
david@localhost 另外還有 shop/starmath/skilltree 的授權,
tom 另有 invoice/rotary/kn。
這些是獨立的授權條目,收 *.* 不會動到它們——
但如果某個站其實是靠 *.* 在存取那些庫(而不是靠單庫授權),收完就會斷。
所以先 SHOW GRANTS 對照,再動手。
驗證 收窄後跑 SHOW GRANTS FOR 'david'@'%'; 應只剩 USAGE ON *.* 與 ALL ON goalu.*;
然後開幾個站的主要頁面跑一輪(尤其 callhub、muxue、lk——它們用自己的帳號,本來就不該受影響)。
回退 GRANT ALL PRIVILEGES ON *.* TO 'david'@'%'; 一行還原,秒退。
每站一個帳號、一次一站、一站一個 commit——這樣任何一站出事都能單獨回退, 不會變成「十五站一起壞、不知道是哪一站」。 建議從碰 goalu 最少的站開始,先把流程走順再處理大的。
| 順位 | 站台 | 新帳號 | 寫入表 | 唯讀表 | 備註 |
|---|---|---|---|---|---|
| 1 | story | story_app 最好下手 | 0 | 1 | 幾乎不碰 goalu,用來把流程走順 |
| 2 | kng.tw | kngtw_app 留意 | 1 | 2 | 金流 callback 在這,切換後務必實測一筆 |
| 3 | pbx | pbx_app | 1 | 4 | 兩套 pbx 並存,兩個都要換 |
| 4 | pbx-laravel12 | pbx12_app | 1 | 4 | 同上 |
| 5 | delivery | delivery_app | 2 | 6 | — |
| 6 | html/report | report_app 最好下手 | 2 | 42 | 讀 42 張表但只寫 2 張,典型的唯讀型站台 |
| 7 | html/sony | sony_app 留意 | 3 | 0 | 明碼密碼寫在 admin/_inc/config.php,一併改 env |
| 8 | html/sales | sales_app 留意 | 6 | 11 | 明碼密碼寫在 include/cndb.php,一併改 env |
| 9 | forms | forms_app 留意 | 7 | 8 | 對外收單入口,切換後實測一筆完整表單 |
| 10 | inventory | inventory_app | 9 | 8 | 目前用 tom 不是 david |
| 11 | www.kngoatmilk.com | www_app 留意 | 11 | 17 | 官網+對外 API,含金流 callback |
| 12 | line | line_app 留意 | 11 | 20 | 訊息量最大的站,切換挑離峰 |
| 13 | invoice | invoice_app 留意 | 17 | 28 | 發票開立,月結前後避開 |
| 14 | ERP(kngoatmilk) | erp_app 最後做 | 63 | 29 | 業務主力,應是多數表的唯一 writer。前面十四站的雷都踩完再動它 |
| 15 | html/salary | salary_legacy_app 最後做 | 87 | 45 | 總務室本體。不一次拆,隨階段 5 逐模組搬遷 |
為什麼這個順序 寫入表越少,出錯面越小、驗證越快。 把最容易的放前面不是偷懶,是讓流程在低風險的站台上先跑通, 等到動 ERP(63 張)和 html/salary(87 張)時,該踩的雷已經踩完了。
david 的權限都還在,所以「把 .env 改回去」永遠是有效的回退手段。
觀察期沒過就急著回收,等於自斷退路。
資料庫裡有 68 個有寫入的 stored procedure、20 個排程 EVENT(全部啟用中)、8 個 TRIGGER,
它們都以 DEFINER 的身分執行,而目前多數是 david@%。
這代表:就算 16 個站全都換好帳號,只要停用 david,這些東西會全部失效——
包括每日估奶、電訪名單輪替、應收自動過帳、還有算薪。
-- 先盤點誰的 DEFINER 是 david
SELECT ROUTINE_TYPE, ROUTINE_NAME, DEFINER
FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='goalu' AND DEFINER LIKE 'david%';
SELECT EVENT_NAME, DEFINER, STATUS
FROM information_schema.EVENTS WHERE EVENT_SCHEMA='goalu';
SELECT TRIGGER_NAME, DEFINER
FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA='goalu';
-- 建一個專門跑排程的身分
CREATE USER 'goalu_scheduler'@'localhost' IDENTIFIED BY '<PASSWORD>';
GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON `goalu`.* TO 'goalu_scheduler'@'localhost';
MySQL/MariaDB 沒有「改 DEFINER」這種語法,必須用新身分重新 CREATE 一次
(DROP 再 CREATE DEFINER=...)。所以動之前一定要先完整備份:
mysqldump --routines --events --triggers --no-data goalu > goalu-routines-backup.sql。
而且 EVENT 在 DROP 到 CREATE 的空窗期如果剛好到執行時間,那一輪就跳過了。
挑離下次執行最遠的時段做,做完用 information_schema.EVENTS.LAST_EXECUTED 確認有跑。
驗證 換完等一個完整週期(多數是每日),比對 LAST_EXECUTED 有更新、
目標表的 UPDATE_TIME 有動、資料筆數符合預期。不要只看「沒報錯」——
排程失效通常是安靜的。
這是整個階段 1 唯一技術上沒有標準答案、需要業務決策的一項。
sys_log 顯示它近 90 天有 214 筆操作、8 台電腦、16 個使用者,最近一次是 2026-08-17——
使用量低,但確實還活著,而且碰的是客戶主檔與薪資。
| 表單 | 對應業務 | 筆數 | 人數 | 影響的表 |
|---|---|---|---|---|
frmhcust | 客戶維護 | 172 | 13 | hcust(+6 個 trigger 連鎖) |
frmhum / frmhum_msar / frmhum_rpt* | 人資與薪資 | 13 | 3 | hum、hum_msar |
frmdept_work0 / frmdept_work | 部門工作 | 10 | 2 | hum_* |
frmhcust_ant_err / frmgcust_ant_edit | 應收帳款調整 | 9 | 2 | hcust_ant、gcust_ant |
frmsales_hcust_qty | 業務業績 | 3 | 1 | sales_hcust_qty |
frmhcust_mulist2 / frmhcust_mdqty / frmhcust_close_chk / frmvend | 名單/數量/結清/廠商 | 7 | — | 家戶相關 |
三個選項:
legacy_desktop(最省事)——照上表的表清單授權,
它就能繼續跑,同時 david 可以停用。缺點是這支程式繼續存在,帳號也得跟著留。frmhcust 一支就佔 172 筆/13 人,
搬完剩下的用量非常低。這條路最乾淨,但要花時間。frmhum_msar 碰的是薪資,
而且 8 月才用過,停掉可能在月結時才爆。
frmhum_msar 會改 hum_msar,
表示新薪資站不是薪資資料的唯一入口。
9 月算薪驗收要把這件事算進去,否則會出現「新站顯示的跟實際不符,但查不出誰改的」。
怎麼判斷它退場了 查 sys_log,不要看 processlist——
低頻使用的程式多數時間不在線上,看起來會像已經死了。
SELECT comp_name, prg_id, COUNT(*) n, MAX(mdate) last
FROM goalu.sys_log
WHERE mdate >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY 1,2 ORDER BY n DESC;
先確認兩個門檻都過了(Step 2 DEFINER 換完、Step 3 桌面程式處理完),然後:
REVOKE ALL PRIVILEGES ON goalu.* FROM 'david'@'%';,帳號留著。
這樣出事的話一行 GRANT 就回來了。information_schema.PROCESSLIST 有沒有 david 的連線、
以及 sys_log 有沒有新增(桌面程式)。DROP USER。
MariaDB 的帳號比對是先精確後萬用:如果同時存在 david@% 和 david@localhost,
從本機連線的會是後者。所以「我明明收了 david@% 的權限,怎麼還是寫得進去」
多半是因為 david@localhost 還在。兩個都要處理。
下面每一段都是從寫入權矩陣直接算出來的,
不是人工整理的。點開即可複製。完整檔案:
grants-draft.sql。
① 密碼是 <PASSWORD> 佔位,執行時換掉,並寫進該站 .env 而不是 PHP 原始碼
(html/salary、report、sales、sony、kng.tw 五站目前是明碼寫死的)。
② 一站一次、一站一 commit。
③ 這是草稿不是定論:靜態掃描抓不到動態表名,切換後一定要跑該站的 smoke test。
④ 出現 DROP 權限的地方要特別看一眼——那是 TRUNCATE 需要的,
給了等於該站能砍表,確認真的在用再給。
繪本站 Laravel(目前用 tom,幾乎不碰 goalu)
-- story → story_app
-- 繪本站 Laravel(目前用 tom,幾乎不碰 goalu)
CREATE USER IF NOT EXISTS 'story_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 唯讀
GRANT SELECT ON `goalu`.`pbx_call_record` TO 'story_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'story_app'@'localhost';集團入口與金流 callback(明碼密碼待改 env)
-- kng.tw → kngtw_app
-- 集團入口與金流 callback(明碼密碼待改 env)
CREATE USER IF NOT EXISTS 'kngtw_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, UPDATE ON `goalu`.`i_form` TO 'kngtw_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`hcust` TO 'kngtw_app'@'localhost';
GRANT SELECT ON `goalu`.`i_form_track` TO 'kngtw_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'kngtw_app'@'localhost';PBX 站 Laravel
-- pbx → pbx_app
-- PBX 站 Laravel
CREATE USER IF NOT EXISTS 'pbx_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE ON `goalu`.`pbx_call_record` TO 'pbx_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`customers` TO 'pbx_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust` TO 'pbx_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_trd` TO 'pbx_app'@'localhost';
GRANT SELECT ON `goalu`.`pbx_token` TO 'pbx_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'pbx_app'@'localhost';PBX Laravel 12 版
-- pbx-laravel12 → pbx12_app
-- PBX Laravel 12 版
CREATE USER IF NOT EXISTS 'pbx12_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE ON `goalu`.`pbx_call_record` TO 'pbx12_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`customers` TO 'pbx12_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust` TO 'pbx12_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_trd` TO 'pbx12_app'@'localhost';
GRANT SELECT ON `goalu`.`pbx_token` TO 'pbx12_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'pbx12_app'@'localhost';配送站 Laravel
-- delivery → delivery_app
-- 配送站 Laravel
CREATE USER IF NOT EXISTS 'delivery_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE ON `goalu`.`delivery_list` TO 'delivery_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust` TO 'delivery_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`address_book_indexes` TO 'delivery_app'@'localhost';
GRANT SELECT ON `goalu`.`customers` TO 'delivery_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_covers` TO 'delivery_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'delivery_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_sales` TO 'delivery_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'delivery_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'delivery_app'@'localhost';報表站(傳統 PHP,明碼密碼待改 env)
-- html/report → report_app
-- 報表站(傳統 PHP,明碼密碼待改 env)
CREATE USER IF NOT EXISTS 'report_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`ibadtrdcount` TO 'report_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`sys_sales` TO 'report_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`address_book_indexes` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`address_books` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`apart_comp` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`apart_sales` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`call_data` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`call_list` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`change_resources` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_gs` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`customers` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_dvs` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_orders` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_sales` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_dv` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_ant` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dorder` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dorder_rt` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dv` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_token` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_trd` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`hum` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`i_day_qty` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`i_deadline` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`i_dv_record_copy3` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`i_visit_data` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`i_visit_group` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`iexpense` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`line_chat` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`order_records` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`pbx_call_record` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`pbx_call_transcript` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`rpt_delivery_achievement` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`rt10_list` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`sales_dvms` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`sales_report` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`trades` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`weekly_order_items` TO 'report_app'@'localhost';
GRANT SELECT ON `goalu`.`weekly_orders` TO 'report_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'report_app'@'localhost';皇銘 SONY 後台(明碼密碼待改 env)
-- html/sony → sony_app
-- 皇銘 SONY 後台(明碼密碼待改 env)
CREATE USER IF NOT EXISTS 'sony_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, UPDATE ON `goalu`.`sony_admin_user` TO 'sony_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sony_line_chat` TO 'sony_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sony_line_user` TO 'sony_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'sony_app'@'localhost';業務站(傳統 PHP,明碼密碼待改 env)
-- html/sales → sales_app
-- 業務站(傳統 PHP,明碼密碼待改 env)
CREATE USER IF NOT EXISTS 'sales_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust` TO 'sales_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_dorder` TO 'sales_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_trd` TO 'sales_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_visit_data` TO 'sales_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`i_visit_group` TO 'sales_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`payment` TO 'sales_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`apart_comp` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`apart_sales` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`gprod` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_ant` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dorder_rt` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dv` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_qtyf` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`i_deadline` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`sales_report` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_sales` TO 'sales_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'sales_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'sales_app'@'localhost';表單站 Laravel
-- forms → forms_app
-- 表單站 Laravel
CREATE USER IF NOT EXISTS 'forms_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT ON `goalu`.`SMS_log` TO 'forms_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust` TO 'forms_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_dorder` TO 'forms_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_trd` TO 'forms_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`i_form` TO 'forms_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`i_form_track` TO 'forms_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`rt10_list` TO 'forms_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`apart_comp` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`apart_sales` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`gprod` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`i_deadline` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`payment` TO 'forms_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_sales` TO 'forms_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'forms_app'@'localhost';庫存站 Laravel(目前用 tom)
-- inventory → inventory_app
-- 庫存站 Laravel(目前用 tom)
CREATE USER IF NOT EXISTS 'inventory_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, DELETE ON `goalu`.`date_tags` TO 'inventory_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`gcust_ml` TO 'inventory_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`hcust` TO 'inventory_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`hcust_trd` TO 'inventory_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`inventory_details` TO 'inventory_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`inventory_items` TO 'inventory_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`inventory_main` TO 'inventory_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`inventory_side_products` TO 'inventory_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`toy_stocks` TO 'inventory_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`daily_dvs` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_orders` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_ml_items` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`hum` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`procedure_performance_logs` TO 'inventory_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'inventory_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'inventory_app'@'localhost';官網與對外 API
-- www.kngoatmilk.com → www_app
-- 官網與對外 API
CREATE USER IF NOT EXISTS 'www_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE ON `goalu`.`SMS_log` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_dorder` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_token` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_trd` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_form` TO 'www_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`i_form_track` TO 'www_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`line_chat` TO 'www_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`message_logs` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`payment` TO 'www_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`trades` TO 'www_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`address_book_indexes` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`address_books` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`cub_rcv` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`customers` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_orders` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_cls` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_dorder` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`gprod` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_amt_pre` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_ant` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dv` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_qtyf` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`i_deadline` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`i_rv_tp_area` TO 'www_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'www_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'www_app'@'localhost';LINE 站 Laravel
-- line → line_app
-- LINE 站 Laravel
CREATE USER IF NOT EXISTS 'line_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT ON `goalu`.`address_book_indexes` TO 'line_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`bug_reports` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`customers` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_token` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_form` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`line_chat` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`line_group_link` TO 'line_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`line_menu_clicks` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`line_unfollow` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`line_unlogin_assign` TO 'line_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`payment` TO 'line_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`address_books` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`cub_rcv` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_orders` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`gprod` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_amt_rcv` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_ant` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dv` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_qtyf` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`i_deadline` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`invoice_details` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`invoices` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`line_unlogin_list` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`posting_log` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`trades` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`weekly_order_items` TO 'line_app'@'localhost';
GRANT SELECT ON `goalu`.`weekly_orders` TO 'line_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'line_app'@'localhost';發票站 Laravel
-- invoice → invoice_app
-- 發票站 Laravel
CREATE USER IF NOT EXISTS 'invoice_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT, UPDATE ON `goalu`.`SMS_log` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`apart_comp` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`apart_sales` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`call_empty` TO 'invoice_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`gcust_ant` TO 'invoice_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`hcust_ant` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_trd` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hum` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hum_con` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hum_nrdd` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hum_rel` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`i_deadline` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`iexpense` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`invoice_details` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`invoices` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sys_sales` TO 'invoice_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sys_uemp` TO 'invoice_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`address_book_indexes` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`call_data` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`call_list` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_gs` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_pms` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_price_details` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_prices` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`customers` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_orders` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_orders` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_dv` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_price` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`gprod` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_amt_pre` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_amt_rcv` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dorder` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_qtyf` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hum_special_off` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`hum_wktime` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`i_sales_car` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`i_sales_daily` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`marea` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`pbx_call_record` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_pm` TO 'invoice_app'@'localhost';
GRANT SELECT ON `goalu`.`trades` TO 'invoice_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'invoice_app'@'localhost';舊 ERP——應為多數業務表的唯一 writer,最後拆
-- ERP(kngoatmilk) → erp_app
-- 舊 ERP——應為多數業務表的唯一 writer,最後拆
CREATE USER IF NOT EXISTS 'erp_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, UPDATE, DELETE ON `goalu`.`address_book_indexes` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`address_books` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`change_resources` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`contacts` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`cron_alerts` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`cub_rcv` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`cub_record` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`customer_evens` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`customer_pms` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`customer_pre_jobs` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`customer_price_details` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`customer_price_templates` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`customer_prices` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`customers` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`daily_dvs` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`daily_gprod_jobs` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`daily_gprods` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`daily_orders` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`dv_covers` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`dv_dvms` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`dv_orders` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`element_events` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`estimate_runs` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`gcust_ant` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_cls` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_dorder` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`gcust_dv` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_even` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_ml` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_mu` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`gcust_narea_ant` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_price` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_price_classes` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_trd` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`gprod` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_amt_pre` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_amt_rcv` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hcust_ant` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_ant_mk` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_dorder` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hcust_dv` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_even` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hcust_qtyf` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust_trd` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hcust_trd_del` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hcust_trd_rt` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`i_credit_qtyf` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_deadline` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_dvm_yn` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`i_prod_no_dvs` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`invoices` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`marea` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`marea_covers` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`order_records` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`price_templates` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sales_dept_performance_targets` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`sales_hcust_qty` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sys_pm` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`tasks` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`trades` TO 'erp_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`weekly_orders` TO 'erp_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`SMS_log` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`audits` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_gs` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_hs` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`daily_orders_shadow` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_orders_shadow` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_unit_price` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_close` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_ml_items` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_close` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_token` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`hum` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`i_day_qty` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`i_rv_tp_area` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`i_steambox_pos` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`invoice_details` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`jobs` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`line_chat` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`line_group_link` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`marea_cover_details` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`message_logs` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`posting_log` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`sales_dvm_products` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`sales_dvms` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_sales` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`task_results` TO 'erp_app'@'localhost';
GRANT SELECT ON `goalu`.`weekly_order_items` TO 'erp_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'erp_app'@'localhost';總務室本體,87 張表寫入。不一次拆,隨階段 5 逐模組搬
-- html/salary → salary_legacy_app
-- 總務室本體,87 張表寫入。不一次拆,隨階段 5 逐模組搬
CREATE USER IF NOT EXISTS 'salary_legacy_app'@'localhost' IDENTIFIED BY '<PASSWORD>';
-- 寫入
GRANT SELECT, INSERT ON `goalu`.`SMS_log` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE, DELETE ON `goalu`.`address_book_indexes` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE, DELETE ON `goalu`.`address_books` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`apart_sales` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`birthday_card_done` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`bug_reports` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`call_data` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`call_empty` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`call_empty_auto` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`change_resources` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`contacts` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`cron_alerts` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`cron_job_logs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`cron_jobs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`cron_permission_snapshots` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`cub_estimate` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`cub_rcv` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`cub_record` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`customer_evens` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE, DROP ON `goalu`.`customer_phone_index` TO 'salary_legacy_app'@'localhost'; -- ⚠️ DROP 是 TRUNCATE 需要的,確認真的在用
GRANT SELECT, INSERT ON `goalu`.`customer_price_details` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`customer_prices` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE, DELETE ON `goalu`.`customers` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`customers_gps` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`daily_orders` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`dv_orders` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`fax_print_log` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`fax_rotation` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`gcust` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`gcust_ant` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`gcust_ant_single` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`gcust_atm_posted` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`gcust_cls` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`gcust_narea_idno` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`hcust` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`hcust_token` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hcust_token_archive` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`hcust_trd` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`hum` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`hum_related_cust` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`hum_wktime` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_form` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`i_send_msg` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`i_steambox_pos` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`i_visit_data` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`i_visit_group` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`invoice_details` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`invoices` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`line_chat` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`line_chat_auto_reply_log` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`line_chat_summary` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`line_cs_quality_cases` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`line_cs_quality_jobs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`line_group_link` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`line_notice` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DROP ON `goalu`.`line_unlogin_list` TO 'salary_legacy_app'@'localhost'; -- ⚠️ DROP 是 TRUNCATE 需要的,確認真的在用
GRANT SELECT, UPDATE ON `goalu`.`marea` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`message_logs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`net_perf_ad_spend` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`net_perf_config` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`net_perf_settlement` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`notes` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`oldprice40_review_notes` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`order_records` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`orders_form_other` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`page_daily_access` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`page_info` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`payment` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`payment_attempts` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`payment_callback_failures` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`pbx_call_record` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`pbx_call_transcript` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`punch_hum_anyip` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`renewal_sms_log` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`rt10_list` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`salary2txt_log` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sony_line_chat` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE ON `goalu`.`sony_line_user` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT ON `goalu`.`sor_config` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`sor_extra` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, DELETE ON `goalu`.`sor_maintain_school` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE ON `goalu`.`sor_price` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE, DROP ON `goalu`.`sys_sidebar_menu` TO 'salary_legacy_app'@'localhost'; -- ⚠️ DROP 是 TRUNCATE 需要的,確認真的在用
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`trades` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, DELETE ON `goalu`.`weekly_order_items` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, UPDATE, DELETE ON `goalu`.`weekly_orders` TO 'salary_legacy_app'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON `goalu`.`youtube_summaries` TO 'salary_legacy_app'@'localhost';
-- 唯讀
GRANT SELECT ON `goalu`.`ai_assets` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`ai_jobs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`apart_comp` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`call_list` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_gs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_hs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`customer_pms` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`delivery_list` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_covers` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_dvms` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`dv_unit_price` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_dorder` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_dv` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_ml` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_ml_items` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_mu` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gcust_trd` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gprod` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`gpu_jobs` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_amt_pre` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_amt_rcv` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_ant` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dorder` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_dv` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hcust_qtyf` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hum_con` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hum_msar` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`hum_nrdd` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`i_deadline` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`i_dv_record_copy3` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`i_dvm_yn` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`i_form_track` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`i_prod` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`i_rv_tp_area` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`inventory_details` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`inventory_items` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`inventory_main` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`line_menu_clicks` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`line_unfollow` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`line_unlogin_assign` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`oldprice40_charged_cache` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`rpt_delivery_achievement` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_pm` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_sales` TO 'salary_legacy_app'@'localhost';
GRANT SELECT ON `goalu`.`sys_uemp` TO 'salary_legacy_app'@'localhost';
-- 驗證:新帳號連線後 SELECT 一張域外表,必須噴權限錯誤
-- SHOW GRANTS FOR 'salary_legacy_app'@'localhost';每一步都有對應的驗證方式。共同原則是:不要只看「沒報錯」——權限問題經常是安靜地錯。
| 步驟 | 驗證方式 | 通過標準 |
|---|---|---|
| STEP 0 | SHOW GRANTS FOR 'david'@'%'; |
只剩 USAGE ON *.* + ALL ON goalu.*;callhub/muxue/lk 各站功能正常 |
| STEP 1 | 用新帳號連線,SELECT 一張域外表 | 必須噴權限錯誤。查得到就是還沒隔離成功 |
| STEP 1 | 該站 smoke test(登入+一個主功能的完整寫入流程) | 功能正常,且 error log 無 SQLSTATE[42000]/command denied |
| STEP 2 | information_schema.EVENTS.LAST_EXECUTED |
換 DEFINER 後一個週期內有更新,且目標表 UPDATE_TIME 有動 |
| STEP 3 | goalu.sys_log 近 30 天筆數 |
歸零(若選擇退場);或確認 legacy_desktop 帳號可正常運作 |
| STEP 4 | PROCESSLIST + 各站 error log + sys_log |
連續 7 天零 david 連線、零異常 |
-- 還有誰在用 ALL ON *.*(應該逐漸歸零)
SELECT CONCAT(user,'@',host) AS account
FROM mysql.global_priv
WHERE JSON_VALUE(Priv,'$.access') > 0;
-- goalu 各帳號的授權範圍
SELECT GRANTEE, TABLE_SCHEMA, COUNT(*) AS n
FROM information_schema.SCHEMA_PRIVILEGES GROUP BY 1,2;
-- 桌面程式還活著嗎
SELECT COUNT(*) AS ops_30d, COUNT(DISTINCT comp_name) AS pcs, MAX(mdate) AS last_seen
FROM goalu.sys_log WHERE mdate >= DATE_SUB(CURDATE(), INTERVAL 30 DAY);
| 雷 | 症狀 | 怎麼避 |
|---|---|---|
同名 @localhost 影子帳號 |
收了 david@% 的權限卻沒效果 |
MariaDB 先精確後萬用,@localhost 會勝出。兩個都要查、都要處理 |
| DEFINER 沒換就停 david | 排程與 trigger 安靜失效,通常月結才發現 | STEP 2 是 STEP 4 的硬門檻,順序不可調 |
| 桌面程式沒盤點 | 停用 david 後 16 個人無法作業 | 用 sys_log 判活,不要用 processlist |
TRUNCATE 需要 DROP 權限 |
為了一個清空動作,賦予了砍表的權限 | 先確認那個 TRUNCATE 真的還在用;能改成 DELETE 就改 |
REPLACE 需要 INSERT+DELETE |
只給 INSERT 會在執行期才失敗 | 草稿已自動處理,但手改時容易漏 |
| 靜態掃描漏動態表名 | 切換後某個少用功能突然權限不足 | 切換後保留 david 權限不回收,出事先改回 .env 再補授權 |
| 明碼密碼留在 PHP | 換了帳號但密碼還是進了 git | 五站(salary/report/sales/sony/kng.tw)要同步改讀 env |
html/salary 不一次拆。它寫 87 張表,是「總務室」問題本體,
應隨階段 5 對內站歸併逐模組搬遷,硬拆只會製造一個同樣肥大的新帳號。