chore: sync workspace state
This commit is contained in:
parent
ed2052c8ec
commit
3a6a571361
38 changed files with 3978 additions and 620 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -76,3 +76,4 @@ errors_utf8.txt
|
|||
|
||||
vault-unseal-keysdefault-vault.tar
|
||||
prompts/sec-bots.md
|
||||
AGENTS-PROD.md
|
||||
|
|
|
|||
37
.playwright-mcp/cristo-snapshot.md
Normal file
37
.playwright-mcp/cristo-snapshot.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
- generic [ref=e5]:
|
||||
- generic [ref=e6]:
|
||||
- generic [ref=e7]: Chat
|
||||
- generic [ref=e8]:
|
||||
- button [ref=e9] [cursor=pointer]:
|
||||
- img [ref=e10]
|
||||
- button [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- button [ref=e14] [cursor=pointer]:
|
||||
- img [ref=e15]
|
||||
- generic [ref=e18]:
|
||||
- generic [ref=e19]:
|
||||
- main [ref=e20]:
|
||||
- paragraph [ref=e31]: Olá! Sou o assistente virtual do Santuário Cristo Redentor. Como posso ajudá-lo hoje com informações sobre celebrações, eventos, visitação ou orações?
|
||||
- contentinfo [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- button "Agendar Batizado" [ref=e32] [cursor=pointer]
|
||||
- button "Agendar Casamento" [ref=e33] [cursor=pointer]
|
||||
- button "Agendar Missa" [ref=e34] [cursor=pointer]
|
||||
- button "Agendar Peregrinação" [ref=e35] [cursor=pointer]
|
||||
- button "Pedido de Oração" [ref=e36] [cursor=pointer]
|
||||
- button "Uso de Imagem" [ref=e37] [cursor=pointer]
|
||||
- button "Licenciamento" [ref=e38] [cursor=pointer]
|
||||
- button "Evento/Iluminação" [ref=e39] [cursor=pointer]
|
||||
- button "Cadastrar Guia" [ref=e40] [cursor=pointer]
|
||||
- button "Fazer Doação" [ref=e41] [cursor=pointer]
|
||||
- generic [ref=e23]:
|
||||
- generic [ref=e24]:
|
||||
- button "Agent" [ref=e25] [cursor=pointer]
|
||||
- button "Chat" [ref=e26] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e27]
|
||||
- button "↑" [ref=e28] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
45
.playwright-mcp/page-2026-03-31T19-16-51-748Z.yml
Normal file
45
.playwright-mcp/page-2026-03-31T19-16-51-748Z.yml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- img "General Bots" [ref=e6]
|
||||
- heading "Welcome Back" [level=1] [ref=e7]
|
||||
- paragraph [ref=e8]: Sign in to your General Bots account
|
||||
- generic [ref=e10]:
|
||||
- generic [ref=e11]:
|
||||
- generic [ref=e12]:
|
||||
- generic [ref=e13]: Email Address
|
||||
- generic [ref=e14]:
|
||||
- img
|
||||
- textbox "Email Address" [ref=e15]:
|
||||
- /placeholder: you@example.com
|
||||
- generic [ref=e16]:
|
||||
- generic [ref=e17]: Password
|
||||
- generic [ref=e18]:
|
||||
- img
|
||||
- textbox "Password" [ref=e19]:
|
||||
- /placeholder: ••••••••
|
||||
- button [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e24]:
|
||||
- generic [ref=e27] [cursor=pointer]: Remember me
|
||||
- link "Forgot password?" [ref=e28] [cursor=pointer]:
|
||||
- /url: /auth/forgot-password
|
||||
- button "Sign In" [ref=e29] [cursor=pointer]:
|
||||
- generic [ref=e30]: Sign In
|
||||
- generic [ref=e32]: or continue with
|
||||
- generic [ref=e33]:
|
||||
- button "Google" [ref=e34] [cursor=pointer]:
|
||||
- img [ref=e35]
|
||||
- text: Google
|
||||
- button "Microsoft" [ref=e40] [cursor=pointer]:
|
||||
- img [ref=e41]
|
||||
- text: Microsoft
|
||||
- button "GitHub" [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- text: GitHub
|
||||
- button "Apple" [ref=e49] [cursor=pointer]:
|
||||
- img [ref=e50]
|
||||
- text: Apple
|
||||
- paragraph [ref=e53]:
|
||||
- text: Don't have an account?
|
||||
- link "Create account" [ref=e54] [cursor=pointer]:
|
||||
- /url: /auth/register
|
||||
25
.playwright-mcp/page-2026-03-31T19-17-49-094Z.yml
Normal file
25
.playwright-mcp/page-2026-03-31T19-17-49-094Z.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
- generic [ref=e5]:
|
||||
- generic [ref=e6]:
|
||||
- generic [ref=e7]: Chat
|
||||
- generic [ref=e8]:
|
||||
- button [ref=e9] [cursor=pointer]:
|
||||
- img [ref=e10]
|
||||
- button [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- button [ref=e14] [cursor=pointer]:
|
||||
- img [ref=e15]
|
||||
- generic [ref=e18]:
|
||||
- generic [ref=e19]:
|
||||
- main [ref=e20]
|
||||
- contentinfo [ref=e21]:
|
||||
- generic [ref=e23]:
|
||||
- generic [ref=e24]:
|
||||
- button "Agent" [ref=e25] [cursor=pointer]
|
||||
- button "Chat" [ref=e26] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e27]
|
||||
- button "↑" [ref=e28] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
111
.playwright-mcp/page-2026-03-31T19-18-09-643Z.yml
Normal file
111
.playwright-mcp/page-2026-03-31T19-18-09-643Z.yml
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic "Home" [ref=e4] [cursor=pointer]:
|
||||
- img [ref=e5]
|
||||
- generic "Search" [ref=e8] [cursor=pointer]:
|
||||
- img [ref=e9]
|
||||
- generic "Terminal" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic "User" [ref=e15] [cursor=pointer]:
|
||||
- img [ref=e16]
|
||||
- generic "Apps" [ref=e19] [cursor=pointer]:
|
||||
- img [ref=e20]
|
||||
- generic "Settings" [ref=e25] [cursor=pointer]:
|
||||
- img [ref=e26]
|
||||
- generic [ref=e29]:
|
||||
- generic [ref=e30]:
|
||||
- generic [ref=e31]:
|
||||
- generic [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e34]
|
||||
- generic [ref=e36]: Vibe
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e39]
|
||||
- generic [ref=e44]: CRM
|
||||
- generic [ref=e45] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- generic [ref=e49]: Campaigns
|
||||
- generic [ref=e50] [cursor=pointer]:
|
||||
- img [ref=e52]
|
||||
- generic [ref=e53]: Lists
|
||||
- generic [ref=e54] [cursor=pointer]:
|
||||
- img [ref=e56]
|
||||
- generic [ref=e58]: Templates
|
||||
- generic [ref=e59] [cursor=pointer]:
|
||||
- img [ref=e61]
|
||||
- generic [ref=e64]: Tasks
|
||||
- generic [ref=e65] [cursor=pointer]:
|
||||
- img [ref=e67]
|
||||
- generic [ref=e69]: Chat
|
||||
- generic [ref=e70] [cursor=pointer]:
|
||||
- img [ref=e72]
|
||||
- generic [ref=e74]: Terminal
|
||||
- generic [ref=e75] [cursor=pointer]:
|
||||
- img [ref=e77]
|
||||
- generic [ref=e79]: Explorer
|
||||
- generic [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e82]
|
||||
- generic [ref=e85]: Editor
|
||||
- generic [ref=e86] [cursor=pointer]:
|
||||
- img [ref=e88]
|
||||
- generic [ref=e93]: Designer
|
||||
- generic [ref=e94] [cursor=pointer]:
|
||||
- img [ref=e96]
|
||||
- generic [ref=e99]: BASIC
|
||||
- generic [ref=e100] [cursor=pointer]:
|
||||
- img [ref=e102]
|
||||
- generic [ref=e105]: Browser
|
||||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- generic:
|
||||
- generic: Connecting...
|
||||
- main [ref=e121]
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
- contentinfo [ref=e130]:
|
||||
- img [ref=e134] [cursor=pointer]
|
||||
- generic [ref=e136]:
|
||||
- combobox [ref=e138] [cursor=pointer]:
|
||||
- option "🎨 Default"
|
||||
- option "☀️ Light" [selected]
|
||||
- option "🍊 Orange"
|
||||
- option "🌃 Cyberpunk"
|
||||
- option "🌴 Retrowave"
|
||||
- option "💭 Vapor Dream"
|
||||
- option "✨ Y2K"
|
||||
- option "🔲 3D Bevel"
|
||||
- option "🕹️ Arcade"
|
||||
- option "🪩 Disco"
|
||||
- option "🎸 Grunge"
|
||||
- option "🎺 Jazz"
|
||||
- option "🌻 Mellow"
|
||||
- option "🏠 Mid Century"
|
||||
- option "📷 Polaroid"
|
||||
- option "📺 Cartoons"
|
||||
- option "🏖️ Seaside"
|
||||
- option "⌨️ Typewriter"
|
||||
- option "📠 Xerox"
|
||||
- option "📁 XTree"
|
||||
- button "Sign In" [ref=e139] [cursor=pointer]
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]: 00:00
|
||||
- generic [ref=e142]: 01/01/2026
|
||||
39
.playwright-mcp/page-2026-03-31T19-18-17-256Z.yml
Normal file
39
.playwright-mcp/page-2026-03-31T19-18-17-256Z.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- main [ref=e121]:
|
||||
- paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje?
|
||||
- generic [ref=e158]: 🔍 Detectar Desvios na Folha
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e123]:
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e146] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e147] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e148] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e149] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e150] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e151] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e152] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e153] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e154] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [active] [ref=e155] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e156] [cursor=pointer]
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
109
.playwright-mcp/page-2026-03-31T19-18-59-132Z.yml
Normal file
109
.playwright-mcp/page-2026-03-31T19-18-59-132Z.yml
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic "Home" [ref=e4] [cursor=pointer]:
|
||||
- img [ref=e5]
|
||||
- generic "Search" [ref=e8] [cursor=pointer]:
|
||||
- img [ref=e9]
|
||||
- generic "Terminal" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic "User" [ref=e15] [cursor=pointer]:
|
||||
- img [ref=e16]
|
||||
- generic "Apps" [ref=e19] [cursor=pointer]:
|
||||
- img [ref=e20]
|
||||
- generic "Settings" [ref=e25] [cursor=pointer]:
|
||||
- img [ref=e26]
|
||||
- generic [ref=e29]:
|
||||
- generic [ref=e30]:
|
||||
- generic [ref=e31]:
|
||||
- generic [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e34]
|
||||
- generic [ref=e36]: Vibe
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e39]
|
||||
- generic [ref=e44]: CRM
|
||||
- generic [ref=e45] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- generic [ref=e49]: Campaigns
|
||||
- generic [ref=e50] [cursor=pointer]:
|
||||
- img [ref=e52]
|
||||
- generic [ref=e53]: Lists
|
||||
- generic [ref=e54] [cursor=pointer]:
|
||||
- img [ref=e56]
|
||||
- generic [ref=e58]: Templates
|
||||
- generic [ref=e59] [cursor=pointer]:
|
||||
- img [ref=e61]
|
||||
- generic [ref=e64]: Tasks
|
||||
- generic [ref=e65] [cursor=pointer]:
|
||||
- img [ref=e67]
|
||||
- generic [ref=e69]: Chat
|
||||
- generic [ref=e70] [cursor=pointer]:
|
||||
- img [ref=e72]
|
||||
- generic [ref=e74]: Terminal
|
||||
- generic [ref=e75] [cursor=pointer]:
|
||||
- img [ref=e77]
|
||||
- generic [ref=e79]: Explorer
|
||||
- generic [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e82]
|
||||
- generic [ref=e85]: Editor
|
||||
- generic [ref=e86] [cursor=pointer]:
|
||||
- img [ref=e88]
|
||||
- generic [ref=e93]: Designer
|
||||
- generic [ref=e94] [cursor=pointer]:
|
||||
- img [ref=e96]
|
||||
- generic [ref=e99]: BASIC
|
||||
- generic [ref=e100] [cursor=pointer]:
|
||||
- img [ref=e102]
|
||||
- generic [ref=e105]: Browser
|
||||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- main [ref=e121]
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
- contentinfo [ref=e130]:
|
||||
- img [ref=e134] [cursor=pointer]
|
||||
- generic [ref=e136]:
|
||||
- combobox [ref=e138] [cursor=pointer]:
|
||||
- option "🎨 Default"
|
||||
- option "☀️ Light" [selected]
|
||||
- option "🍊 Orange"
|
||||
- option "🌃 Cyberpunk"
|
||||
- option "🌴 Retrowave"
|
||||
- option "💭 Vapor Dream"
|
||||
- option "✨ Y2K"
|
||||
- option "🔲 3D Bevel"
|
||||
- option "🕹️ Arcade"
|
||||
- option "🪩 Disco"
|
||||
- option "🎸 Grunge"
|
||||
- option "🎺 Jazz"
|
||||
- option "🌻 Mellow"
|
||||
- option "🏠 Mid Century"
|
||||
- option "📷 Polaroid"
|
||||
- option "📺 Cartoons"
|
||||
- option "🏖️ Seaside"
|
||||
- option "⌨️ Typewriter"
|
||||
- option "📠 Xerox"
|
||||
- option "📁 XTree"
|
||||
- button "Sign In" [ref=e139] [cursor=pointer]
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]: 00:00
|
||||
- generic [ref=e142]: 01/01/2026
|
||||
50
.playwright-mcp/page-2026-03-31T19-19-05-563Z.yml
Normal file
50
.playwright-mcp/page-2026-03-31T19-19-05-563Z.yml
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- main [ref=e121]:
|
||||
- paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje?
|
||||
- generic [ref=e169]: 🔍 Detectar Desvios na Folha
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e123]:
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e146] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e147] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e148] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e149] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e150] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e151] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e152] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e153] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e154] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [active] [ref=e155] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e156] [cursor=pointer]
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e157] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e158] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e159] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e160] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e161] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e162] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e163] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e164] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e165] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e166] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e167] [cursor=pointer]
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
123
.playwright-mcp/page-2026-03-31T19-19-18-155Z.yml
Normal file
123
.playwright-mcp/page-2026-03-31T19-19-18-155Z.yml
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- main [ref=e121]:
|
||||
- paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje?
|
||||
- generic [ref=e169]: 🔍 Detectar Desvios na Folha
|
||||
- generic [ref=e171]:
|
||||
- paragraph [ref=e172]:
|
||||
- text: 🕵️♀️
|
||||
- strong [ref=e173]: Passo a passo para detectar desvios na sua folha de pagamento
|
||||
- list [ref=e174]:
|
||||
- listitem [ref=e175]:
|
||||
- paragraph [ref=e176]:
|
||||
- strong [ref=e177]: Acesse o Contracheque Online
|
||||
- list [ref=e178]:
|
||||
- listitem [ref=e179]: Entre no portal SIPES/SEAD com seu login.
|
||||
- listitem [ref=e180]: Visualize o contracheque do mês corrente e dos últimos 6 meses.
|
||||
- listitem [ref=e181]:
|
||||
- paragraph [ref=e182]:
|
||||
- strong [ref=e183]: Compare os valores
|
||||
- list [ref=e184]:
|
||||
- listitem [ref=e185]:
|
||||
- text: 📈
|
||||
- strong [ref=e186]: Remuneração Base
|
||||
- text: ": deve permanecer constante (exceto progressões ou promoções)."
|
||||
- listitem [ref=e187]:
|
||||
- text: ➕
|
||||
- strong [ref=e188]: Verbas Variáveis
|
||||
- text: ": identifique adicionais, horas extras ou gratificações que não reconhece."
|
||||
- listitem [ref=e189]:
|
||||
- text: ➖
|
||||
- strong [ref=e190]: Descontos
|
||||
- text: ": cheque descontos de INSS, IRRF, pensão alimentícia e demais deduções."
|
||||
- listitem [ref=e191]:
|
||||
- paragraph [ref=e192]:
|
||||
- strong [ref=e193]: Solicite documentos complementares (se necessário)
|
||||
- list [ref=e194]:
|
||||
- listitem [ref=e195]:
|
||||
- strong [ref=e196]: Histórico de contracheques
|
||||
- text: – tipo
|
||||
- code [ref=e197]: CONTRACHEQUE_HISTORICO
|
||||
- text: .
|
||||
- listitem [ref=e198]:
|
||||
- strong [ref=e199]: Informe de rendimentos
|
||||
- text: ou
|
||||
- strong [ref=e200]: Ficha financeira
|
||||
- text: – para cruzar com a Receita Federal.
|
||||
- listitem [ref=e201]:
|
||||
- paragraph [ref=e202]:
|
||||
- strong [ref=e203]: Cruzamento de informações
|
||||
- list [ref=e204]:
|
||||
- listitem [ref=e205]:
|
||||
- text: Use o
|
||||
- strong [ref=e206]: Informe de Rendimentos
|
||||
- text: para verificar se os valores declarados coincidem com o que consta na folha.
|
||||
- listitem [ref=e207]:
|
||||
- text: Compare a
|
||||
- strong [ref=e208]: Ficha Financeira
|
||||
- text: com o extrato bancário (se houver depósito direto).
|
||||
- listitem [ref=e209]:
|
||||
- paragraph [ref=e210]:
|
||||
- strong [ref=e211]: Registre um protocolo na SEAD/SEPLAG‑SE
|
||||
- list [ref=e212]:
|
||||
- listitem [ref=e213]:
|
||||
- text: "Caso encontre alguma divergência, abra um protocolo (ex.:"
|
||||
- code [ref=e214]: CCH123456
|
||||
- text: ).
|
||||
- listitem [ref=e215]: Descreva claramente a diferença encontrada e anexe prints ou documentos.
|
||||
- listitem [ref=e216]:
|
||||
- paragraph [ref=e217]:
|
||||
- strong [ref=e218]: Acompanhamento
|
||||
- list [ref=e219]:
|
||||
- listitem [ref=e220]: Acompanhe o status do protocolo pelo portal ou pelo telefone da Ouvidoria.
|
||||
- listitem [ref=e221]: Se a resposta for insatisfatória, solicite revisão ou escalonamento.
|
||||
- separator [ref=e222]
|
||||
- paragraph [ref=e223]:
|
||||
- text: ⚙️
|
||||
- strong [ref=e224]: Posso ajudar a solicitar algum desses documentos
|
||||
- text: (histórico de contracheques, informe de rendimentos, ficha financeira) ou a abrir um protocolo de auditoria. Basta informar os dados solicitados (CPF, matrícula, e‑mail, etc.).
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e123]:
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e225] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e226] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e227] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e228] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e229] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e230] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e231] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e232] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e233] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e234] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e235] [cursor=pointer]
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e236] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e237] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e238] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e239] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e240] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e241] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e242] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e243] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e244] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e245] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e246] [cursor=pointer]
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
111
.playwright-mcp/page-2026-03-31T19-21-55-814Z.yml
Normal file
111
.playwright-mcp/page-2026-03-31T19-21-55-814Z.yml
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic "Home" [ref=e4] [cursor=pointer]:
|
||||
- img [ref=e5]
|
||||
- generic "Search" [ref=e8] [cursor=pointer]:
|
||||
- img [ref=e9]
|
||||
- generic "Terminal" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic "User" [ref=e15] [cursor=pointer]:
|
||||
- img [ref=e16]
|
||||
- generic "Apps" [ref=e19] [cursor=pointer]:
|
||||
- img [ref=e20]
|
||||
- generic "Settings" [ref=e25] [cursor=pointer]:
|
||||
- img [ref=e26]
|
||||
- generic [ref=e29]:
|
||||
- generic [ref=e30]:
|
||||
- generic [ref=e31]:
|
||||
- generic [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e34]
|
||||
- generic [ref=e36]: Vibe
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e39]
|
||||
- generic [ref=e44]: CRM
|
||||
- generic [ref=e45] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- generic [ref=e49]: Campaigns
|
||||
- generic [ref=e50] [cursor=pointer]:
|
||||
- img [ref=e52]
|
||||
- generic [ref=e53]: Lists
|
||||
- generic [ref=e54] [cursor=pointer]:
|
||||
- img [ref=e56]
|
||||
- generic [ref=e58]: Templates
|
||||
- generic [ref=e59] [cursor=pointer]:
|
||||
- img [ref=e61]
|
||||
- generic [ref=e64]: Tasks
|
||||
- generic [ref=e65] [cursor=pointer]:
|
||||
- img [ref=e67]
|
||||
- generic [ref=e69]: Chat
|
||||
- generic [ref=e70] [cursor=pointer]:
|
||||
- img [ref=e72]
|
||||
- generic [ref=e74]: Terminal
|
||||
- generic [ref=e75] [cursor=pointer]:
|
||||
- img [ref=e77]
|
||||
- generic [ref=e79]: Explorer
|
||||
- generic [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e82]
|
||||
- generic [ref=e85]: Editor
|
||||
- generic [ref=e86] [cursor=pointer]:
|
||||
- img [ref=e88]
|
||||
- generic [ref=e93]: Designer
|
||||
- generic [ref=e94] [cursor=pointer]:
|
||||
- img [ref=e96]
|
||||
- generic [ref=e99]: BASIC
|
||||
- generic [ref=e100] [cursor=pointer]:
|
||||
- img [ref=e102]
|
||||
- generic [ref=e105]: Browser
|
||||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- generic:
|
||||
- generic: Connecting...
|
||||
- main [ref=e121]
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
- contentinfo [ref=e130]:
|
||||
- img [ref=e134] [cursor=pointer]
|
||||
- generic [ref=e136]:
|
||||
- combobox [ref=e138] [cursor=pointer]:
|
||||
- option "🎨 Default"
|
||||
- option "☀️ Light" [selected]
|
||||
- option "🍊 Orange"
|
||||
- option "🌃 Cyberpunk"
|
||||
- option "🌴 Retrowave"
|
||||
- option "💭 Vapor Dream"
|
||||
- option "✨ Y2K"
|
||||
- option "🔲 3D Bevel"
|
||||
- option "🕹️ Arcade"
|
||||
- option "🪩 Disco"
|
||||
- option "🎸 Grunge"
|
||||
- option "🎺 Jazz"
|
||||
- option "🌻 Mellow"
|
||||
- option "🏠 Mid Century"
|
||||
- option "📷 Polaroid"
|
||||
- option "📺 Cartoons"
|
||||
- option "🏖️ Seaside"
|
||||
- option "⌨️ Typewriter"
|
||||
- option "📠 Xerox"
|
||||
- option "📁 XTree"
|
||||
- button "Sign In" [ref=e139] [cursor=pointer]
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]: 00:00
|
||||
- generic [ref=e142]: 01/01/2026
|
||||
61
.playwright-mcp/page-2026-03-31T19-22-10-960Z.yml
Normal file
61
.playwright-mcp/page-2026-03-31T19-22-10-960Z.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- main [ref=e121]:
|
||||
- paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje?
|
||||
- generic [ref=e180]: 🔍 Detectar Desvios na Folha
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e123]:
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e146] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e147] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e148] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e149] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e150] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e151] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e152] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e153] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e154] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [active] [ref=e155] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e156] [cursor=pointer]
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e157] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e158] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e159] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e160] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e161] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e162] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e163] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e164] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e165] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e166] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e167] [cursor=pointer]
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e168] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e169] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e170] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e171] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e172] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e173] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e174] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e175] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e176] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e177] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e178] [cursor=pointer]
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
82
.playwright-mcp/page-2026-03-31T19-22-29-106Z.yml
Normal file
82
.playwright-mcp/page-2026-03-31T19-22-29-106Z.yml
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- main [ref=e121]:
|
||||
- paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje?
|
||||
- generic [ref=e180]: 🔍 Detectar Desvios na Folha
|
||||
- generic [ref=e182]:
|
||||
- paragraph [ref=e183]:
|
||||
- text: 🔎
|
||||
- strong [ref=e184]: Vamos analisar a sua folha de pagamento para identificar possíveis desvios.
|
||||
- text: Para isso preciso que você me informe os dados cadastrais do servidor (mesmo que já os tenha usado antes). Assim consigo solicitar o histórico de contracheques e compará‑los.
|
||||
- blockquote [ref=e185]:
|
||||
- paragraph [ref=e186]:
|
||||
- strong [ref=e187]: Dados necessários
|
||||
- list [ref=e188]:
|
||||
- listitem [ref=e189]: CPF (com ou sem formatação)
|
||||
- listitem [ref=e190]: Matrícula funcional
|
||||
- listitem [ref=e191]: Nome completo do servidor
|
||||
- listitem [ref=e192]: "Órgão de lotação (ex.: SEAD, SEED, SES, SSP)"
|
||||
- listitem [ref=e193]: E‑mail (para envio do documento)
|
||||
- listitem [ref=e194]: Telefone com DDD
|
||||
- paragraph [ref=e195]:
|
||||
- text: ⚠️
|
||||
- strong [ref=e196]: Confirme
|
||||
- text: que está de acordo em fornecer essas informações e eu solicito o documento
|
||||
- code [ref=e197]: CONTRACHEQUE_HISTORICO
|
||||
- text: . Depois analisaremos juntos as variações encontradas.
|
||||
- contentinfo [ref=e122]:
|
||||
- generic [ref=e123]:
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e198] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e199] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e200] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e201] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e202] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e203] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e204] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e205] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e206] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e207] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e208] [cursor=pointer]
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e209] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e210] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e211] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e212] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e213] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e214] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e215] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e216] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e217] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e218] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e219] [cursor=pointer]
|
||||
- button "📄 Segunda Via de Contracheque" [ref=e220] [cursor=pointer]
|
||||
- button "📅 Agendar Atendimento no CEAC" [ref=e221] [cursor=pointer]
|
||||
- button "🩺 Agendar Perícia Médica" [ref=e222] [cursor=pointer]
|
||||
- button "📋 Solicitar Documentos Funcionais" [ref=e223] [cursor=pointer]
|
||||
- button "✏️ Atualizar Dados Cadastrais" [ref=e224] [cursor=pointer]
|
||||
- button "🎓 Cursos da Escola de Governo" [ref=e225] [cursor=pointer]
|
||||
- button "💰 Informações sobre Benefícios" [ref=e226] [cursor=pointer]
|
||||
- button "🔍 Consultar Protocolo" [ref=e227] [cursor=pointer]
|
||||
- button "📞 Falar com a SEAD" [ref=e228] [cursor=pointer]
|
||||
- button "🔍 Detectar Desvios na Folha" [ref=e229] [cursor=pointer]
|
||||
- button "❓ Outras Solicitações" [ref=e230] [cursor=pointer]
|
||||
- generic [ref=e124]:
|
||||
- generic [ref=e125]:
|
||||
- button "Agent" [ref=e126] [cursor=pointer]
|
||||
- button "Chat" [ref=e127] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [ref=e128]
|
||||
- button "↑" [ref=e129] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
27
.playwright-mcp/page-2026-04-02T11-04-19-343Z.yml
Normal file
27
.playwright-mcp/page-2026-04-02T11-04-19-343Z.yml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
- generic [ref=e5]:
|
||||
- generic [ref=e6]:
|
||||
- generic [ref=e7]: Chat
|
||||
- generic [ref=e8]:
|
||||
- button [ref=e9] [cursor=pointer]:
|
||||
- img [ref=e10]
|
||||
- button [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- button [ref=e14] [cursor=pointer]:
|
||||
- img [ref=e15]
|
||||
- generic [ref=e18]:
|
||||
- generic [ref=e19]:
|
||||
- generic:
|
||||
- generic: Connecting...
|
||||
- main [ref=e20]
|
||||
- contentinfo [ref=e21]:
|
||||
- generic [ref=e23]:
|
||||
- generic [ref=e24]:
|
||||
- button "Agent" [ref=e25] [cursor=pointer]
|
||||
- button "Chat" [ref=e26] [cursor=pointer]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e27]
|
||||
- button "↑" [ref=e28] [cursor=pointer]
|
||||
- button "Scroll to bottom":
|
||||
- img
|
||||
- generic:
|
||||
- generic:
|
||||
- button "View"
|
||||
147
.playwright-mcp/page-2026-04-02T11-05-27-254Z.yml
Normal file
147
.playwright-mcp/page-2026-04-02T11-05-27-254Z.yml
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic "Home" [ref=e4] [cursor=pointer]:
|
||||
- img [ref=e5]
|
||||
- generic "Search" [ref=e8] [cursor=pointer]:
|
||||
- img [ref=e9]
|
||||
- generic "Terminal" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic "User" [ref=e15] [cursor=pointer]:
|
||||
- img [ref=e16]
|
||||
- generic "Apps" [ref=e19] [cursor=pointer]:
|
||||
- img [ref=e20]
|
||||
- generic "Settings" [ref=e25] [cursor=pointer]:
|
||||
- img [ref=e26]
|
||||
- generic [ref=e29]:
|
||||
- generic [ref=e30]:
|
||||
- generic [ref=e31]:
|
||||
- generic [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e34]
|
||||
- generic [ref=e36]: Vibe
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e39]
|
||||
- generic [ref=e44]: CRM
|
||||
- generic [ref=e45] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- generic [ref=e49]: Campaigns
|
||||
- generic [ref=e50] [cursor=pointer]:
|
||||
- img [ref=e52]
|
||||
- generic [ref=e53]: Lists
|
||||
- generic [ref=e54] [cursor=pointer]:
|
||||
- img [ref=e56]
|
||||
- generic [ref=e58]: Templates
|
||||
- generic [ref=e59] [cursor=pointer]:
|
||||
- img [ref=e61]
|
||||
- generic [ref=e64]: Tasks
|
||||
- generic [ref=e65] [cursor=pointer]:
|
||||
- img [ref=e67]
|
||||
- generic [ref=e69]: Chat
|
||||
- generic [ref=e70] [cursor=pointer]:
|
||||
- img [ref=e72]
|
||||
- generic [ref=e74]: Terminal
|
||||
- generic [ref=e75] [cursor=pointer]:
|
||||
- img [ref=e77]
|
||||
- generic [ref=e79]: Explorer
|
||||
- generic [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e82]
|
||||
- generic [ref=e85]: Editor
|
||||
- generic [ref=e86] [cursor=pointer]:
|
||||
- img [ref=e88]
|
||||
- generic [ref=e93]: Designer
|
||||
- generic [ref=e94] [cursor=pointer]:
|
||||
- img [ref=e96]
|
||||
- generic [ref=e99]: BASIC
|
||||
- generic [ref=e100] [cursor=pointer]:
|
||||
- img [ref=e102]
|
||||
- generic [ref=e105]: Browser
|
||||
- generic [ref=e106]:
|
||||
- generic [ref=e107]:
|
||||
- generic [ref=e108]: Chat
|
||||
- generic [ref=e109]:
|
||||
- button [ref=e110] [cursor=pointer]:
|
||||
- img [ref=e111]
|
||||
- button [ref=e112] [cursor=pointer]:
|
||||
- img [ref=e113]
|
||||
- button [ref=e115] [cursor=pointer]:
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e120]:
|
||||
- complementary [ref=e121]:
|
||||
- button "Chat" [ref=e122]:
|
||||
- img [ref=e123]
|
||||
- button "Tasks" [ref=e125]:
|
||||
- img [ref=e126]
|
||||
- button "Terminal" [ref=e129]:
|
||||
- img [ref=e130]
|
||||
- button "Explorer" [ref=e132]:
|
||||
- img [ref=e133]
|
||||
- button "Editor" [ref=e135]:
|
||||
- img [ref=e136]
|
||||
- button "Browser" [ref=e139]:
|
||||
- img [ref=e140]
|
||||
- main
|
||||
- generic [ref=e143]:
|
||||
- generic [ref=e144]:
|
||||
- text: // BROWSER
|
||||
- textbox "No preview active" [ref=e145]
|
||||
- generic [ref=e147]: Waiting for app preview...
|
||||
- generic [ref=e149]: // TERMINAL
|
||||
- generic [ref=e150]:
|
||||
- generic [ref=e151]: "Agent #1"
|
||||
- text: EVOLVED
|
||||
- generic [ref=e152]: Claude Opus 4.5 — 99%
|
||||
- generic [ref=e153]:
|
||||
- generic [ref=e154]:
|
||||
- text: Plan
|
||||
- button [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- text: YOLO
|
||||
- button [ref=e157]
|
||||
- generic [ref=e158]:
|
||||
- generic [ref=e159]:
|
||||
- button "◄" [ref=e160]
|
||||
- text: 0 / 0
|
||||
- button "►" [ref=e161]
|
||||
- generic [ref=e162]:
|
||||
- button "💬" [ref=e163]
|
||||
- button "✏️" [ref=e164]
|
||||
- button "</>" [ref=e165]
|
||||
- contentinfo [ref=e166]:
|
||||
- generic [ref=e168]: Reference Entity
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]:
|
||||
- button "Agent" [ref=e171]
|
||||
- button "Chat" [ref=e172]
|
||||
- textbox "Message... (type @ to mention)" [active] [ref=e173]
|
||||
- button "↑" [ref=e174]
|
||||
- button "Scroll to bottom" [ref=e175]:
|
||||
- img [ref=e176]
|
||||
- button "View" [ref=e180]
|
||||
- contentinfo [ref=e181]:
|
||||
- img [ref=e185] [cursor=pointer]
|
||||
- generic [ref=e187]:
|
||||
- combobox [ref=e189] [cursor=pointer]:
|
||||
- option "🎨 Default"
|
||||
- option "☀️ Light"
|
||||
- option "🍊 Orange"
|
||||
- option "🌃 Cyberpunk"
|
||||
- option "🌴 Retrowave"
|
||||
- option "💭 Vapor Dream"
|
||||
- option "✨ Y2K"
|
||||
- option "🔲 3D Bevel"
|
||||
- option "🕹️ Arcade"
|
||||
- option "🪩 Disco"
|
||||
- option "🎸 Grunge"
|
||||
- option "🎺 Jazz"
|
||||
- option "🌻 Mellow"
|
||||
- option "🏠 Mid Century"
|
||||
- option "📷 Polaroid"
|
||||
- option "📺 Cartoons"
|
||||
- option "🏖️ Seaside"
|
||||
- option "⌨️ Typewriter" [selected]
|
||||
- option "📠 Xerox"
|
||||
- option "📁 XTree"
|
||||
- button "Sign In" [ref=e190] [cursor=pointer]
|
||||
- generic [ref=e191]:
|
||||
- generic [ref=e192]: 00:00
|
||||
- generic [ref=e193]: 01/01/2026
|
||||
16
.playwright-mcp/suggestions-container-check.json
Normal file
16
.playwright-mcp/suggestions-container-check.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"found": true,
|
||||
"tag": "DIV",
|
||||
"className": "suggestions-container",
|
||||
"innerHTML": "",
|
||||
"childCount": 0,
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"opacity": "1",
|
||||
"height": "4px",
|
||||
"maxHeight": "100px",
|
||||
"overflow": "auto",
|
||||
"hasClass": false,
|
||||
"parent": "FOOTER ",
|
||||
"styleAttr": null
|
||||
}
|
||||
64
.playwright-mcp/suggestions-css-rules.json
Normal file
64
.playwright-mcp/suggestions-css-rules.json
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"rules": [
|
||||
{
|
||||
"sheet": "https://chat.pragmatismo.com.br/suite/css/app.css",
|
||||
"selector": "*",
|
||||
"display": "",
|
||||
"maxHeight": "",
|
||||
"overflow": "",
|
||||
"opacity": "",
|
||||
"visibility": "",
|
||||
"height": "",
|
||||
"padding": "0px",
|
||||
"margin": "0px"
|
||||
},
|
||||
{
|
||||
"sheet": "https://chat.pragmatismo.com.br/suite/css/app.css",
|
||||
"selector": "*",
|
||||
"display": "",
|
||||
"maxHeight": "",
|
||||
"overflow": "",
|
||||
"opacity": "",
|
||||
"visibility": "",
|
||||
"height": "",
|
||||
"padding": "",
|
||||
"margin": ""
|
||||
},
|
||||
{
|
||||
"sheet": "https://chat.pragmatismo.com.br/suite/css/base.css",
|
||||
"selector": "*",
|
||||
"display": "",
|
||||
"maxHeight": "",
|
||||
"overflow": "",
|
||||
"opacity": "",
|
||||
"visibility": "",
|
||||
"height": "",
|
||||
"padding": "0px",
|
||||
"margin": "0px"
|
||||
},
|
||||
{
|
||||
"sheet": "inline",
|
||||
"selector": "*",
|
||||
"display": "",
|
||||
"maxHeight": "",
|
||||
"overflow": "",
|
||||
"opacity": "",
|
||||
"visibility": "",
|
||||
"height": "",
|
||||
"padding": "0px",
|
||||
"margin": "0px"
|
||||
},
|
||||
{
|
||||
"sheet": "https://chat.pragmatismo.com.br/suite/chat/chat.css?v=3",
|
||||
"selector": ".suggestions-container",
|
||||
"display": "flex",
|
||||
"maxHeight": "100px",
|
||||
"overflow": "",
|
||||
"opacity": "",
|
||||
"visibility": "",
|
||||
"height": "",
|
||||
"padding": "",
|
||||
"margin": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
112
.playwright-mcp/suggestions-dom-check.json
Normal file
112
.playwright-mcp/suggestions-dom-check.json
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
[
|
||||
{
|
||||
"tag": "DIV",
|
||||
"className": "suggestions-container has-bot-suggestions",
|
||||
"id": "suggestions",
|
||||
"text": "Agendar BatizadoAgendar CasamentoAgendar MissaAgendar PeregrinaçãoPedido de OraçãoUso de ImagemLicen",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "FOOTER."
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Agendar Batizado",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Agendar Casamento",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Agendar Missa",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Agendar Peregrinação",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Pedido de Oração",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Uso de Imagem",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Licenciamento",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Evento/Iluminação",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Cadastrar Guia",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
},
|
||||
{
|
||||
"tag": "BUTTON",
|
||||
"className": "suggestion-chip",
|
||||
"id": "",
|
||||
"text": "Fazer Doação",
|
||||
"display": "flex",
|
||||
"visibility": "visible",
|
||||
"hidden": false,
|
||||
"parent": "DIV.suggestions-container has-bot-suggestions"
|
||||
}
|
||||
]
|
||||
68
AGENTS.md
68
AGENTS.md
|
|
@ -246,6 +246,7 @@ match x {
|
|||
|
||||
## ❌ Absolute Prohibitions
|
||||
- NEVER search /target folder! It is binary compiled.
|
||||
- ❌ **NEVER** hardcode passwords, tokens, API keys, or any credentials in source code — ALWAYS use `generate_random_string()` or environment variables
|
||||
- ❌ **NEVER** build in release mode - ONLY debug builds allowed
|
||||
- ❌ **NEVER** use `--release` flag on ANY cargo command
|
||||
- ❌ **NEVER** run `cargo build` - use `cargo check` for syntax verification
|
||||
|
|
@ -860,3 +861,70 @@ Continue on gb/ workspace. Follow AGENTS.md strictly:
|
|||
- **VERIFY LAST** - Only compile/diagnostics after ALL fixes
|
||||
- **DELETE DEAD CODE** - Don't keep unused code around
|
||||
- **GIT WORKFLOW** - ALWAYS push to ALL repositories (github, pragmatismo)
|
||||
|
||||
---
|
||||
|
||||
## Deploy in Prod Workflow
|
||||
|
||||
### CI/CD Pipeline (Primary Method)
|
||||
|
||||
1. **Push to ALM** — triggers CI/CD automatically:
|
||||
```bash
|
||||
cd botserver
|
||||
git push alm main
|
||||
git push origin main
|
||||
cd ..
|
||||
git add botserver
|
||||
git commit -m "Update botserver: <description>"
|
||||
git push alm main
|
||||
git push origin main
|
||||
```
|
||||
|
||||
2. **Wait for CI** — build takes ~3-4 minutes. Check status:
|
||||
```bash
|
||||
# Via web: https://alm.pragmatismo.com.br/GeneralBots/botserver/actions
|
||||
# Or check binary timestamp after ~4 min sleep:
|
||||
sleep 240
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \
|
||||
"sudo incus exec system -- stat -c '%y' /opt/gbo/bin/botserver"
|
||||
```
|
||||
|
||||
3. **Restart in prod** — after binary updates:
|
||||
```bash
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \
|
||||
"sudo incus exec system -- pkill -f botserver || true"
|
||||
sleep 2
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \
|
||||
"sudo incus exec system -- bash -c 'cd /opt/gbo/bin && sudo -u gbuser RUST_LOG=info ./botserver --noconsole > /opt/gbo/logs/botserver-output.log 2>&1 &'"
|
||||
```
|
||||
|
||||
4. **Verify deployment**:
|
||||
```bash
|
||||
# Wait for bootstrap (~2 min)
|
||||
sleep 120
|
||||
# Check health
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \
|
||||
"sudo incus exec system -- curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/health"
|
||||
# Check logs
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \
|
||||
"sudo incus exec system -- tail -30 /opt/gbo/logs/botserver-output.log"
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
|---------|-------|-----|
|
||||
| `valkey-cli ping` hangs indefinitely | Valkey requires password auth | Install `nc` or `ss` in container for health checks |
|
||||
| `nc: command not found` | Prod container lacks netcat | `sudo incus exec system -- apt-get install -y netcat-openbsd` |
|
||||
| Cache connection timeout | iptables DROP rule on port 6379 | `sudo incus exec system -- iptables -I INPUT -i lo -j ACCEPT` |
|
||||
| `AUTH failed` on Valkey | Valkey runs without password but Vault has one | Code tries no-password URL first, then with password |
|
||||
| `Cannot start a runtime from within a runtime` | `block_on()` called from async context | Use `.await` directly, never `runtime.block_on()` in async functions |
|
||||
| Secret not found in Vault | Path mismatch between seeding and reading | Seeding: `secret/gbo/cache`, Reading: `gbo/cache` (kv2 prepends `secret/`) |
|
||||
| CI completed but binary not updated | Deploy step fails silently (SSH/transfer issue) | Build locally and transfer, or re-push to trigger CI again |
|
||||
|
||||
### Critical Paths in Vault
|
||||
|
||||
- **Seeding writes to**: `secret/gbo/{service}` (e.g., `secret/gbo/cache`)
|
||||
- **Code reads via**: `SecretPaths::{SERVICE}` which maps to `gbo/{service}`
|
||||
- **kv2::read** prepends `secret/` automatically and looks up `secret/data/gbo/{service}`
|
||||
- **All paths must match**: `gbo/cache`, `gbo/drive`, `gbo/tables`, `gbo/directory`, `gbo/llm`, `gbo/meet`, `gbo/alm`, `gbo/vectordb`, `gbo/encryption`, `gbo/email`
|
||||
|
|
|
|||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1275,7 +1275,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "botapp"
|
||||
version = "6.3.0"
|
||||
version = "6.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"botlib",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ members = [
|
|||
"bottest",
|
||||
"botui",
|
||||
]
|
||||
exclude = ["backup-to-s3"]
|
||||
|
||||
[workspace.lints.rust]
|
||||
|
||||
|
|
|
|||
7
backup-to-s3/.env.example
Normal file
7
backup-to-s3/.env.example
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
AWS_ACCESS_KEY_ID=your_access_key
|
||||
AWS_SECRET_ACCESS_KEY=your_secret_key
|
||||
AWS_REGION=us-east-1
|
||||
S3_BUCKET=prod-gbo-backup
|
||||
INCUS_PROJECT=PROD-GBO1
|
||||
CONTAINER_NAME=
|
||||
KEEP_COUNT=10
|
||||
1818
backup-to-s3/Cargo.lock
generated
Normal file
1818
backup-to-s3/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
15
backup-to-s3/Cargo.toml
Normal file
15
backup-to-s3/Cargo.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "backup-to-s3"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
rusoto_s3 = "0.48"
|
||||
rusoto_core = "0.48"
|
||||
rusoto_credential = "0.48"
|
||||
dotenv = "0.15"
|
||||
chrono = "0.4"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing-appender = "0.2"
|
||||
213
backup-to-s3/src/main.rs
Normal file
213
backup-to-s3/src/main.rs
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
use chrono::Utc;
|
||||
use rusoto_core::Region;
|
||||
use rusoto_credential::StaticProvider;
|
||||
use rusoto_s3::{S3, S3Client};
|
||||
use std::env;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Config {
|
||||
access_key: String,
|
||||
secret_key: String,
|
||||
bucket: String,
|
||||
project: String,
|
||||
keep_count: usize,
|
||||
region: Region,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn from_env() -> Result<Self, String> {
|
||||
dotenv::from_path(".env").ok();
|
||||
|
||||
let access_key = env::var("AWS_ACCESS_KEY_ID").map_err(|_| "AWS_ACCESS_KEY_ID not set")?;
|
||||
let secret_key = env::var("AWS_SECRET_ACCESS_KEY").map_err(|_| "AWS_SECRET_ACCESS_KEY not set")?;
|
||||
let bucket = env::var("S3_BUCKET").map_err(|_| "S3_BUCKET not set")?;
|
||||
let project = env::var("INCUS_PROJECT").unwrap_or_else(|_| "PROD-GBO1".to_string());
|
||||
let keep_count: usize = env::var("KEEP_COUNT").unwrap_or_else(|_| "10".to_string()).parse().unwrap_or(10);
|
||||
let region_name = env::var("AWS_REGION").unwrap_or_else(|_| "us-east-1".to_string());
|
||||
let region = region_name.parse().unwrap_or(Region::UsEast1);
|
||||
|
||||
Ok(Config {
|
||||
access_key,
|
||||
secret_key,
|
||||
bucket,
|
||||
project,
|
||||
keep_count,
|
||||
region,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter("info")
|
||||
.init();
|
||||
|
||||
let config = Config::from_env()?;
|
||||
info!("Starting backup to S3 bucket: {}", config.bucket);
|
||||
|
||||
let credential_provider = StaticProvider::new_minimal(
|
||||
config.access_key.clone(),
|
||||
config.secret_key.clone(),
|
||||
);
|
||||
let s3_client = S3Client::new_with(
|
||||
rusoto_core::HttpClient::new().map_err(|e| format!("HTTP client: {}", e))?,
|
||||
credential_provider,
|
||||
config.region.clone(),
|
||||
);
|
||||
|
||||
let timestamp = Utc::now().format("%Y-%m-%d").to_string();
|
||||
let container_name = env::var("CONTAINER_NAME").unwrap_or_default();
|
||||
|
||||
let containers = if container_name.is_empty() {
|
||||
list_inc_containers(&config.project).await?
|
||||
} else {
|
||||
vec![container_name]
|
||||
};
|
||||
|
||||
for container in containers {
|
||||
info!("Backing up container: {}", container);
|
||||
|
||||
match backup_container(&container, &config, &s3_client, ×tamp).await {
|
||||
Ok(_) => info!("Successfully backed up {}", container),
|
||||
Err(e) => {
|
||||
error!("Failed to backup {}: {}", container, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_old_backups(&config, &s3_client).await?;
|
||||
|
||||
info!("Backup completed!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_inc_containers(project: &str) -> Result<Vec<String>, String> {
|
||||
let output = tokio::process::Command::new("incus")
|
||||
.args(["list", "--project", project, "--format", "csv", "-c", "n"])
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to list containers: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("incus list failed".to_string());
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let containers: Vec<String> = stdout.lines()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
|
||||
Ok(containers)
|
||||
}
|
||||
|
||||
async fn backup_container(
|
||||
container: &str,
|
||||
config: &Config,
|
||||
s3_client: &S3Client,
|
||||
timestamp: &str,
|
||||
) -> Result<(), String> {
|
||||
let temp_dir = std::env::temp_dir();
|
||||
let backup_file = temp_dir.join(format!("{}.tar.gz", container));
|
||||
|
||||
info!("Exporting {} to {:?}", container, backup_file);
|
||||
|
||||
let export_output = tokio::process::Command::new("incus")
|
||||
.args([
|
||||
"export",
|
||||
container,
|
||||
backup_file.to_str().unwrap(),
|
||||
"--instance-only",
|
||||
"--project",
|
||||
&config.project,
|
||||
])
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to export container: {}", e))?;
|
||||
|
||||
if !export_output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&export_output.stderr);
|
||||
return Err(format!("incus export failed: {}", stderr));
|
||||
}
|
||||
|
||||
let s3_key = format!("{}/{}.tar.gz", timestamp, container);
|
||||
info!("Uploading to s3://{}/{}", config.bucket, s3_key);
|
||||
|
||||
let file_data = tokio::fs::read(&backup_file)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read backup file: {}", e))?;
|
||||
|
||||
let put_request = rusoto_s3::PutObjectRequest {
|
||||
body: Some(file_data.into()),
|
||||
bucket: config.bucket.clone(),
|
||||
key: s3_key,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
s3_client.put_object(put_request)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to upload to S3: {}", e))?;
|
||||
|
||||
tokio::fs::remove_file(&backup_file)
|
||||
.await
|
||||
.map_err(|e| warn!("Failed to delete temp file: {}", e)).ok();
|
||||
|
||||
info!("Uploaded {}", container);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn cleanup_old_backups(config: &Config, s3_client: &S3Client) -> Result<(), String> {
|
||||
info!("Cleaning up old backups, keeping {} most recent", config.keep_count);
|
||||
|
||||
let list_output = s3_client
|
||||
.list_objects_v2(rusoto_s3::ListObjectsV2Request {
|
||||
bucket: config.bucket.clone(),
|
||||
delimiter: Some("/".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.map_err(|e| format!("Failed to list S3: {}", e))?;
|
||||
|
||||
let mut prefixes: Vec<String> = list_output
|
||||
.common_prefixes
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|p| p.prefix.clone().unwrap_or_default().trim_end_matches('/').to_string())
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect();
|
||||
|
||||
prefixes.sort_by(|a, b| b.cmp(a));
|
||||
|
||||
if prefixes.len() > config.keep_count {
|
||||
for prefix in prefixes.iter().skip(config.keep_count) {
|
||||
info!("Deleting old backup: {}", prefix);
|
||||
|
||||
let objects = s3_client
|
||||
.list_objects_v2(rusoto_s3::ListObjectsV2Request {
|
||||
bucket: config.bucket.clone(),
|
||||
prefix: Some(format!("{}/", prefix)),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.map_err(|e| format!("Failed to list objects: {}", e))?;
|
||||
|
||||
for obj in objects.contents.unwrap_or_default() {
|
||||
if let Some(key) = obj.key {
|
||||
s3_client
|
||||
.delete_object(rusoto_s3::DeleteObjectRequest {
|
||||
bucket: config.bucket.clone(),
|
||||
key,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit dae0feb6a567dd1b7e66a382786deef4e7f1fb8a
|
||||
Subproject commit 7b4753af0d1c8b2ce763e5171676e8116ddd5fb4
|
||||
Binary file not shown.
5
ping_google.cmd
Normal file
5
ping_google.cmd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@echo off
|
||||
:loop
|
||||
ping google.com
|
||||
timeout /t 18000 /nobreak >nul
|
||||
goto loop
|
||||
110
prompts/c1.md
110
prompts/c1.md
|
|
@ -1,85 +1,29 @@
|
|||
# Plan: Migrate LXC Containers to Incus (COMPLETED)
|
||||
Migrate 10 LXC containers from source (root@82.29.59.188 - LXD/ZFS) to destination (administrator@63.141.255.9 - Incus/Btrfs) using streaming method.
|
||||
Instructions
|
||||
- Use Btrfs storage on destination (loop1 mounted at /mnt/btrfs)
|
||||
- Use streaming method: tar | zstd | ssh (no local storage)
|
||||
- Migrate one by one: stop → copy → create → start → delete
|
||||
- DO NOT delete containers before copying data
|
||||
Discoveries
|
||||
- Source: LXD with ZFS backend (default/containers/*), containers at /var/snap/lxd/common/lxd/storage-pools/default/containers/
|
||||
- Destination: Incus 6.23, Btrfs pool PROD-GBO on loop1, project PROD-GBO1
|
||||
- SSH: Works from source root → destination administrator
|
||||
- MISTAKE MADE: Deleted all Incus containers with cleanup command before data was properly inside them
|
||||
Accomplished
|
||||
- ✅ Created Btrfs storage pool PROD-GBO on destination
|
||||
- ✅ SSH access configured from source to destination
|
||||
- ❌ Containers were deleted during cleanup - they need to be recreated
|
||||
- ❌ Data is outside containers, needs to be copied INTO containers properly
|
||||
|
||||
## Summary
|
||||
✅ All containers migrated from LXD (pragmatismo.com.br) to Incus (63.141.255.9)
|
||||
✅ All data synced from host /opt/gbo/tenants/ to containers
|
||||
✅ All binaries copied from source to containers
|
||||
✅ All services configured and running
|
||||
Next steps
|
||||
1. Create empty Incus containers: incus create --empty --project PROD-GBO1 -s PROD-GBO
|
||||
2. Migrate from source: tar -C rootfs -cf - . | zstd | ssh dest 'sudo tar -I zstd -xf - -C /mnt/btrfs/containers/PROD-GBO1_<name>/rootfs'
|
||||
3. Start containers: incus start <name>
|
||||
4. Delete from source: lxc delete --force
|
||||
|
||||
## Container & Service Status
|
||||
| Container | Service | Status |
|
||||
|-----------|---------|--------|
|
||||
| dns | coredns | ✅ RUNNING |
|
||||
| email | stalwart-mail | ✅ RUNNING |
|
||||
| webmail | php built-in server (:5252) | ✅ RUNNING |
|
||||
| alm | forgejo | ✅ RUNNING |
|
||||
| drive | minio | ✅ RUNNING |
|
||||
| tables | postgresql | ✅ RUNNING |
|
||||
| system | botserver | ✅ RUNNING |
|
||||
|
||||
|
||||
## Service Files Location
|
||||
All service files in `/etc/systemd/system/` inside containers:
|
||||
- `dns.service` - coredns (User=root)
|
||||
- `email.service` - stalwart-mail (User=root)
|
||||
- `alm.service` - forgejo (User=alm, Group=alm)
|
||||
- `minio.service` - minio (User=root)
|
||||
|
||||
## Binary Locations
|
||||
| Service | Binary Path |
|
||||
|---------|-------------|
|
||||
| coredns | /opt/gbo/bin/coredns |
|
||||
| stalwart | /opt/gbo/bin/stalwart |
|
||||
| forgejo | /opt/gbo/bin/forgejo |
|
||||
| minio | /usr/local/bin/minio |
|
||||
|
||||
## Key Paths Inside Containers
|
||||
- **Binaries**: /opt/gbo/bin/
|
||||
- **Data**: /opt/gbo/data/
|
||||
- **Config**: /opt/gbo/conf/
|
||||
- **Logs**: /opt/gbo/logs/
|
||||
|
||||
## IPS (Destination)
|
||||
- dns: 10.107.115.155
|
||||
- email: 10.107.115.200
|
||||
- webmail: 10.107.115.208
|
||||
- alm: 10.107.115.4
|
||||
- drive: 10.107.115.114
|
||||
- tables: 10.107.115.33
|
||||
- system: 10.107.115.229
|
||||
|
||||
- alm-ci: 10.107.115.190
|
||||
- table-editor: 10.107.115.73
|
||||
|
||||
## Port Forwarding (iptables NAT)
|
||||
```
|
||||
# DNS
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j DNAT --to-destination 10.107.115.155:53
|
||||
sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination 10.107.115.155:53
|
||||
|
||||
# Email
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 10.107.115.200:25
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 587 -j DNAT --to-destination 10.107.115.200:587
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 465 -j DNAT --to-destination 10.107.115.200:465
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 143 -j DNAT --to-destination 10.107.115.200:143
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 993 -j DNAT --to-destination 10.107.115.200:993
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 110 -j DNAT --to-destination 10.107.115.200:110
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 995 -j DNAT --to-destination 10.107.115.200:995
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 4190 -j DNAT --to-destination 10.107.115.200:4190
|
||||
|
||||
# Webmail
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 5252 -j DNAT --to-destination 10.107.115.208:5252
|
||||
|
||||
# ALM (forgejo)
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747
|
||||
|
||||
# Caddy (80, 443) - already exists for proxy container
|
||||
```
|
||||
|
||||
## Workflow (PRODUCTION TESTED)
|
||||
1. Copy container: `incus copy --instance-only lxd-source:<source> <dest>`
|
||||
2. Add eth0 network: `incus config device add <c> eth0 nic name=eth0 network=PROD-GBO`
|
||||
3. Sync data: `incus file push --recursive /opt/gbo/tenants/pragmatismo/<container>/ <container>/opt/gbo/`
|
||||
4. Copy binaries: from source via `lxc file pull` → scp to dest → `incus file push`
|
||||
5. Create service file: `cat > /tmp/<svc>.service && incus file push <svc>.service <c>/etc/systemd/system/`
|
||||
6. Enable/start: `incus exec <c> -- systemctl enable <svc> && systemctl start <svc>`
|
||||
Accomplished
|
||||
- Recreated Btrfs storage pool PROD-GBO on /mnt/btrfs
|
||||
- Created 10 empty Incus containers
|
||||
- Migrated all containers using tar | zstd | ssh
|
||||
- All 10 containers running on Btrfs
|
||||
- Used ~24GB of Btrfs storage
|
||||
|
|
|
|||
461
prompts/c3.md
Normal file
461
prompts/c3.md
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
# Fast Migration Plan - LXC/LXD to Incus (ONE BY ONE)
|
||||
|
||||
## Strategy: STREAMING DATA TRANSFER (NO DISK I/O ON SOURCE)
|
||||
|
||||
Transfer container data directly via compressed stream, NO intermediate tarballs on source.
|
||||
Use `pv` (pipe viewer) for colorful progress bars and rate monitoring.
|
||||
|
||||
---
|
||||
|
||||
## Why This is FASTEST
|
||||
|
||||
✅ **NO disk writes on source** (99% full) - streaming only
|
||||
✅ **zstd compression** - 3-5x faster than gzip
|
||||
✅ **pv monitoring** - colorful progress, ETA, rate
|
||||
✅ **Parallel transfers** - migrate multiple containers simultaneously
|
||||
✅ **Immediate deletion** - frees space for next transfer
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### On Source (82.29.59.188)
|
||||
```bash
|
||||
# Install required tools
|
||||
sudo apt update && sudo apt install -y zstd pv
|
||||
|
||||
# List all containers
|
||||
lxc list
|
||||
```
|
||||
|
||||
### On Destination (63.141.255.9)
|
||||
```bash
|
||||
# Incus is already installed and clean (LXC/LXD removed)
|
||||
|
||||
# Create PROD-GBO network bridge
|
||||
sudo incus network create PROD-GBO --type bridge ipv4.address=10.107.115.1/24
|
||||
|
||||
# Create PROD-GBO1 project
|
||||
sudo incus project create PROD-GBO1
|
||||
sudo incus project switch PROD-GBO1
|
||||
|
||||
# Configure project
|
||||
sudo incus project set PROD-GBO1 features.storage.volumes=true
|
||||
sudo incus project set PROD-GBO1 features.profiles=true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Workflow (Per Container)
|
||||
|
||||
### Step 1: Stop Container on Source
|
||||
```bash
|
||||
# On source server
|
||||
lxc stop pragmatismo-alm
|
||||
```
|
||||
|
||||
### Step 2: Stream Data to Destination (FASTEST - NO DISK I/O)
|
||||
```bash
|
||||
# On source server - stream compressed data directly
|
||||
lxc exec pragmatismo-alm -- tar -cf - /opt/gbo/ | \
|
||||
zstd -3 -q | \
|
||||
pv -s $(du -sb /opt/gbo | awk '{print $1}') | \
|
||||
ssh administrator@63.141.255.9 "zstd -d | tar -xf - -C ~/gbo/pragmatismo-alm/"
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. `tar` reads /opt/gbo from container (streamed, no disk write)
|
||||
2. `zstd -3` compresses on-the-fly (fast compression)
|
||||
3. `pv` shows colorful progress with ETA and rate
|
||||
4. `ssh` streams to destination
|
||||
5. Destination decompresses and extracts directly to ~/gbo/pragmatismo-alm/
|
||||
6. **NO tarball on source disk** - solves 99% full problem!
|
||||
|
||||
**pv Options:**
|
||||
- `-s` : Total size (from `du -sb` for accurate progress)
|
||||
- Colorful output with ETA, rate, elapsed time
|
||||
|
||||
### Step 3: Delete Container from Source
|
||||
```bash
|
||||
# Free space immediately for next transfer
|
||||
lxc delete pragmatismo-alm
|
||||
```
|
||||
|
||||
### Step 4: Create Fresh Incus Container
|
||||
```bash
|
||||
# On destination server
|
||||
sudo incus launch images:debian/12/cloud pragmatismo-alm
|
||||
|
||||
# Add network
|
||||
sudo incus config device add pragmatismo-alm eth0 nic name=eth0 network=PROD-GBO
|
||||
|
||||
# Set static IP
|
||||
sudo incus config set pragmatismo-alm ipv4.address=10.107.115.4
|
||||
|
||||
# Create gbuser inside container
|
||||
sudo incus exec pragmatismo-alm -- useradd -m -s /bin/bash gbuser
|
||||
```
|
||||
|
||||
### Step 5: Push Data to Container
|
||||
```bash
|
||||
# Create directory structure
|
||||
sudo incus exec pragmatismo-alm -- mkdir -p /opt/gbo
|
||||
|
||||
# Push data (recursive)
|
||||
sudo incus file push -r ~/gbo/pragmatismo-alm/* pragmatismo-alm/opt/gbo/
|
||||
|
||||
# Fix ownership
|
||||
sudo incus exec pragmatismo-alm -- chown -R gbuser:gbuser /opt/gbo
|
||||
|
||||
# Make binaries executable
|
||||
sudo incus exec pragmatismo-alm -- chmod +x /opt/gbo/bin/*
|
||||
```
|
||||
|
||||
### Step 6: Start Container and Verify Service
|
||||
```bash
|
||||
sudo incus start pragmatismo-alm
|
||||
|
||||
# Check service status
|
||||
sudo incus exec pragmatismo-alm -- systemctl status alm
|
||||
|
||||
# Check logs
|
||||
sudo incus exec pragmatismo-alm -- journalctl -u alm -f
|
||||
```
|
||||
|
||||
### Step 7: Configure NAT Rules (ON HOST, NOT IN CONTAINER)
|
||||
```bash
|
||||
# On destination host (63.141.255.9)
|
||||
|
||||
# For ALM (example):
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.4 --dport 4747 -j ACCEPT
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.4 -j MASQUERADE
|
||||
|
||||
# Save rules
|
||||
sudo sh -c 'iptables-save > /etc/iptables/rules.v4'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Full Migration Script (ALL CONTAINERS)
|
||||
|
||||
### Run Multiple Containers in Parallel (for speed)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# MIGRATE-ALL.sh - Fast parallel migration
|
||||
|
||||
set -e
|
||||
|
||||
# Container list with IPs
|
||||
declare -A CONTAINERS=(
|
||||
["dns"]="10.107.115.155"
|
||||
["email"]="10.107.115.200"
|
||||
["webmail"]="10.107.115.208"
|
||||
["alm"]="10.107.115.4"
|
||||
["drive"]="10.107.115.114"
|
||||
["tables"]="10.107.115.33"
|
||||
["system"]="10.107.115.229"
|
||||
["alm-ci"]="10.107.115.190"
|
||||
["table-editor"]="10.107.115.73"
|
||||
)
|
||||
|
||||
for container in "${!CONTAINERS[@]}"; do
|
||||
ip="${CONTAINERS[$container]}"
|
||||
|
||||
echo -e "\e[1;32m=== Migrating $container ($ip) ===\e[0m"
|
||||
|
||||
# Step 1: Stop
|
||||
echo -e "\e[1;33mStopping $container...\e[0m"
|
||||
ssh root@82.29.59.188 "lxc stop $container" || true
|
||||
|
||||
# Step 2: Get size for pv
|
||||
echo -e "\e[1;33mGetting size of /opt/gbo...\e[0m"
|
||||
size=$(ssh root@82.29.59.188 "lxc exec $container -- du -sb /opt/gbo | awk '{print \$1}'")
|
||||
|
||||
# Step 3: Stream data (FAST!)
|
||||
echo -e "\e[1;33mStreaming data to destination...\e[0m"
|
||||
ssh root@82.29.59.188 "lxc exec $container -- tar -cf - /opt/gbo/" | \
|
||||
zstd -3 -q | \
|
||||
pv -s $size | \
|
||||
ssh administrator@63.141.255.9 "mkdir -p ~/gbo/$container && zstd -d | tar -xf - -C ~/gbo/$container/"
|
||||
|
||||
# Step 4: Delete from source
|
||||
echo -e "\e[1;33mDeleting $container from source...\e[0m"
|
||||
ssh root@82.29.59.188 "lxc delete $container"
|
||||
|
||||
# Step 5: Create fresh container
|
||||
echo -e "\e[1;33mCreating Incus container...\e[0m"
|
||||
ssh administrator@63.141.255.9 "sudo incus launch images:debian/12/cloud $container && \
|
||||
sudo incus config device add $container eth0 nic name=eth0 network=PROD-GBO && \
|
||||
sudo incus config set $container ipv4.address=$ip && \
|
||||
sudo incus exec $container -- useradd -m -s /bin/bash gbuser"
|
||||
|
||||
# Step 6: Push data
|
||||
echo -e "\e[1;33mPushing data to container...\e[0m"
|
||||
ssh administrator@63.141.255.9 "sudo incus exec $container -- mkdir -p /opt/gbo && \
|
||||
sudo incus file push -r ~/gbo/$container/* $container:/opt/gbo/ && \
|
||||
sudo incus exec $container -- chown -R gbuser:gbuser /opt/gbo && \
|
||||
sudo incus exec $container -- chmod +x /opt/gbo/bin/*"
|
||||
|
||||
# Step 7: Start
|
||||
echo -e "\e[1;33mStarting $container...\e[0m"
|
||||
ssh administrator@63.141.255.9 "sudo incus start $container"
|
||||
|
||||
echo -e "\e[1;32m✓ $container migrated successfully!\e[0m"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo -e "\e[1;32m=== ALL MIGRATIONS COMPLETE ===\e[0m"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Single Container Migration (Quick Test)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# migrate-one.sh - Migrate single container
|
||||
|
||||
CONTAINER=$1
|
||||
|
||||
if [ -z "$CONTAINER" ]; then
|
||||
echo "Usage: $0 <container-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "\e[1;32m=== Migrating $CONTAINER ===\e[0m"
|
||||
|
||||
# Stop on source
|
||||
echo -e "\e[1;33m[1/7] Stopping container...\e[0m"
|
||||
ssh root@82.29.59.188 "lxc stop $CONTAINER"
|
||||
|
||||
# Get size
|
||||
echo -e "\e[1;33m[2/7] Getting data size...\e[0m"
|
||||
SIZE=$(ssh root@82.29.59.188 "lxc exec $CONTAINER -- du -sb /opt/gbo | awk '{print \$1}'")
|
||||
|
||||
# Stream data (NO DISK WRITE!)
|
||||
echo -e "\e[1;33m[3/7] Streaming data (pv will show progress)...\e[0m"
|
||||
ssh root@82.29.59.188 "lxc exec $CONTAINER -- tar -cf - /opt/gbo/" | \
|
||||
zstd -3 -q | \
|
||||
pv -s $SIZE | \
|
||||
ssh administrator@63.141.255.9 "mkdir -p ~/gbo/$CONTAINER && zstd -d | tar -xf - -C ~/gbo/$CONTAINER/"
|
||||
|
||||
# Delete from source
|
||||
echo -e "\e[1;33m[4/7] Deleting from source...\e[0m"
|
||||
ssh root@82.29.59.188 "lxc delete $CONTAINER"
|
||||
|
||||
# Create container
|
||||
echo -e "\e[1;33m[5/7] Creating Incus container...\e[0m"
|
||||
ssh administrator@63.141.255.9 "sudo incus launch images:debian/12/cloud $CONTAINER && \
|
||||
sudo incus config device add $CONTAINER eth0 nic name=eth0 network=PROD-GBO && \
|
||||
sudo incus exec $CONTAINER -- useradd -m -s /bin/bash gbuser"
|
||||
|
||||
# Push data
|
||||
echo -e "\e[1;33m[6/7] Pushing data...\e[0m"
|
||||
ssh administrator@63.141.255.9 "sudo incus exec $CONTAINER -- mkdir -p /opt/gbo && \
|
||||
sudo incus file push -r ~/gbo/$CONTAINER/* $CONTAINER:/opt/gbo/ && \
|
||||
sudo incus exec $CONTAINER -- chown -R gbuser:gbuser /opt/gbo && \
|
||||
sudo incus exec $CONTAINER -- chmod +x /opt/gbo/bin/*"
|
||||
|
||||
# Start
|
||||
echo -e "\e[1;33m[7/7] Starting container...\e[0m"
|
||||
ssh administrator@63.141.255.9 "sudo incus start $CONTAINER"
|
||||
|
||||
echo -e "\e[1;32m✓ Migration complete! Check with: incus list\e[0m"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Port Forwarding (iptables NAT) - Complete Rules
|
||||
|
||||
```bash
|
||||
# Enable IP forwarding (persistent)
|
||||
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
# Enable route_localnet
|
||||
echo "net.ipv4.conf.all.route_localnet = 1" | sudo tee /etc/sysctl.d/99-localnet.conf
|
||||
sudo sysctl -w net.ipv4.conf.all.route_localnet=1
|
||||
|
||||
# DNS (53)
|
||||
sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination 10.107.115.155:53
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j DNAT --to-destination 10.107.115.155:53
|
||||
sudo iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 10.107.115.155:53
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to-destination 10.107.115.155:53
|
||||
|
||||
# Email (25,465,587,993,995,143,110,4190)
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 10.107.115.200:25
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 465 -j DNAT --to-destination 10.107.115.200:465
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 587 -j DNAT --to-destination 10.107.115.200:587
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 993 -j DNAT --to-destination 10.107.115.200:993
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 995 -j DNAT --to-destination 10.107.115.200:995
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 143 -j DNAT --to-destination 10.107.115.200:143
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 110 -j DNAT --to-destination 10.107.115.200:110
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 4190 -j DNAT --to-destination 10.107.115.200:4190
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 25 -j DNAT --to-destination 10.107.115.200:25
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 465 -j DNAT --to-destination 10.107.115.200:465
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 587 -j DNAT --to-destination 10.107.115.200:587
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 993 -j DNAT --to-destination 10.107.115.200:993
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 995 -j DNAT --to-destination 10.107.115.200:995
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 143 -j DNAT --to-destination 10.107.115.200:143
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 110 -j DNAT --to-destination 10.107.115.200:110
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 4190 -j DNAT --to-destination 10.107.115.200:4190
|
||||
|
||||
# Webmail (5252)
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 5252 -j DNAT --to-destination 10.107.115.208:5252
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 5252 -j DNAT --to-destination 10.107.115.208:5252
|
||||
|
||||
# ALM (4747)
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747
|
||||
|
||||
# Tables/PostgreSQL (5432)
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 5432 -j DNAT --to-destination 10.107.115.33:5432
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 5432 -j DNAT --to-destination 10.107.115.33:5432
|
||||
|
||||
# Proxy/Caddy (80, 443)
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.107.115.236:80
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination 10.107.115.236:443
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 10.107.115.236:80
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 10.107.115.236:443
|
||||
|
||||
# FORWARD rules (allow traffic to containers)
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.155 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p udp -d 10.107.115.155 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.200 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -s 10.107.115.200 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.208 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.4 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.33 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -d 10.107.115.236 -j ACCEPT
|
||||
sudo iptables -A FORWARD -p tcp -s 10.107.115.236 -j ACCEPT
|
||||
|
||||
# POSTROUTING MASQUERADE (return traffic)
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.155 -j MASQUERADE
|
||||
sudo iptables -t nat -A POSTROUTING -p udp -d 10.107.115.155 -j MASQUERADE
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.200 -j MASQUERADE
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.208 -j MASQUERADE
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.4 -j MASQUERADE
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.33 -j MASQUERADE
|
||||
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.236 -j MASQUERADE
|
||||
|
||||
# Save rules
|
||||
sudo sh -c 'iptables-save > /etc/iptables/rules.v4'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benefits of This Approach
|
||||
|
||||
✅ **FASTEST** - No intermediate tarballs, pure streaming
|
||||
✅ **COLORFUL** - pv shows beautiful progress bars
|
||||
✅ **EFFICIENT** - zstd compression (3-5x gzip)
|
||||
✅ **SAFE** - One container at a time, verify before next
|
||||
✅ **CLEAN** - Immediate deletion frees disk space
|
||||
✅ **MONITORED** - Real-time transfer rate and ETA
|
||||
✅ **PARALLEL** - Can migrate multiple containers at once
|
||||
|
||||
**Estimated time:**
|
||||
- Small containers (<1GB): ~5-10 minutes each
|
||||
- Medium containers (1-5GB): ~10-20 minutes each
|
||||
- Large containers (>5GB): ~20-40 minutes each
|
||||
- Total (parallel): ~1-2 hours for all containers
|
||||
|
||||
vs lxc export method: ~4-8 hours total
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 1. Install pv on source
|
||||
ssh root@82.29.59.188 "apt install -y pv zstd"
|
||||
|
||||
# 2. Save migration scripts
|
||||
# Copy migrate-one.sh and MIGRATE-ALL.sh
|
||||
|
||||
# 3. Run single test
|
||||
./migrate-one.sh pragmatismo-alm
|
||||
|
||||
# 4. If successful, run all
|
||||
./MIGRATE-ALL.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### pv not showing progress?
|
||||
```bash
|
||||
# Check pv is installed
|
||||
pv --version
|
||||
|
||||
# Check size detection
|
||||
ssh root@82.29.59.188 "lxc exec pragmatismo-alm -- du -sb /opt/gbo"
|
||||
```
|
||||
|
||||
### Container not starting?
|
||||
```bash
|
||||
# Check logs
|
||||
sudo incus list
|
||||
sudo incus info pragmatismo-alm
|
||||
sudo incus logs pragmatismo-alm --show-log
|
||||
|
||||
# Check network
|
||||
sudo incus exec pragmatismo-alm -- ip addr
|
||||
```
|
||||
|
||||
### Service not running?
|
||||
```bash
|
||||
# Inside container
|
||||
sudo incus exec pragmatismo-alm -- bash
|
||||
systemctl status alm
|
||||
journalctl -u alm -n 50
|
||||
```
|
||||
|
||||
### NAT not working?
|
||||
```bash
|
||||
# Check rules
|
||||
sudo iptables -t nat -L -n -v
|
||||
|
||||
# Check forwarding
|
||||
sudo iptables -L FORWARD -n -v
|
||||
|
||||
# Test from host
|
||||
nc -zv 127.0.0.1 4747
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This plan achieves:
|
||||
✅ **FASTEST possible migration** - streaming, no disk I/O
|
||||
✅ **Colorful progress** - pv with ETA, rate, elapsed
|
||||
✅ **Immediate space cleanup** - delete as we go
|
||||
✅ **Parallel migration** - do multiple containers at once
|
||||
✅ **Clean Incus** - fresh start, no LXC/LXD trash
|
||||
✅ **Proper NAT** - iptables only, no socat/proxy devices
|
||||
|
||||
**No mbuffer needed** - pv does the job with colorful output!
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After each migration:
|
||||
- [ ] Container running: `sudo incus list`
|
||||
- [ ] Service active: `sudo incus exec $c -- systemctl status <service>`
|
||||
- [ ] Data intact: `sudo incus exec $c -- ls -la /opt/gbo`
|
||||
- [ ] Port accessible: `nc -zv 127.0.0.1 <port>`
|
||||
- [ ] Source deleted: `ssh root@82.29.59.188 lxc list` (should not show container)
|
||||
- [ ] NAT rules added: `sudo iptables -t nat -L -n | grep <ip>`
|
||||
|
||||
After all migrations:
|
||||
- [ ] All containers running on Incus
|
||||
- [ ] All services active
|
||||
- [ ] All NAT rules configured
|
||||
- [ ] External access works
|
||||
- [ ] Source server: no containers left
|
||||
|
|
@ -6,6 +6,33 @@ This document describes how to improve `installer.rs` to automate the deployment
|
|||
|
||||
---
|
||||
|
||||
# rust incus install base
|
||||
sudo apt install -y curl gnupg
|
||||
|
||||
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
|
||||
|
||||
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
|
||||
|
||||
|
||||
sudo sh -c 'cat > /etc/apt/sources.list.d/zabbly-incus-stable.sources << EOF
|
||||
Enabled: yes
|
||||
Types: deb
|
||||
URIs: https://pkgs.zabbly.com/incus/stable
|
||||
Suites: bookworm
|
||||
Components: main
|
||||
Architectures: $(dpkg --print-architecture)
|
||||
Signed-By: /etc/apt/keyrings/zabbly.asc
|
||||
EOF'
|
||||
|
||||
|
||||
sudo apt update
|
||||
|
||||
|
||||
sudo apt install incus
|
||||
|
||||
|
||||
## What Was Done Manually (Reference Implementation)
|
||||
|
||||
### Migration Summary (pragmatismo tenant)
|
||||
|
|
@ -449,8 +476,6 @@ mail IN A <HOST_IP>
|
|||
|
||||
---
|
||||
|
||||
## Container Cleanup (BEFORE Setting Up NAT)
|
||||
|
||||
**ALWAYS remove socat and Incus proxy devices before configuring iptables NAT:**
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
# fail2ban startup script for Incus containers
|
||||
# Usage: Place in /opt/gbo/bin/ and run as root in container
|
||||
|
||||
LOGFILE=/opt/gbo/logs/fail2ban.log
|
||||
|
||||
mkdir -p /opt/gbo/logs
|
||||
nohup /usr/bin/fail2ban-server -x -f > $LOGFILE 2>&1 &
|
||||
sleep 2
|
||||
fail2ban-client reload
|
||||
echo "Fail2ban started - check status with: fail2ban-client status"
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# Production Smoke Test Plan
|
||||
|
||||
## Status: Caddy running ✅
|
||||
|
||||
## URLs to Test (YOLO)
|
||||
1. https://webmail.pragmatismo.com.br/
|
||||
2. https://chat.pragmatismo.com.br/cristo
|
||||
3. https://alm.pragmatismo.com.br/
|
||||
4. https://tables.pragmatismo.com.br/
|
||||
|
||||
## Steps
|
||||
1. Navigate to each URL
|
||||
2. Take screenshot
|
||||
3. Check for errors in console/network
|
||||
4. Report pass/fail per URL
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# LXC to Incus Migration Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Export all LXC containers and transfer to destination
|
||||
|
||||
containers="pragmatismo-alm pragmatismo-alm-ci pragmatismo-dns pragmatismo-drive pragmatismo-email pragmatismo-proxy pragmatismo-system pragmatismo-table-editor pragmatismo-tables pragmatismo-webmail"
|
||||
|
||||
for name in $containers; do
|
||||
echo "=== Migrating $name ==="
|
||||
ssh root@pragmatismo.com.br "lxc export $name /tmp/$name.tar.gz"
|
||||
scp root@pragmatismo.com.br:/tmp/$name.tar.gz administrator@63.141.255.9:~/
|
||||
ssh root@pragmatismo.com.br "rm /tmp/$name.tar.gz"
|
||||
echo "✓ $name transferred"
|
||||
done
|
||||
|
||||
echo "Migration complete. All .tar.gz files in ~/ on destination."
|
||||
```
|
||||
244
prompts/win.md
244
prompts/win.md
|
|
@ -1,244 +0,0 @@
|
|||
# General Bots — Plano de Execução no Windows
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
- Windows 10/11 (x64)
|
||||
- Visual Studio Build Tools (com C++ workload) ou Visual Studio
|
||||
- Rust toolchain (`rustup` com `stable-x86_64-pc-windows-msvc`)
|
||||
- Git para Windows
|
||||
|
||||
---
|
||||
|
||||
## 1. Dependências de Build
|
||||
|
||||
### 1.1 PostgreSQL (libpq.lib para Diesel ORM)
|
||||
|
||||
O BotServer usa Diesel com backend Postgres. No Windows, o linker precisa de `libpq.lib`.
|
||||
|
||||
```powershell
|
||||
# Baixar binários do PostgreSQL (não precisa instalar o serviço)
|
||||
Invoke-WebRequest -Uri "https://get.enterprisedb.com/postgresql/postgresql-17.4-1-windows-x64-binaries.zip" -OutFile "$env:TEMP\pgsql.zip" -UseBasicParsing
|
||||
|
||||
# Extrair para C:\pgsql
|
||||
Expand-Archive -Path "$env:TEMP\pgsql.zip" -DestinationPath "C:\pgsql" -Force
|
||||
|
||||
# Configurar variável de ambiente permanente
|
||||
[System.Environment]::SetEnvironmentVariable("PQ_LIB_DIR", "C:\pgsql\pgsql\lib", "User")
|
||||
$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib"
|
||||
$env:PATH = "C:\pgsql\pgsql\bin;$env:PATH"
|
||||
```
|
||||
|
||||
Ou simplesmente execute:
|
||||
```powershell
|
||||
.\DEPENDENCIES.ps1
|
||||
```
|
||||
|
||||
### 1.2 sccache (opcional)
|
||||
|
||||
O `.cargo/config.toml` referencia `sccache`. Se não estiver instalado, comente a linha:
|
||||
```toml
|
||||
# [build]
|
||||
# rustc-wrapper = "sccache"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Compilação
|
||||
|
||||
```powershell
|
||||
# Garantir que PQ_LIB_DIR está configurado
|
||||
$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib"
|
||||
|
||||
# Build do botserver
|
||||
cargo build -p botserver
|
||||
|
||||
# Build do botui (interface desktop Tauri)
|
||||
cargo build -p botui
|
||||
```
|
||||
|
||||
### Correções de compilação aplicadas
|
||||
|
||||
| Arquivo | Problema | Correção |
|
||||
|---------|----------|----------|
|
||||
| `bootstrap.rs` | `use std::os::unix::fs::PermissionsExt` | Envolvido com `#[cfg(unix)]` |
|
||||
| `antivirus.rs` | `error!` macro não importada | Adicionado `use tracing::error` |
|
||||
| `installer.rs` | `check_root()` só existe no Unix | Adicionado `check_admin()` para Windows com `#[cfg(windows)]` |
|
||||
| `installer.rs` | `SUDOERS_CONTENT` não usado no Windows | Envolvido com `#[cfg(not(windows))]` |
|
||||
| `facade.rs` | `make_executable` param `path` não usado | Renomeado para `_path` no Windows |
|
||||
|
||||
---
|
||||
|
||||
## 3. Compatibilidade de Runtime (3rdparty.toml)
|
||||
|
||||
O `3rdparty.toml` agora tem entradas `_windows` para cada componente:
|
||||
|
||||
| Componente | Linux | Windows |
|
||||
|------------|-------|---------|
|
||||
| `drive` | `minio` (linux-amd64) | `minio.exe` (windows-amd64) |
|
||||
| `tables` | `postgresql-*-linux-gnu.tar.gz` | `postgresql-*-windows-x64-binaries.zip` |
|
||||
| `cache` | `valkey-*-jammy-x86_64.tar.gz` | `memurai-developer.msi` |
|
||||
| `llm` | `llama-*-ubuntu-x64.zip` | `llama-*-win-cpu-x64.zip` (ou CUDA/Vulkan) |
|
||||
| `email` | `stalwart-mail-*-linux.tar.gz` | `stalwart-mail-*-windows.zip` |
|
||||
| `proxy` | `caddy_*_linux_amd64.tar.gz` | `caddy_*_windows_amd64.zip` |
|
||||
| `directory` | `zitadel-linux-amd64.tar.gz` | `zitadel-windows-amd64.zip` |
|
||||
| `alm` | `forgejo-*-linux-amd64` | `forgejo-*-windows-amd64.exe` |
|
||||
| `alm_ci` | `forgejo-runner-*-linux-amd64` | `forgejo-runner-*-windows-amd64.exe` |
|
||||
| `dns` | `coredns_*_linux_amd64.tgz` | `coredns_*_windows_amd64.tgz` |
|
||||
| `meet` | `livekit_*_linux_amd64.tar.gz` | `livekit_*_windows_amd64.zip` |
|
||||
| `table_editor` | `nocodb-linux-x64` | `nocodb-win-x64.exe` |
|
||||
| `vector_db` | `qdrant-*-linux-gnu.tar.gz` | `qdrant-*-windows-msvc.zip` |
|
||||
| `timeseries_db` | `influxdb2-*-linux-amd64.tar.gz` | `influxdb2-*-windows-amd64.zip` |
|
||||
| `vault` | `vault_*_linux_amd64.zip` | `vault_*_windows_amd64.zip` |
|
||||
| `observability` | `vector-*-linux-gnu.tar.gz` | `vector-*-windows-msvc.zip` |
|
||||
|
||||
A seleção é automática via `get_component_url()` em `installer.rs` que busca `{name}_windows` primeiro.
|
||||
|
||||
---
|
||||
|
||||
## 4. Adaptações de Código para Windows
|
||||
|
||||
### 4.1 Execução de Comandos Shell
|
||||
|
||||
| Contexto | Linux | Windows |
|
||||
|----------|-------|---------|
|
||||
| `run_commands_with_password()` | `bash -c "{cmd}"` | `powershell -NoProfile -Command "{cmd}"` |
|
||||
| `start()` (spawnar processos) | `sh -c "{exec_cmd}"` | `powershell -NoProfile -Command "{exec_cmd}"` |
|
||||
| `safe_sh_command()` | `sh -c` | `powershell -NoProfile -Command` |
|
||||
|
||||
### 4.2 Gerenciamento de Processos
|
||||
|
||||
| Ação | Linux | Windows |
|
||||
|------|-------|---------|
|
||||
| Matar processos | `pkill -9 -f {name}` | `taskkill /F /IM {name}*` |
|
||||
| Verificar processo | `pgrep -f {name}` | `Get-Process \| Where-Object { $_.ProcessName -like '*{name}*' }` |
|
||||
|
||||
### 4.3 Health Checks (fallback quando curl não está disponível)
|
||||
|
||||
| Check | Linux | Windows |
|
||||
|-------|-------|---------|
|
||||
| Porta aberta | `nc -z -w 1 127.0.0.1 {port}` | `(Test-NetConnection -ComputerName 127.0.0.1 -Port {port}).TcpTestSucceeded` |
|
||||
|
||||
### 4.4 Extração de Arquivos
|
||||
|
||||
| Formato | Linux | Windows |
|
||||
|---------|-------|---------|
|
||||
| `.zip` | `unzip -o -q {file}` | `Expand-Archive -Path '{file}' -DestinationPath '{dest}' -Force` |
|
||||
| `.tar.gz` | `tar -xzf {file}` | `tar -xzf {file}` (Windows 10+ tem tar nativo) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Execução
|
||||
|
||||
### 5.1 Modo CLI (gerenciar componentes)
|
||||
```powershell
|
||||
$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib"
|
||||
$env:PATH = "C:\pgsql\pgsql\bin;$env:PATH"
|
||||
|
||||
# Instalar um componente específico
|
||||
.\target\debug\botserver.exe install vault
|
||||
|
||||
# Listar componentes
|
||||
.\target\debug\botserver.exe list
|
||||
|
||||
# Iniciar componentes
|
||||
.\target\debug\botserver.exe start
|
||||
|
||||
# Ver status
|
||||
.\target\debug\botserver.exe status
|
||||
```
|
||||
|
||||
### 5.2 Modo Servidor (bootstrap completo + HTTP)
|
||||
```powershell
|
||||
$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib"
|
||||
$env:PATH = "C:\pgsql\pgsql\bin;$env:PATH"
|
||||
|
||||
# Executa bootstrap (baixa/instala todos os componentes) + inicia servidor HTTP
|
||||
.\target\debug\botserver.exe
|
||||
```
|
||||
|
||||
O bootstrap automático:
|
||||
1. Baixa e instala Vault, PostgreSQL, Valkey, MinIO, Zitadel, LLM, etc.
|
||||
2. Gera certificados TLS
|
||||
3. Inicializa o banco de dados
|
||||
4. Inicia o servidor HTTP na porta **5858**
|
||||
|
||||
Acesse: **http://localhost:5858**
|
||||
|
||||
### 5.3 Via restart.ps1
|
||||
```powershell
|
||||
.\restart.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Detecção Automática de GPU (LLM)
|
||||
|
||||
O sistema detecta automaticamente:
|
||||
- **CUDA 13.x** → `llama-*-win-cuda-13.1-x64.zip`
|
||||
- **CUDA 12.x** → `llama-*-win-cuda-12.4-x64.zip`
|
||||
- **Vulkan SDK** → `llama-*-win-vulkan-x64.zip`
|
||||
- **CPU only** → `llama-*-win-cpu-x64.zip`
|
||||
- **ARM64** → `llama-*-win-cpu-arm64.zip`
|
||||
|
||||
---
|
||||
|
||||
## 7. Estrutura de Diretórios
|
||||
|
||||
```
|
||||
botserver-stack/
|
||||
├── bin/ # Binários dos componentes
|
||||
│ ├── vault/
|
||||
│ ├── tables/ # PostgreSQL
|
||||
│ ├── cache/ # Valkey/Memurai
|
||||
│ ├── drive/ # MinIO
|
||||
│ ├── directory/ # Zitadel
|
||||
│ ├── llm/ # llama.cpp
|
||||
│ └── ...
|
||||
├── conf/ # Configurações
|
||||
│ ├── vault/
|
||||
│ ├── system/certificates/
|
||||
│ └── ...
|
||||
├── data/ # Dados persistentes
|
||||
│ ├── vault/
|
||||
│ ├── tables/pgdata/
|
||||
│ └── ...
|
||||
└── logs/ # Logs de cada componente
|
||||
├── vault/
|
||||
├── tables/
|
||||
└── ...
|
||||
|
||||
botserver-installers/ # Cache de downloads (reutilizado)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Troubleshooting
|
||||
|
||||
### Erro: `LNK1181: libpq.lib não encontrado`
|
||||
```powershell
|
||||
$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib"
|
||||
# Ou execute .\DEPENDENCIES.ps1
|
||||
```
|
||||
|
||||
### Erro: `sccache não encontrado`
|
||||
Comente no `.cargo/config.toml`:
|
||||
```toml
|
||||
# [build]
|
||||
# rustc-wrapper = "sccache"
|
||||
```
|
||||
|
||||
### Erro: `Path traversal detected`
|
||||
Limpe o cache e recompile:
|
||||
```powershell
|
||||
Remove-Item -Path ".\botserver-stack" -Recurse -Force
|
||||
Remove-Item -Path ".\botserver-installers" -Recurse -Force
|
||||
cargo clean -p botserver
|
||||
cargo build -p botserver
|
||||
```
|
||||
|
||||
### Componentes baixam versão Linux
|
||||
Recompile o botserver para que o `3rdparty.toml` embutido seja atualizado:
|
||||
```powershell
|
||||
cargo clean -p botserver
|
||||
cargo build -p botserver
|
||||
```
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
# BASIC Workflow Engine Plan
|
||||
|
||||
## Current State
|
||||
|
||||
- `workflow_executions` and `workflow_events` tables exist in DB
|
||||
- `WorkflowExecution` / `WorkflowEvent` models exist in `core/shared/models/workflow_models.rs`
|
||||
- `ORCHESTRATE WORKFLOW` keyword exists in `basic/keywords/orchestration.rs` (stub)
|
||||
- `STEP` keyword registered but not durable
|
||||
- Compiler (`basic/mod.rs`) produces Rhai AST and runs it in one shot via `engine.eval_ast_with_scope`
|
||||
- `HEAR` currently blocks a thread (new) — works but not crash-safe
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
BASIC scripts run as **durable step sequences**. Each keyword is a step. On crash/restart, execution resumes from the last completed step. No re-run. No Rhai for control flow.
|
||||
|
||||
```basic
|
||||
' ticket.bas
|
||||
TALK "Describe the issue" ← Step 1
|
||||
HEAR description ← Step 2 (suspends, waits)
|
||||
SET ticket = CREATE(description) ← Step 3
|
||||
TALK "Ticket #{ticket} created" ← Step 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Two Execution Modes
|
||||
|
||||
The compiler serves both modes via a pragma at the top of the `.bas` file:
|
||||
|
||||
```basic
|
||||
' Default: Rhai mode (current behavior, fast, no durability)
|
||||
TALK "Hello"
|
||||
|
||||
' Workflow mode (durable, crash-safe)
|
||||
#workflow
|
||||
TALK "Hello"
|
||||
HEAR name
|
||||
```
|
||||
|
||||
`ScriptService::compile()` detects `#workflow` and returns either:
|
||||
- `ExecutionPlan::Rhai(AST)` — current path, unchanged
|
||||
- `ExecutionPlan::Workflow(Vec<Step>)` — new path
|
||||
|
||||
`ScriptService::run()` dispatches accordingly.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### 1. Compiler changes (`basic/mod.rs`, `basic/compiler/`)
|
||||
|
||||
Add `compile_to_steps(script: &str) -> Result<Vec<Step>>`:
|
||||
|
||||
```rust
|
||||
pub enum Step {
|
||||
Talk { template: String },
|
||||
Hear { variable: String, input_type: String },
|
||||
Set { variable: String, expression: String },
|
||||
If { condition: String, then_steps: Vec<Step>, else_steps: Vec<Step> },
|
||||
Call { function: String, args: Vec<String>, result_var: Option<String> },
|
||||
// ... one variant per keyword
|
||||
}
|
||||
```
|
||||
|
||||
Expressions inside steps (`condition`, `expression`, `template`) are still evaluated by Rhai — but only as **pure expression evaluator**, no custom syntax, no side effects. This keeps Rhai as a math/string engine only.
|
||||
|
||||
### 2. WorkflowEngine (`basic/workflow/engine.rs`)
|
||||
|
||||
```rust
|
||||
pub struct WorkflowEngine {
|
||||
state: Arc<AppState>,
|
||||
session: UserSession,
|
||||
}
|
||||
|
||||
impl WorkflowEngine {
|
||||
/// Start a new workflow or resume existing one for this session+script
|
||||
pub async fn run(&self, script_path: &str, steps: Vec<Step>) -> Result<()>
|
||||
|
||||
/// Execute one step, persist result, return next action
|
||||
async fn execute_step(&self, exec_id: Uuid, step: &Step, vars: &mut Variables) -> StepResult
|
||||
|
||||
/// Load execution state from DB
|
||||
async fn load_state(&self, exec_id: Uuid) -> (usize, Variables)
|
||||
|
||||
/// Persist step completion
|
||||
async fn save_state(&self, exec_id: Uuid, step_index: usize, vars: &Variables, status: &str)
|
||||
}
|
||||
|
||||
pub enum StepResult {
|
||||
Continue, // go to next step
|
||||
Suspend, // HEAR — save state, return, wait for next message
|
||||
Done, // script finished
|
||||
}
|
||||
```
|
||||
|
||||
### 3. HEAR in workflow mode
|
||||
|
||||
No thread blocking. Instead:
|
||||
|
||||
1. `execute_step(Hear)` saves state to `workflow_executions` with `status = "waiting"`, `current_step = N`
|
||||
2. Returns `StepResult::Suspend` → engine returns to caller
|
||||
3. Next user message → `stream_response` checks `workflow_executions` for `session_id` with `status = "waiting"`
|
||||
4. Loads variables, sets `variables["description"] = user_input`, advances `current_step`, resumes
|
||||
|
||||
### 4. `stream_response` dispatch (`core/bot/mod.rs`)
|
||||
|
||||
```rust
|
||||
// At top of stream_response, before LLM:
|
||||
if let Some(exec) = WorkflowEngine::find_waiting(state, session_id).await {
|
||||
WorkflowEngine::resume(state, exec, message_content).await?;
|
||||
return Ok(());
|
||||
}
|
||||
```
|
||||
|
||||
### 5. DB schema (already exists, minor additions)
|
||||
|
||||
```sql
|
||||
-- Already exists:
|
||||
workflow_executions (id, bot_id, workflow_name, current_step, state_json, status, ...)
|
||||
|
||||
-- Add:
|
||||
ALTER TABLE workflow_executions ADD COLUMN session_id UUID;
|
||||
ALTER TABLE workflow_executions ADD COLUMN script_path TEXT;
|
||||
-- state_json stores: { "variables": {...}, "step_index": N }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Phase 1 — Parallel mode (no breaking changes)
|
||||
- Add `compile_to_steps()` alongside existing `compile()`
|
||||
- Add `WorkflowEngine` as new struct
|
||||
- `#workflow` pragma routes to new path
|
||||
- All existing `.bas` files unchanged, run via Rhai as before
|
||||
|
||||
### Phase 2 — Keyword parity
|
||||
Implement step variants for all keywords used in practice:
|
||||
`TALK`, `HEAR`, `SET`, `IF/ELSE/END IF`, `CALL` (HTTP, LLM, tool), `SEND MAIL`, `SCHEDULE`
|
||||
|
||||
### Phase 3 — Default for new scripts
|
||||
New `.bas` files default to workflow mode. Rhai mode kept for backwards compat and tool scripts (short-lived, no HEAR).
|
||||
|
||||
### Phase 4 — Rhai scope reduction
|
||||
Remove Rhai custom syntax registrations. Keep Rhai only as expression evaluator:
|
||||
```rust
|
||||
engine.eval_expression::<Dynamic>(&expr, &scope)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Map
|
||||
|
||||
```
|
||||
basic/
|
||||
mod.rs ← add compile_to_steps(), ExecutionPlan enum
|
||||
compiler/
|
||||
mod.rs ← existing Rhai compiler, unchanged
|
||||
step_compiler.rs ← NEW: BASIC → Vec<Step>
|
||||
workflow/
|
||||
mod.rs ← NEW: WorkflowEngine
|
||||
engine.rs ← NEW: execute_step, load/save state
|
||||
variables.rs ← NEW: Variables (HashMap<String, Dynamic>)
|
||||
steps.rs ← NEW: Step enum
|
||||
keywords/ ← existing, unchanged in Phase 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Keyword Compatibility
|
||||
|
||||
### Category A — Workflow steps (implement as `Step` variants)
|
||||
`TALK`, `HEAR`, `SET`, `IF/ELSE/END IF`, `SEND MAIL`, `SEND TEMPLATE`, `SCHEDULE`,
|
||||
`SAVE`/`INSERT`/`UPDATE`, `GET`, `FIND`, `SEARCH`, `USE KB`, `USE TOOL`, `REMEMBER`,
|
||||
`HTTP GET/POST/PUT/DELETE`, `WAIT`, `TRANSFER TO HUMAN`, `CREATE TASK`, `BOOK`, `SCORE LEAD`
|
||||
|
||||
### Category B — Pure expressions (Rhai as calculator, no step boundary)
|
||||
`math/*`, `datetime/*`, `string_functions`, `arrays/*`, `core_functions`, `validation/*`, `FORMAT`
|
||||
→ Stored as expression strings in Step, evaluated at runtime via `engine.eval_expression_with_scope()`
|
||||
|
||||
### Category C — Rhai-only (scripts using these stay in Rhai mode, no `#workflow`)
|
||||
`code_sandbox`, `use_website`, `face_api`, `on_change`, `on_email`, `webhook`,
|
||||
`procedures` (FUNCTION/SUB/CALL), `for_next` (FOR EACH loops), `switch_case`, `events`, `orchestration`
|
||||
|
||||
A script with any Category C keyword cannot use `#workflow`. The compiler detects this and errors early.
|
||||
|
||||
---
|
||||
|
||||
## How Compilation Works Without Rhai
|
||||
|
||||
Workflow compiler is a **line-by-line parser**, not a Rhai AST walk:
|
||||
|
||||
```
|
||||
Input line → Step variant
|
||||
─────────────────────────────────────────────────────
|
||||
TALK "Hello ${name}" → Step::Talk { template }
|
||||
HEAR description → Step::Hear { var, input_type }
|
||||
SET x = score + 1 → Step::Set { var, expr: "score + 1" }
|
||||
IF score > 10 THEN → Step::If { cond: "score > 10", then_steps, else_steps }
|
||||
SEND MAIL to, s, b → Step::SendMail { to, subject, body }
|
||||
USE TOOL path → Step::UseTool { path, args }
|
||||
```
|
||||
|
||||
Expressions (`score + 1`, `score > 10`) are stored as **raw strings** in the Step struct.
|
||||
At runtime, Rhai evaluates them as pure expressions — no custom syntax, no side effects:
|
||||
|
||||
```rust
|
||||
let mut engine = Engine::new(); // no register_custom_syntax calls
|
||||
let mut scope = Scope::new();
|
||||
for (k, v) in &variables { scope.push_dynamic(k, v.clone()); }
|
||||
let result = engine.eval_expression_with_scope::<Dynamic>(&mut scope, expr)?;
|
||||
```
|
||||
|
||||
Rhai remains a dependency but is used only as a math/string expression evaluator (~5 lines of code at runtime). All custom keyword machinery is bypassed entirely.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
| Engine | Lang | Latency | RAM | Rust SDK | Verdict |
|
||||
|--------|------|---------|-----|----------|---------|
|
||||
| **Custom (this plan)** | Rust | ~1ms | 0 extra | Native | ✅ Best fit |
|
||||
| **[Restate](https://restate.dev)** | Rust server | ~5ms | ~50MB | ✅ official | Fallback option |
|
||||
| **[Rhythm](https://github.com/maxnorth/rhythm)** | Rust | ~2ms | ~10MB | Native | Experimental |
|
||||
| **Temporal** | Go+Java | ~20ms | ~500MB | ❌ | Too heavy |
|
||||
| **Windmill** | Rust+TS | ~10ms | ~200MB | ❌ | Wrong abstraction |
|
||||
|
||||
**Why custom over Restate:** Restate requires its own server as a proxy between HTTP requests and handlers — adds a network hop and an extra process. The custom plan uses PostgreSQL already running in the stack, zero extra infrastructure.
|
||||
|
||||
**Escape hatch:** The `Step` enum in this plan maps 1:1 to Restate workflow steps. If the custom engine proves too complex to maintain, migration to Restate is mechanical — swap `WorkflowEngine::execute_step` internals, keep the compiler and Step enum unchanged.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
- **No re-run ever.** Steps before current_step are skipped on resume.
|
||||
- **Rhai never removed entirely** — used for expression eval only.
|
||||
- **Backwards compatible** — no `#workflow` = Rhai mode, existing bots unaffected.
|
||||
- **HEAR in workflow mode = zero threads held.** State in DB, not RAM.
|
||||
- **Tool scripts** (called by LLM) stay in Rhai mode — they're short-lived, no HEAR needed.
|
||||
23
restart.sh
23
restart.sh
|
|
@ -4,21 +4,38 @@ set -e
|
|||
echo "Stopping..."
|
||||
pkill -f botserver || true
|
||||
pkill -f botui || true
|
||||
pkill -f botmodels || true
|
||||
pkill -f rustc || true
|
||||
|
||||
echo "Cleaning..."
|
||||
rm -f botserver.log botui.log
|
||||
rm -f botserver.log botui.log botmodels.log
|
||||
|
||||
echo "Building..."
|
||||
cargo build -p botserver
|
||||
cargo build -p botui
|
||||
|
||||
echo "Starting botmodels..."
|
||||
cd botmodels
|
||||
source venv/bin/activate
|
||||
uvicorn src.main:app --host 0.0.0.0 --port 8085 > ../botmodels.log 2>&1 &
|
||||
echo " PID: $!"
|
||||
cd ..
|
||||
|
||||
echo "Waiting for botmodels..."
|
||||
for i in $(seq 1 30); do
|
||||
if curl -s http://localhost:8085/api/health > /dev/null 2>&1; then
|
||||
echo " botmodels ready"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Starting botserver..."
|
||||
RUST_LOG=debug ./target/debug/botserver --noconsole > botserver.log 2>&1 &
|
||||
BOTMODELS_HOST="http://localhost:8085" BOTMODELS_API_KEY="starter" RUST_LOG=debug ./target/debug/botserver --noconsole > botserver.log 2>&1 &
|
||||
echo " PID: $!"
|
||||
|
||||
echo "Starting botui..."
|
||||
BOTSERVER_URL="http://localhost:8080" ./target/debug/botui > botui.log 2>&1 &
|
||||
echo " PID: $!"
|
||||
|
||||
echo "Done. Logs: tail -f botserver.log botui.log"
|
||||
echo "Done. Logs: tail -f botserver.log botui.log botmodels.log"
|
||||
|
|
|
|||
154
usekb2.md
Normal file
154
usekb2.md
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# USE KB 2.0: Group-Based Knowledge Base Access
|
||||
|
||||
## Overview
|
||||
Modify the USE KB keyword to respect user group permissions, ensuring that THINK KB queries only return answers from knowledge base folders that belong to groups the logged-in user is a member of.
|
||||
|
||||
## Current Architecture
|
||||
|
||||
### USE KB Flow
|
||||
1. User executes `USE KB "kb_name"` in BASIC script
|
||||
2. `use_kb.rs:add_kb_to_session()` checks if KB exists in `kb_collections`
|
||||
3. Creates default KB entry if not found
|
||||
4. Adds association to `session_kb_associations` table
|
||||
5. KB becomes active for the session
|
||||
|
||||
### THINK KB Flow
|
||||
1. User executes `THINK KB "query"`
|
||||
2. `think_kb.rs:think_kb_search()` gets all active KBs from `session_kb_associations`
|
||||
3. For each active KB, calls `KnowledgeBaseManager.search()` on its Qdrant collection
|
||||
4. Returns combined results from all active KBs
|
||||
|
||||
### Group System
|
||||
- Groups stored in `rbac_groups` table
|
||||
- User membership in `rbac_user_groups` table
|
||||
- Group permissions via `rbac_group_roles` table
|
||||
|
||||
## Proposed Changes
|
||||
|
||||
### 1. Database Schema Changes
|
||||
|
||||
Add new table `kb_group_associations`:
|
||||
|
||||
```sql
|
||||
CREATE TABLE kb_group_associations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
kb_id UUID NOT NULL REFERENCES kb_collections(id) ON DELETE CASCADE,
|
||||
group_id UUID NOT NULL REFERENCES rbac_groups(id) ON DELETE CASCADE,
|
||||
granted_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(kb_id, group_id)
|
||||
);
|
||||
```
|
||||
|
||||
Migration file: `botserver/migrations/6.2.0-01-kb-groups/up.sql`
|
||||
|
||||
### 2. Backend Logic Changes
|
||||
|
||||
#### Modify `think_kb_search()` in `think_kb.rs`
|
||||
- Add user group lookup before searching
|
||||
- Filter active KBs to only those accessible by user's groups
|
||||
- Allow access if KB has no group associations (public KBs) OR user is in associated groups
|
||||
|
||||
```rust
|
||||
async fn think_kb_search(
|
||||
kb_manager: Arc<KnowledgeBaseManager>,
|
||||
db_pool: DbPool,
|
||||
session_id: Uuid,
|
||||
bot_id: Uuid,
|
||||
user_id: Uuid, // Add user_id parameter
|
||||
query: &str,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
// Get user's groups
|
||||
let user_groups = get_user_groups(&db_pool, user_id)?;
|
||||
|
||||
// Get active KBs filtered by groups
|
||||
let accessible_kbs = get_accessible_kbs_for_session(&db_pool, session_id, &user_groups)?;
|
||||
|
||||
// Search only accessible KBs
|
||||
// ... rest of search logic
|
||||
}
|
||||
```
|
||||
|
||||
#### Add `get_accessible_kbs_for_session()` function
|
||||
```rust
|
||||
fn get_accessible_kbs_for_session(
|
||||
conn_pool: &DbPool,
|
||||
session_id: Uuid,
|
||||
user_groups: &[String],
|
||||
) -> Result<Vec<(String, String, String)>, String> {
|
||||
// Query that joins session_kb_associations with kb_group_associations
|
||||
// Returns KBs where group_id IS NULL (public) OR group_id IN user_groups
|
||||
}
|
||||
```
|
||||
|
||||
#### Modify `add_kb_to_session()` in `use_kb.rs`
|
||||
- Add optional group access check
|
||||
- Allow USE KB if user has access to the KB's groups
|
||||
|
||||
### 3. API Changes
|
||||
|
||||
Add new endpoints in `rbac.rs` for KB-group management:
|
||||
|
||||
```rust
|
||||
// Assign KB to group
|
||||
POST /api/rbac/kbs/{kb_id}/groups/{group_id}
|
||||
|
||||
// Remove KB from group
|
||||
DELETE /api/rbac/kbs/{kb_id}/groups/{group_id}
|
||||
|
||||
// Get groups for KB
|
||||
GET /api/rbac/kbs/{kb_id}/groups
|
||||
|
||||
// Get KBs accessible by user
|
||||
GET /api/rbac/users/{user_id}/accessible-kbs
|
||||
```
|
||||
|
||||
### 4. Frontend Changes
|
||||
|
||||
#### Update `botui/ui/suite/admin/groups.html`
|
||||
- Add "Knowledge Bases" tab to group detail panel
|
||||
- Show list of KBs assigned to the group
|
||||
- Allow adding/removing KB assignments
|
||||
|
||||
#### Update `botui/ui/suite/drive/drive.html`
|
||||
- Add group visibility indicators for KB folders
|
||||
- Show which groups have access to each KB
|
||||
|
||||
### 5. Migration Strategy
|
||||
|
||||
1. Create new migration for `kb_group_associations` table
|
||||
2. Run migration to create table
|
||||
3. Assign existing KBs to default groups (e.g., "all_users" group)
|
||||
4. Update application code
|
||||
5. Deploy and test
|
||||
|
||||
### 6. Backward Compatibility
|
||||
|
||||
- Existing KBs without group associations remain public
|
||||
- Existing USE KB calls continue to work
|
||||
- THINK KB will filter results based on new permissions
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. ✅ Database migration for kb_group_associations
|
||||
2. ✅ Modify think_kb_search to accept user_id and filter by groups
|
||||
3. ✅ Update THINK KB keyword registration to pass user_id
|
||||
4. ✅ Add group access check to USE KB
|
||||
5. ✅ Add API endpoints for KB-group management
|
||||
6. ✅ Update admin UI for group-KB assignment
|
||||
7. ✅ Update drive UI to show group access
|
||||
8. ✅ Add tests for group-based access control
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- All KB access checks must happen at the database level
|
||||
- No client-side filtering of search results
|
||||
- Group membership verified on each request
|
||||
- Audit logging for KB access attempts
|
||||
|
||||
## Testing
|
||||
|
||||
- Unit tests for group access functions
|
||||
- Integration tests for THINK KB with group filtering
|
||||
- UI tests for admin group-KB management
|
||||
- End-to-end tests with different user group scenarios
|
||||
Loading…
Add table
Reference in a new issue