package cli import ( "context" "fmt" "github.com/spf13/cobra" "gitea.mynodes.xyz/ilker/llmgw/internal/config" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) // defaultStorePath is where a single-machine deployment keeps its store. const defaultStorePath = "/var/lib/llmgw/llmgw.db" // defaultConfigPath is where the control plane's configuration usually lives. // // Consulted rather than required. The commands that work on the store run on // the same machine as the server far more often than not, and the server // already knows where the store is — asking it is better than making the // operator repeat an answer that is written down three lines away. const defaultConfigPath = "/etc/llmgw/server.yaml" // storeFlags is how a command that works directly on the store finds it. type storeFlags struct { address string config string } // bind adds --store and --config to a command. func (f *storeFlags) bind(cmd *cobra.Command) { cmd.PersistentFlags().StringVar(&f.address, "store", "", "store address: a SQLite path, a postgres:// DSN, or @file holding one "+ "(default: auth.store from --config, else "+defaultStorePath+")") cmd.PersistentFlags().StringVar(&f.config, "config", defaultConfigPath, "server configuration to read the store address from") } // open resolves the store and opens it. // // Explicit --store wins; otherwise the server's own configuration is asked, // and only then does the single-machine default apply. That order exists // because the default was a SQLite path and nothing else: on a cluster whose // store is PostgreSQL, every one of these commands failed with "unable to open // database file" while the right answer sat in the config file beside it. The // operator's conclusion was that the tool was broken, and they were not far off. func (f *storeFlags) open(cmd *cobra.Command) (*store.DB, error) { return f.openCtx(cmd.Context()) } // openCtx is open for a caller that has a context rather than a command. func (f *storeFlags) openCtx(ctx context.Context) (*store.DB, error) { address, from := f.resolve() s, err := store.Open(ctx, address) if err != nil { return nil, fmt.Errorf("opening the store at %s (%s): %w", address, from, err) } return s, nil } // resolve returns the address and where it came from, for the error message. // // Where it came from is half the message. "unable to open database file" says // nothing about which of three sources chose that path, and the whole problem // is that the wrong one did. func (f *storeFlags) resolve() (address, from string) { if f.address != "" { return f.address, "--store" } if f.config != "" { if cfg, err := config.Load(f.config); err == nil && cfg.Auth.Store != "" { return cfg.Auth.Store, "auth.store in " + f.config } } return defaultStorePath, "the default; no --store and nothing usable in " + f.config }