Workspace Google (/api/v1/admin/workspace/*)
ชุด 11 route ที่ให้ผู้ใช้ "ล็อกอินด้วยอีเมล Google" แล้วเลือก/สร้างบริษัท (ฐานข้อมูล) ของตนเอง, เชิญสมาชิกด้วยอีเมล, ขอดูสิทธิ์หน้าจอ และผูกบัญชี LINE. อยู่ใต้ AdminAuth (กุญแจ X-Admin-Api-Key) เหมือน Admin API ส่วนอื่น (Admin API สำหรับจัดการ Provider/ผู้ใช้/ฐานข้อมูล/สิทธิ์เมนู (Admin API)).
ทำงานอย่างไร
- ตารางที่ใช้ (สร้างด้วย
ensureWorkspaceTablesทุกครั้งที่ handler เรียก,workspace_google.go:36-76; ผลถูกกลืน):bc_google_account(PKemail,google_sub,display_name,picture_url,line_user_id,line_display_name,line_linked_at),bc_google_workspace_db(PKdatabase_name,data_code,company_name,owner_email,provider_code),bc_google_workspace_member(PKdatabase_name+email,role,mapped_user_code,invited_by_email) และเติมคอลัมน์email,line_user_idให้bc_user_list. อยู่ใน Main DB ของ Provider (โมเดลผู้เช่าหลายราย: Tenant, Provider, Main DB, Company (Multi-tenant model)); Provider ว่าง =bcdemo(ค่า default ในทุก handler). POST /workspace/google-login(:263): ต้องมีemailที่มี@; upsertbc_google_account(เก็บgoogle_sub/ชื่อ/รูปเฉพาะค่าที่ไม่ว่าง) แล้วคืนรายการบริษัทที่เข้าถึงได้.queryAccessibleWorkspaceDatabases(:91) รวมบริษัทจาก 3 แหล่ง แล้ว merge ตามชื่อฐาน (role ADMIN ชนะ), ข้ามชื่อขึ้นต้นbcerpmain:- (1)
bc_google_workspace_memberตามอีเมล — roleMEMBERต้องผูกbc_user_listที่ active (ตามmapped_user_codeหรือหาจากemail) ไม่งั้นถูกข้าม. - (2)
bc_user_list.emailตรงอีเมล + มีสิทธิ์เข้าบริษัทในbc_database_list_user_and_group(user_or_group_status=0); role = ADMIN ถ้าuser_level >= 9. - (3) ถ้าส่ง
user_code: บริษัทที่ผู้ใช้ได้สิทธิ์ตรง; role ADMIN ถ้า level ≥ 9 หรือรหัสSUPERADMIN/admin; บริษัทที่ Google-owner เป็นคนอื่นจะไม่รั่วให้ เว้นแต่ผู้ใช้ถูกเชิญ/ผูกอยู่ในตารางสมาชิก.
- (1)
GET /workspace/databasesใช้ฟังก์ชันเดียวกัน (queryemail,user_code);GET /workspace/check-db-name?name=เรียกcheckDatabaseNameAvailable(:343).- กติกาชื่อฐานข้อมูล (
:19,345-373):^[a-z][a-z0-9_]{2,40}$(ตัวพิมพ์เล็ก) , ห้ามอยู่ในreservedDBs(:20-33เช่นpostgres,template0/1,bcdemo,default,admin,system,public), ห้ามขึ้นต้นbcerpmainหรือpg_, ห้ามซ้ำกับbc_database_list/bc_google_workspace_db(ชื่อหรือdata_code) และห้ามมีอยู่แล้วในpg_databaseของเซิร์ฟเวอร์ → ไม่ผ่าน = 409DB_NAME_TAKEN. POST /workspace/databases(:533) สร้างบริษัทใหม่ 3 ขั้น:- ต้องมี
owner_email(หรือemail; ถ้าไม่มีแต่มีuser_codeจะใช้user_codeพิมพ์เล็กแทน) ไม่งั้น 400MISSING_OWNER; ชื่อบริษัทว่าง =กิจการ <ชื่อฐานพิมพ์ใหญ่>;data_codeว่าง = ชื่อฐานพิมพ์ใหญ่. CREATE DATABASE ... ENCODING 'UTF8'บนฐานpostgresของ Provider (:597) แล้วcloneEmptySchemaFromTemplateภายใต้ timeout 45 วินาที (:604).- ลงทะเบียนใน Main DB: แถว
bc_database_list(groupMAIN), สิทธิ์เข้าบริษัท, แถวbc_google_workspace_dbและสมาชิก ADMIN. ผลของการลงทะเบียนถูกกลืน (_, _ =).
- ต้องมี
cloneEmptySchemaFromTemplate(:395-531): อ่านโครงตารางทั้งหมดใน schemapublicของฐานแม่แบบbcdemoจาก catalog (pg_attribute,pg_constraint) แล้วCREATE TABLE IF NOT EXISTSทีละตารางในฐานใหม่ (ถ้าพังใช้ DDL แบบพื้นฐานแทน), คัดลอก PRIMARY KEY/UNIQUE, แล้ว seed เฉพาะerp_option(ทศนิยม 2 ตำแหน่ง),erp_company_profile(ชื่อบริษัท สาขา 00000 "สำนักงานใหญ่") และผู้ใช้erp_userเริ่มต้น — ไม่คัดลอกข้อมูลและไม่คัดลอก index/FK/view/function (คัดเฉพาะ PK/UNIQUE; ส่วนอื่นยังไม่ยืนยันว่ามีที่อื่นสร้างให้หรือไม่).POST /workspace/select-db(:674): รับชื่อบริษัท (database_name|database_code|database) แล้วหาchosenจากรายการที่เข้าถึงได้ ไม่เจอ → 403; จากนั้นเรียกtokens.IssueSession(:808) ออก access/refresh token ของบริษัทนั้นพร้อมlevel(JWT session token และการเพิกถอน (JWT + revocation store)).- สมาชิก:
GET /workspace/members?database=คืนสมาชิก + สถานะ LINE + รายชื่อbc_usersที่ active (ให้แอดมินเลือกผูก);POST /workspace/members(:972) และDELETE /workspace/members(:1096) ต้องผ่านisDatabaseAdmin(:839) — ตรวจจาก role ในตารางสมาชิก /owner_email/user_level >= 9/ รหัสSUPERADMIN|admin. Upsert สร้างmapped_user_code, สร้างbc_user_listถ้ายังไม่มี, เพิ่มสิทธิ์เข้าบริษัท และซิงก์อีเมลไปerp_userในฐานบริษัท. GET /workspace/my-screen-rights(:1195): ถ้าเป็น Database Admin (isDatabaseAdmin) →{"is_admin":true,"menus":[]}; ไม่งั้นหาmapped_user_codeจากอีเมลแล้วรวมimage_fileของ user + กลุ่มโดย รวมรายรหัสเมนู (OR ต่อเมนู) ด้วยloadMergedMenuRightsForUser(:1129) →{"is_admin":false,"menus":[...]}(สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission)).- LINE:
GET /workspace/line-binding(:1256) แปลงอีเมล ↔line_user_id(จากbc_google_accountก่อน แล้วbc_user_list) พร้อมรายชื่อพนักงานและสถานะผูก LINE ของบริษัท;POST /workspace/line-binding(:1368) ผูก/ยกเลิก (unbind) — ผูกได้เมื่อเป็นบัญชีของตัวเองหรือผู้เรียกเป็น Database Admin; การผูกใหม่จะล้างline_user_idเดิมที่ซ้ำกับบัญชีอื่น แล้วเขียนทั้งbc_google_account,bc_user_listและerp_user(เพิ่มคอลัมน์line_user_id) ในฐานบริษัท.
ข้อควรระวัง
- มีประเด็นด้านความปลอดภัยที่ทีมพัฒนาต้องตรวจ (ไม่เผยแพร่รายละเอียดบนเว็บสาธารณะ)
- การสร้างบริษัทไม่ atomic: ถ้า
CREATE DATABASEสำเร็จแต่ clone ล้ม ฐานจริงค้างอยู่โดยไม่มีแถวลงทะเบียน (ไม่พบขั้นตอน rollback/DROP ในโค้ด). - ฐานแม่แบบถูกกำหนดตายตัวเป็น
bcdemoของ Provider ที่เรียก (:396) — ถ้า Provider นั้นไม่มีฐานนี้ ฟังก์ชันคืน error (:398) และการสร้างบริษัทล้ม (ยังไม่ยืนยันพฤติกรรมตอนเปิด pool ฐานที่ไม่มีอยู่จริง). - ทุก handler รัน
CREATE TABLE/ALTER TABLE IF NOT EXISTSซ้ำทุกคำขอ และกลืน error — ถ้าผู้ใช้ DB ไม่มีสิทธิ์ DDL ตารางจะไม่ถูกสร้างและ query ถัดไปล้มเงียบ ๆ (ตัวจัดการ connection pool ต่อฐานข้อมูล (PoolManager)). ensureWorkspaceTablesยังถูกเรียกจากsyncUserAllowedDatabasesของ Admin API (Admin API สำหรับจัดการ Provider/ผู้ใช้/ฐานข้อมูล/สิทธิ์เมนู (Admin API)) จึงเป็นตาราง "ของระบบ" ไม่ใช่เฉพาะ route กลุ่มนี้.
ที่มาในโค้ด
backend/internal/adapter/inbound/http/admin/workspace_google.go:18-76— regex ชื่อฐาน,reservedDBs, ตารางที่สร้าง.backend/internal/adapter/inbound/http/admin/workspace_google.go:91-261—queryAccessibleWorkspaceDatabases(3 แหล่ง).backend/internal/adapter/inbound/http/admin/workspace_google.go:263-393— google-login, list, ตรวจชื่อ.backend/internal/adapter/inbound/http/admin/workspace_google.go:395-672— clone แม่แบบ + สร้างบริษัท.backend/internal/adapter/inbound/http/admin/workspace_google.go:674-837—select-db+ ออก token.backend/internal/adapter/inbound/http/admin/workspace_google.go:839-1128—isDatabaseAdmin, สมาชิก.backend/internal/adapter/inbound/http/admin/workspace_google.go:1129-1481— สิทธิ์หน้าจอ, LINE binding.backend/internal/adapter/inbound/http/router.go:237-247— route ทั้ง 11 ตัว.
ดูเพิ่ม
ภาพรวม Backend Go ของ BC Ai Account (Backend overview) · Admin API สำหรับจัดการ Provider/ผู้ใช้/ฐานข้อมูล/สิทธิ์เมนู (Admin API) · โมเดลผู้เช่าหลายราย: Tenant, Provider, Main DB, Company (Multi-tenant model) · JWT session token และการเพิกถอน (JWT + revocation store) · สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission) · ขั้นตอน login / เลือกบริษัท / refresh / logout (Identity login flow) · ADR-0003 ข้อมูลผู้ใช้และสิทธิ์อยู่ใน DB ของแต่ละผู้เช่า ไม่ใช่ control-plane · สารบัญ ADR ของ backend (Architecture Decision Records)