need to refresh on go modules. implemented rudimentary config and cli arguments.
This commit is contained in:
@@ -0,0 +1,230 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
//"context"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
//"sync"
|
||||||
|
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
//"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
//"os"
|
||||||
|
//"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Dynamically get key? Need to research best way to store private keys between two devices.
|
||||||
|
var key = []byte("passphrasewhichneedstobe32bytes!")
|
||||||
|
|
||||||
|
func createToken(username string) (string, error) {
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||||
|
jwt.MapClaims{
|
||||||
|
"username": username,
|
||||||
|
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||||
|
})
|
||||||
|
|
||||||
|
jwtToken, err := token.SignedString(key)
|
||||||
|
if err != nil {
|
||||||
|
return "Error creating JWT.", err
|
||||||
|
}
|
||||||
|
return jwtToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyToken(tokenString string) error {
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return key, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid {
|
||||||
|
return fmt.Errorf("invalid token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(text string) string {
|
||||||
|
hasher := sha1.New()
|
||||||
|
hasher.Write([]byte(text))
|
||||||
|
return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt() {
|
||||||
|
var newCommunication Communication
|
||||||
|
|
||||||
|
text := []byte(newCommunication.Communication)
|
||||||
|
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return
|
||||||
|
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||||
|
}
|
||||||
|
|
||||||
|
var b []byte = gcm.Seal(nonce, nonce, text, nil)
|
||||||
|
hex, err := convertBytesToHex(b)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
var test string = "{data: " + hex + ", hash: " + hash(string(text)) + "}"
|
||||||
|
log.Println("encrypted string:" + test)
|
||||||
|
//gc.IndentedJSON(http.StatusCreated, gin.H{"message": test})
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertBytesToHex(b []byte) (string, error) {
|
||||||
|
// Handle nil pointer case
|
||||||
|
if b == nil {
|
||||||
|
return "", errors.New("nil pointer provided for hex string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the hex string by spaces
|
||||||
|
var h string = hex.EncodeToString(b)
|
||||||
|
var builder strings.Builder
|
||||||
|
for i := 0; i < len(h); i += 2 {
|
||||||
|
end := i + 2
|
||||||
|
if end > len(h) {
|
||||||
|
end = len(h)
|
||||||
|
}
|
||||||
|
chunk := h[i:end]
|
||||||
|
builder.WriteString(chunk)
|
||||||
|
if end < len(h) {
|
||||||
|
builder.WriteString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the slice of uint8 and any errors encountered
|
||||||
|
return builder.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertHexToBytes(hexString *string) ([]uint8, error) {
|
||||||
|
// Handle nil pointer case
|
||||||
|
if hexString == nil {
|
||||||
|
return nil, errors.New("nil pointer provided for hex string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the hex string by spaces
|
||||||
|
hexBytes := strings.Fields(*hexString)
|
||||||
|
|
||||||
|
// Initialize an empty slice for uint8
|
||||||
|
data := make([]uint8, len(hexBytes))
|
||||||
|
|
||||||
|
// Iterate and convert each hex byte
|
||||||
|
for i, hexByte := range hexBytes {
|
||||||
|
// Convert each hex string to a uint8 value (handling errors)
|
||||||
|
value, err := strconv.ParseUint(hexByte, 16, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing hex byte '%s': %w", hexByte, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the converted value to the slice
|
||||||
|
data[i] = uint8(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the slice of uint8 and any errors encountered
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decrypt(input *Communication, output *Chat) bool {
|
||||||
|
// TODO: Add testing flag for easier manipulation.
|
||||||
|
//ciphertext, err := ioutil.ReadFile("myfile")
|
||||||
|
ciphertext, err := convertHexToBytes(&input.Communication)
|
||||||
|
|
||||||
|
// if our program was unable to read the file
|
||||||
|
// print out the reason why it can't
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
if len(ciphertext) < nonceSize {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||||
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
s := string(plaintext)
|
||||||
|
if validateHash(s, input.Hash) {
|
||||||
|
output.Content = s
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
input.Communication = "DATA CORRUPTED OR TAMPERED"
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate there's no tampering with SHA-1 sum. The decrypted hash and the transmitted hash should be identical.
|
||||||
|
func validateHash(decrypted string, hash string) bool {
|
||||||
|
// Calculate the SHA256 sum of the decrypted request
|
||||||
|
hasher := sha1.New()
|
||||||
|
hasher.Write([]byte(decrypted))
|
||||||
|
decryptedHashString := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||||
|
|
||||||
|
// TODO: Add error handling if hash doesn't match.
|
||||||
|
return decryptedHashString == hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the authentication and encryption layer to a provided local port.
|
||||||
|
// Authentication takes place solely on the backend.
|
||||||
|
func ServeAuthentication(
|
||||||
|
router *mux.Router,
|
||||||
|
port int,
|
||||||
|
) {
|
||||||
|
// TODO: Add error handling
|
||||||
|
authR := router.Host("http://localhost").Subrouter()
|
||||||
|
authSrv := &http.Server{
|
||||||
|
Addr: "0.0.0.0:" + string(port),
|
||||||
|
WriteTimeout: time.Second * 15,
|
||||||
|
ReadTimeout: time.Second * 15,
|
||||||
|
IdleTimeout: time.Second * 60,
|
||||||
|
Handler: authR,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := authSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Println("Auth server is running on port " + string(port))
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
@@ -20,21 +21,35 @@ require (
|
|||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.3.0 // indirect
|
github.com/gorilla/sessions v1.3.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.19.0 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
github.com/quaxlyqueen/services v1.0.0
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
@@ -37,6 +39,8 @@ github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFz
|
|||||||
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
@@ -45,8 +49,12 @@ github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
|
|||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -57,6 +65,22 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quaxlyqueen/services v1.0.0 h1:k3VaNSHydxetGVGkv8zKV8H/FlVtLtcDCiENyIAYvjo=
|
github.com/quaxlyqueen/services v1.0.0 h1:k3VaNSHydxetGVGkv8zKV8H/FlVtLtcDCiENyIAYvjo=
|
||||||
github.com/quaxlyqueen/services v1.0.0/go.mod h1:mTP/H7h+u+H/NzcNh3igIP/hkLA4ETYRU9dh9pUfClM=
|
github.com/quaxlyqueen/services v1.0.0/go.mod h1:mTP/H7h+u+H/NzcNh3igIP/hkLA4ETYRU9dh9pUfClM=
|
||||||
|
github.com/quaxlyqueen/services v1.0.1 h1:k6d7j7AKh0QxbzRNFlT0N7ebByTQzYPCGQinvJHH76k=
|
||||||
|
github.com/quaxlyqueen/services v1.0.1/go.mod h1:DONdzmMLG0uArv3wBqyMRnJpC5/Kbt7jIXsck1ZhYxE=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@@ -68,15 +92,23 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -88,6 +120,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -16,33 +16,98 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/quaxlyqueen/services"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebpagesStructure struct {
|
var CONFIG_FILE string
|
||||||
Domain string `mapstructure:"domain"`
|
|
||||||
WebpageDir string `mapstructure:"webpage_dir"`
|
|
||||||
WebpagePort string `mapstructure:"webpage_port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigStructure struct {
|
|
||||||
TextModel string `mapstructure:"text_model"`
|
|
||||||
ImageModel string `mapstructure:"image_model"`
|
|
||||||
VideoModel string `mapstructure:"video_model"`
|
|
||||||
DocModel string `mapstructure:"doc_model"`
|
|
||||||
ResponseStream string `mapstructure:"response_stream"`
|
|
||||||
|
|
||||||
Domain string `mapstructure:"domain"`
|
|
||||||
RouterPort string `mapstructure:"router_port"`
|
|
||||||
API string `mapstructure:"api"`
|
|
||||||
APIPort string `mapstructure:"api_port"`
|
|
||||||
Webpages WebpagesStructure `mapstructure:"webpages"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var CONFIG_DIR string
|
|
||||||
var config ConfigStructure
|
var config ConfigStructure
|
||||||
var chats []services.Chat
|
var chats []Chat
|
||||||
|
|
||||||
|
// Interfaces with the internal API to encrypt a message and return it in a
|
||||||
|
// Communication struct (see types.go). This should be called after a
|
||||||
|
// response has been received from the internal API for the AI, but before
|
||||||
|
// sending a HTTP response to the client.
|
||||||
|
func encryptMessage(response string) Communication {
|
||||||
|
encryptedMessage := Communication{}
|
||||||
|
msgToEncrypt := Communication{}
|
||||||
|
msgToEncrypt.Communication = response
|
||||||
|
msgToEncrypt.Hash = ""
|
||||||
|
j, err := json.Marshal(msgToEncrypt)
|
||||||
|
if err != nil {
|
||||||
|
msgToEncrypt.Communication = ""
|
||||||
|
return msgToEncrypt
|
||||||
|
}
|
||||||
|
buf := strings.NewReader(string(j))
|
||||||
|
|
||||||
|
resp, err := http.Post("http://localhost:1113/encrypt", "application/json", buf)
|
||||||
|
if err != nil {
|
||||||
|
encryptedMessage.Communication = "Error connecting to internal auth API."
|
||||||
|
return encryptedMessage
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Decode the JSON response into a temporary struct
|
||||||
|
var responseData struct {
|
||||||
|
Communication string `json:"communication"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil {
|
||||||
|
encryptedMessage.Communication = "Error decoding JSON response from internal auth API."
|
||||||
|
return encryptedMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the desired fields and assign them to Communication struct
|
||||||
|
encryptedMessage.Communication = responseData.Communication
|
||||||
|
encryptedMessage.Hash = responseData.Hash
|
||||||
|
|
||||||
|
return encryptedMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interfaces with the internal API to decrypt a message and return the prompt
|
||||||
|
// as a string.
|
||||||
|
// TODO: Allow for additional fields for the client, ie. streaming, model, etc.
|
||||||
|
func decryptMessage(comm Communication) Communication {
|
||||||
|
j, err := json.Marshal(comm)
|
||||||
|
if err != nil {
|
||||||
|
return Communication{}
|
||||||
|
}
|
||||||
|
buf := strings.NewReader(string(j))
|
||||||
|
|
||||||
|
resp, err := http.Post("http://localhost:1113/decrypt", "application/json", buf)
|
||||||
|
if err != nil {
|
||||||
|
return Communication{}
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Decode the JSON response into a temporary struct
|
||||||
|
var responseData struct {
|
||||||
|
Filetype string `json:"filetype"`
|
||||||
|
Communication string `json:"communication"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil {
|
||||||
|
return Communication{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendError(w http.ResponseWriter, msg string) {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendResponse(w http.ResponseWriter, encryptedMessage Communication) {
|
||||||
|
encryptedResponse, err := json.Marshal(encryptedMessage)
|
||||||
|
if err != nil {
|
||||||
|
sendError(w, "Error marshalling JSON of encrypted message.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, string(encryptedResponse))
|
||||||
|
}
|
||||||
|
|
||||||
// User HTTP GET request has the prompt decrypted and verified with a SHA-1 checksum.
|
// User HTTP GET request has the prompt decrypted and verified with a SHA-1 checksum.
|
||||||
// If there's been no data corruption, re-construct user prompt for ollama
|
// If there's been no data corruption, re-construct user prompt for ollama
|
||||||
@@ -56,61 +121,86 @@ func chat(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
prompt := services.Communication{}
|
prompt := Communication{}
|
||||||
json.Unmarshal(input, &prompt)
|
json.Unmarshal(input, &prompt)
|
||||||
|
prompt = decryptMessage(prompt)
|
||||||
|
|
||||||
newChat := services.Chat{}
|
// TODO: Add support for dynamically changing models.
|
||||||
|
switch prompt.Filetype {
|
||||||
|
case "txt":
|
||||||
|
model = "qwen:0.5b"
|
||||||
|
case "img":
|
||||||
|
model = "llava"
|
||||||
|
case "doc":
|
||||||
|
model = "omniparser"
|
||||||
|
}
|
||||||
|
|
||||||
|
newChat := Chat{}
|
||||||
newChat.Role = true
|
newChat.Role = true
|
||||||
newChat.Content = prompt.Communication
|
newChat.Content = prompt.Communication
|
||||||
|
|
||||||
if services.decrypt(&prompt, &newChat) {
|
if len(chats) == 0 {
|
||||||
|
chats = []Chat{newChat}
|
||||||
|
} else {
|
||||||
chats = append(chats, newChat)
|
chats = append(chats, newChat)
|
||||||
|
}
|
||||||
|
|
||||||
apiCall := services.PromptWHistory{}
|
apiCall := PromptWHistory{}
|
||||||
apiCall.Model = model
|
apiCall.Model = model
|
||||||
apiCall.Messages = chats
|
apiCall.Messages = chats
|
||||||
apiCall.Stream = viper.Get("response_stream")
|
|
||||||
log.Print("Pre-JSON ification: ")
|
// TODO: Allow for this as a user setting...
|
||||||
log.Println(apiCall)
|
apiCall.Stream = false
|
||||||
|
|
||||||
j, err := json.Marshal(apiCall)
|
j, err := json.Marshal(apiCall)
|
||||||
buf := strings.NewReader(string(j))
|
buf := strings.NewReader(string(j))
|
||||||
log.Print("Post-JSON ification: ")
|
|
||||||
log.Println(string(j))
|
|
||||||
|
|
||||||
// Port 11434 corresponds to Ollama. Direct access to Ollama is restricted.
|
// Port 11434 corresponds to Ollama. Direct access to Ollama is restricted.
|
||||||
resp, err := http.Post("http://localhost:11434/api/chat", "application/json", buf)
|
resp, err := http.Post("http://localhost:11434/api/chat", "application/json", buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sendError(w, "Error connecting to local process.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := io.ReadAll(resp.Body)
|
output, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sendError(w, "Error reading local process communication.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
response := services.ResponseWHistory{}
|
|
||||||
json.Unmarshal(output, &response)
|
|
||||||
log.Println(response)
|
|
||||||
|
|
||||||
} else {
|
response := ResponseWHistory{}
|
||||||
return
|
json.Unmarshal(output, &response)
|
||||||
}
|
sendResponse(w, encryptMessage(response.Message.Content))
|
||||||
}
|
}
|
||||||
|
|
||||||
func generate(w http.ResponseWriter, r *http.Request) {
|
func generate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
model := "qwen:0.5b" // qwen:0.5b used for testing while hosting from my laptop. llama3 seems to be the best to use normally.
|
||||||
input, err := io.ReadAll(r.Body)
|
input, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sendError(w, "Error reading request.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
prompt := services.Communication{}
|
prompt := Communication{}
|
||||||
json.Unmarshal(input, &prompt)
|
err = json.Unmarshal(input, &prompt)
|
||||||
|
if err != nil {
|
||||||
|
sendError(w, "Error unmarshaling JSON.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt = decryptMessage(prompt)
|
||||||
|
|
||||||
// TODO: Add support for dynamically changing models.
|
// TODO: Add support for dynamically changing models.
|
||||||
model := "qwen:0.5b" // qwen:0.5b used for testing while hosting from my laptop. llama3 seems to be the best to use normally.
|
switch prompt.Filetype {
|
||||||
|
case "txt":
|
||||||
|
model = "qwen:0.5b"
|
||||||
|
case "img":
|
||||||
|
model = "llava"
|
||||||
|
case "doc":
|
||||||
|
model = "omniparser"
|
||||||
|
}
|
||||||
|
|
||||||
apicall := "{\"model\": \""
|
apicall := "{\"model\": \""
|
||||||
apicall = apicall + model
|
apicall = apicall + model
|
||||||
@@ -120,25 +210,26 @@ func generate(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
buf := strings.NewReader(apicall)
|
buf := strings.NewReader(apicall)
|
||||||
|
|
||||||
// Port 11434 corresponds to Ollama. Direct access to Ollama is restricted.
|
|
||||||
resp, err := http.Post("http://localhost:11434/api/generate", "application/json", buf)
|
resp, err := http.Post("http://localhost:11434/api/generate", "application/json", buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sendError(w, "Error connecting to local process.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := io.ReadAll(resp.Body)
|
output, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sendError(w, "Error reading local process communication.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
defer r.Body.Close()
|
response := Response{}
|
||||||
response := services.Response{}
|
err = json.Unmarshal(output, &response)
|
||||||
json.Unmarshal(output, &response)
|
if err != nil {
|
||||||
|
sendError(w, "Error unmarshaling local process JSON.")
|
||||||
fmt.Println(response.Response) // Print the body as a string
|
return
|
||||||
// TODO: Encrypt response.Response and send back as a Communication JSON object.
|
}
|
||||||
//c.IndentedJSON(http.StatusCreated, resp)
|
sendResponse(w, encryptMessage(response.Response))
|
||||||
}
|
}
|
||||||
|
|
||||||
func serve() {
|
func serve() {
|
||||||
@@ -156,9 +247,12 @@ func serve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
services.servePage(r, viper.Get("webpage_dir"), "/", viper.Get("webpage_port"))
|
//for _, webpage := range config.Webpages {
|
||||||
services.serveApi(r, viper.Get("api"), endpoint, function, viper.Get("api_port"))
|
//}
|
||||||
addr := "0.0.0.0:", viper.Get("router_port")
|
ServePage(r, config.WebpageDir, "/", config.WebpagePort)
|
||||||
|
ServeApi(r, config.API, endpoint, function, config.APIPort)
|
||||||
|
ServeAuthentication(r, config.AuthPort)
|
||||||
|
addr := "0.0.0.0:" + string(config.RouterPort)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
@@ -178,7 +272,7 @@ func serve() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Println("Router is running on port ", viper.Get("router_port"))
|
log.Println("Router is running on port ", config.RouterPort)
|
||||||
|
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+Shift+C)
|
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+Shift+C)
|
||||||
@@ -205,30 +299,26 @@ func parseCLI() {
|
|||||||
// Define CLI option, shorthand, default value, and description
|
// Define CLI option, shorthand, default value, and description
|
||||||
// TODO: Dynamically obtain default config location from environment variables.
|
// TODO: Dynamically obtain default config location from environment variables.
|
||||||
pflag.StringP("config", "c", "/home/violet/.config/one-ai/default.json", "Configuration file used in initializing One AI.")
|
pflag.StringP("config", "c", "/home/violet/.config/one-ai/default.json", "Configuration file used in initializing One AI.")
|
||||||
|
//pflag.StringP("help", "h", "false", "Display information about using the One AI CLI.")
|
||||||
|
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
viper.BindPFlags(pflag.CommandLine)
|
viper.BindPFlags(pflag.CommandLine)
|
||||||
|
|
||||||
// Retrieve CLI argument, either the default value or the user provided value.
|
// Retrieve CLI argument, either the default value or the user provided value.
|
||||||
CONFIG_DIR = viper.GetString("config")
|
CONFIG_FILE = viper.GetString("config")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConfig() {
|
func parseConfig() {
|
||||||
viper.SetConfigType("json")
|
viper.SetConfigType("json")
|
||||||
viper.SetConfigFile(CONFIG)
|
viper.SetConfigFile(CONFIG_FILE)
|
||||||
viper.ReadInConfig()
|
viper.ReadInConfig()
|
||||||
|
|
||||||
err := viper.Unmarshal(&config)
|
// TODO: Handle error
|
||||||
if err != nil {
|
viper.Unmarshal(&config)
|
||||||
fmt.Println("error unmarshalling config file")
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
fmt.Println(config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
parseCLI()
|
parseCLI()
|
||||||
parseConfig()
|
parseConfig()
|
||||||
//serve()
|
serve()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Serve an API using mux.Router().Host({domain}).Subrouter().
|
||||||
|
// Provide the router, domain, an array of endpoints and functions, and
|
||||||
|
// the port you would like the API accessible to.
|
||||||
|
func ServeApi(
|
||||||
|
router *mux.Router,
|
||||||
|
domain string,
|
||||||
|
endpoint []string,
|
||||||
|
function []func(http.ResponseWriter, *http.Request),
|
||||||
|
port int,
|
||||||
|
) {
|
||||||
|
// TODO: Add error handling
|
||||||
|
apiR := router.Host(domain).Subrouter()
|
||||||
|
|
||||||
|
for i := 0; i < len(endpoint); i++ {
|
||||||
|
apiR.HandleFunc(endpoint[i], function[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
apiSrv := &http.Server{
|
||||||
|
Addr: "0.0.0.0:" + string(port),
|
||||||
|
WriteTimeout: time.Second * 15,
|
||||||
|
ReadTimeout: time.Second * 15,
|
||||||
|
IdleTimeout: time.Second * 60,
|
||||||
|
Handler: apiR,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := apiSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Println("API server is running on port " + string(port))
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Serve a directory to a port.
|
||||||
|
func ServePage(router *mux.Router, dest string, path string, port int) {
|
||||||
|
router.PathPrefix(path).Handler(http.FileServer(http.Dir(dest)))
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: "0.0.0.0: " + string(port),
|
||||||
|
// Good practice to set timeouts to avoid Slowloris attacks.
|
||||||
|
WriteTimeout: time.Second * 15,
|
||||||
|
ReadTimeout: time.Second * 15,
|
||||||
|
IdleTimeout: time.Second * 60,
|
||||||
|
Handler: router, // Pass our instance of gorilla/mux in.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run our server in a goroutine so that it doesn't block.
|
||||||
|
go func() {
|
||||||
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Println("Web server is running on port " + string(port))
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type Communication struct {
|
||||||
|
Filetype string `json:"filetype"`
|
||||||
|
Communication string `json:"communication"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
Response string `json:"response"`
|
||||||
|
Done bool `json:"done"`
|
||||||
|
DoneReason string `json:"done_reason"`
|
||||||
|
Context []int `json:"context"`
|
||||||
|
TotalDuration int `json:"total_duration"`
|
||||||
|
LoadDuration int `json:"load_duration"`
|
||||||
|
PromptEC int `json:"prompt_eval_count"`
|
||||||
|
PromptED int `json:"prompt_eval_duration"`
|
||||||
|
EvalCount int `json:"eval_count"`
|
||||||
|
EvalDuration int `json:"eval_duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chats are updated once the content is decrypted. Since this is never leaving
|
||||||
|
// the server once decrypted, and possibly will not be saved (depending on user
|
||||||
|
// settings), it will remain decrypted until it's time to transform into a
|
||||||
|
// Communication JSON object, which is the HTTP response that is re-encrypted.
|
||||||
|
|
||||||
|
// TODO: If the user has enabled conversation history, then save the encrypted
|
||||||
|
// chats to the server's drive. Additionally, only accept incoming additional
|
||||||
|
// messages from the user, rather than having the client re-send Chats already
|
||||||
|
// stored on the server.
|
||||||
|
type Chat struct {
|
||||||
|
Role bool `json:"role"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PromptWHistory struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Messages []Chat `json:"messages"`
|
||||||
|
Stream bool `json:"stream"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseWHistory struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
Message Chat `json:"message"`
|
||||||
|
Done bool `json:"done"`
|
||||||
|
DoneReason string `json:"done_reason"`
|
||||||
|
Context []int `json:"context"`
|
||||||
|
TotalDuration int `json:"total_duration"`
|
||||||
|
LoadDuration int `json:"load_duration"`
|
||||||
|
PromptEC int `json:"prompt_eval_count"`
|
||||||
|
PromptED int `json:"prompt_eval_duration"`
|
||||||
|
EvalCount int `json:"eval_count"`
|
||||||
|
EvalDuration int `json:"eval_duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebpagesStructure struct {
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
WebpageDir string `mapstructure:"webpage_dir"`
|
||||||
|
WebpagePort int `mapstructure:"webpage_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigStructure struct {
|
||||||
|
TextModel string `mapstructure:"text_model"`
|
||||||
|
ImageModel string `mapstructure:"image_model"`
|
||||||
|
VideoModel string `mapstructure:"video_model"`
|
||||||
|
DocModel string `mapstructure:"doc_model"`
|
||||||
|
ResponseStream bool `mapstructure:"response_stream"`
|
||||||
|
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
RouterPort int `mapstructure:"router_port"`
|
||||||
|
API string `mapstructure:"api"`
|
||||||
|
APIPort int `mapstructure:"api_port"`
|
||||||
|
AuthPort int `mapstructure:"auth_port"`
|
||||||
|
//Webpages WebpagesStructure `mapstructure:"webpages"`
|
||||||
|
WebpageDir string `mapstructure:"webpage_dir"`
|
||||||
|
WebpagePort int `mapstructure:"webpage_port"`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user