/** * RemoteBrain - rbrain * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Paolo Lulli * @copyright Paolo Lulli 2026 */ package tlsclient import ( "bytes" "crypto/tls" "database/sql" "encoding/json" "fmt" "net/http" "os" "path" "rbrain/internal/config" "rbrain/internal/ollama" "rbrain/internal/queries" "rbrain/internal/service" ) type RBrain struct { Config config.RemoteChatConfiguration } func (c *RBrain) secureHttpClient(key string, cert string) (client *http.Client) { x509cert, err := tls.LoadX509KeyPair(cert, key) if err != nil { fmt.Printf("Error loading X509 certificate: %s and/or key: %s", cert, key) panic(err.Error()) } certs := []tls.Certificate{x509cert} if len(certs) == 0 { client = &http.Client{} return } insecureSkipVerify := false if c.Config.TlsVerifyServer == "false" { insecureSkipVerify = true } tr := &http.Transport{ TLSClientConfig: &tls.Config{Certificates: certs, InsecureSkipVerify: insecureSkipVerify}, } client = &http.Client{Transport: tr} return } func (c *RBrain) ApiPost(endpoint string, body string) string { var client *http.Client if c.Config.Insecure == "true" { client = &http.Client{} } else { cert := path.Join(c.Config.TlsCertificate) certKey := path.Join(c.Config.TlsKeyFile) client = c.secureHttpClient(certKey, cert) } req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer([]byte(body))) req.Header.Set("Content-Type", "application/json; charset=UTF-8") if err != nil { fmt.Println("Unable to make POST request", err) os.Exit(1) } req.Header.Add("Accept", "* / *") resp, err := client.Do(req) if err != nil { fmt.Println(err) os.Exit(1) } var r ollama.Response if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { return "" } defer resp.Body.Close() return r.Response } func (c *RBrain) CallOllama(prompt, model string) (string, error) { payload, _ := json.Marshal(ollama.Request{ Model: model, Prompt: prompt, Stream: false, }) resp := c.ApiPost(c.Config.Endpoint, bytes.NewBuffer(payload).String()) return resp, nil } // processMessage saves the request, calls Ollama, and saves the response — // all in a separate goroutine. It sends a single result on ch when done. func (c *RBrain) ProcessMessage(db *sql.DB, message, model string, ch chan<- service.Result) { go func() { requestID, err := queries.SaveRequest(db, message) if err != nil { ch <- service.Result{Err: err} return } response, err := c.CallOllama(message, model) if err != nil { ch <- service.Result{Err: err} return } if err := queries.SaveResponse(db, requestID, response); err != nil { ch <- service.Result{Err: err} return } ch <- service.Result{Text: response} }() }