1
0
mirror of https://github.com/coalaura/whiskr.git synced 2025-09-08 00:29:54 +00:00
Files
whiskr/env.go

153 lines
3.7 KiB
Go
Raw Normal View History

2025-08-05 03:56:23 +02:00
package main
import (
2025-08-16 16:03:36 +02:00
"bytes"
"crypto/rand"
"encoding/base64"
2025-08-05 03:56:23 +02:00
"errors"
2025-08-16 16:03:36 +02:00
"io"
2025-08-05 03:56:23 +02:00
"os"
2025-08-16 15:15:06 +02:00
"github.com/goccy/go-yaml"
2025-08-05 03:56:23 +02:00
)
2025-08-16 15:15:06 +02:00
type EnvTokens struct {
2025-08-16 16:03:36 +02:00
Secret string `json:"secret"`
2025-08-16 15:15:06 +02:00
OpenRouter string `json:"openrouter"`
Exa string `json:"exa"`
2025-08-25 18:37:30 +02:00
GitHub string `json:"github"`
2025-08-16 15:15:06 +02:00
}
2025-08-14 17:08:45 +02:00
2025-08-16 15:15:06 +02:00
type EnvSettings struct {
2025-08-25 22:45:03 +02:00
CleanContent bool `json:"cleanup"`
TitleModel string `json:"title-model"`
2025-08-16 15:15:06 +02:00
}
2025-08-14 17:08:45 +02:00
2025-08-16 16:03:36 +02:00
type EnvUser struct {
Username string `json:"username"`
Password string `json:"password"`
}
type EnvAuthentication struct {
2025-08-16 17:18:48 +02:00
lookup map[string]*EnvUser
Enabled bool `json:"enabled"`
Users []*EnvUser `json:"users"`
2025-08-16 16:03:36 +02:00
}
2025-08-16 15:15:06 +02:00
type Environment struct {
2025-08-16 16:03:36 +02:00
Debug bool `json:"debug"`
Tokens EnvTokens `json:"tokens"`
Settings EnvSettings `json:"settings"`
Authentication EnvAuthentication `json:"authentication"`
2025-08-16 15:15:06 +02:00
}
2025-08-18 05:15:48 +02:00
var env = Environment{
// defaults
Settings: EnvSettings{
2025-08-23 15:19:43 +02:00
CleanContent: true,
2025-08-18 05:15:48 +02:00
},
}
2025-08-05 03:56:23 +02:00
func init() {
2025-08-16 15:15:06 +02:00
file, err := os.OpenFile("config.yml", os.O_RDONLY, 0)
2025-08-29 22:55:41 +02:00
log.MustFail(err)
2025-08-05 03:56:23 +02:00
2025-08-16 15:15:06 +02:00
defer file.Close()
2025-08-14 03:53:14 +02:00
2025-08-16 15:15:06 +02:00
err = yaml.NewDecoder(file).Decode(&env)
2025-08-29 22:55:41 +02:00
log.MustFail(err)
2025-08-14 17:08:45 +02:00
2025-08-29 22:55:41 +02:00
log.MustFail(env.Init())
2025-08-16 15:15:06 +02:00
}
2025-08-14 17:08:45 +02:00
2025-08-16 15:15:06 +02:00
func (e *Environment) Init() error {
// print if debug is enabled
if e.Debug {
2025-08-29 22:55:41 +02:00
log.Warnln("Debug mode enabled")
2025-08-16 15:15:06 +02:00
}
2025-08-14 03:53:14 +02:00
2025-08-16 16:03:36 +02:00
// check if server secret is set
if e.Tokens.Secret == "" {
2025-08-29 22:55:41 +02:00
log.Warnln("Missing tokens.secret, generating new...")
2025-08-16 16:03:36 +02:00
key := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, key)
if err != nil {
return err
}
e.Tokens.Secret = base64.StdEncoding.EncodeToString(key)
err = e.Store()
if err != nil {
return err
}
2025-08-29 22:55:41 +02:00
log.Println("Stored new tokens.secret")
2025-08-16 16:03:36 +02:00
}
2025-08-16 15:15:06 +02:00
// check if openrouter token is set
if e.Tokens.OpenRouter == "" {
return errors.New("missing tokens.openrouter")
2025-08-14 03:53:14 +02:00
}
2025-08-11 01:16:52 +02:00
2025-08-16 15:15:06 +02:00
// check if exa token is set
if e.Tokens.Exa == "" {
2025-08-29 22:55:41 +02:00
log.Warnln("Missing token.exa, web search unavailable")
2025-08-05 03:56:23 +02:00
}
2025-08-11 01:16:52 +02:00
2025-08-25 22:45:03 +02:00
// check if github token is set
if e.Tokens.GitHub == "" {
2025-08-29 22:55:41 +02:00
log.Warnln("Missing token.github, limited api requests")
2025-08-25 22:45:03 +02:00
}
// default title model
if e.Settings.TitleModel == "" {
e.Settings.TitleModel = "google/gemini-2.5-flash-lite"
}
2025-08-16 17:18:48 +02:00
// create user lookup map
e.Authentication.lookup = make(map[string]*EnvUser)
2025-08-16 16:03:36 +02:00
for _, user := range e.Authentication.Users {
2025-08-16 17:18:48 +02:00
e.Authentication.lookup[user.Username] = user
2025-08-16 16:03:36 +02:00
}
2025-08-16 17:18:48 +02:00
return nil
2025-08-16 16:03:36 +02:00
}
func (e *Environment) Store() error {
var (
buffer bytes.Buffer
comments = yaml.CommentMap{
"$.debug": {yaml.HeadComment(" enable verbose logging and diagnostics")},
"$.tokens": {yaml.HeadComment("")},
"$.settings": {yaml.HeadComment("")},
"$.authentication": {yaml.HeadComment("")},
"$.tokens.secret": {yaml.HeadComment(" server secret for signing auth tokens; auto-generated if empty")},
"$.tokens.openrouter": {yaml.HeadComment(" openrouter.ai api token (required)")},
"$.tokens.exa": {yaml.HeadComment(" exa search api token (optional; used by search tools)")},
2025-08-25 18:37:30 +02:00
"$.tokens.github": {yaml.HeadComment(" github api token (optional; used by search tools)")},
2025-08-16 16:03:36 +02:00
2025-08-25 22:45:03 +02:00
"$.settings.cleanup": {yaml.HeadComment(" normalize unicode in assistant output (optional; default: true)")},
"$.settings.title-model": {yaml.HeadComment(" model used to generate titles (needs to have structured output support; default: google/gemini-2.5-flash-lite)")},
2025-08-16 16:03:36 +02:00
"$.authentication.enabled": {yaml.HeadComment(" require login with username and password")},
"$.authentication.users": {yaml.HeadComment(" list of users with bcrypt password hashes")},
}
)
err := yaml.NewEncoder(&buffer, yaml.WithComment(comments)).Encode(e)
if err != nil {
return err
}
body := bytes.ReplaceAll(buffer.Bytes(), []byte("#\n"), []byte("\n"))
return os.WriteFile("config.yml", body, 0644)
}