From 3de46894d0e94b10501fe3b00e27bb464280236a Mon Sep 17 00:00:00 2001 From: Jabberwocky238 <7176656@qq.com> Date: Thu, 16 Jul 2026 08:33:38 -0400 Subject: [PATCH] s --- common/config.go | 67 ++++++++++++++++++++++++++++++++++++++++++ gitrpc/gitrpc.proto | 8 ++--- gitrpc/server.go | 19 ++---------- gitrpc/v1/gitrpc.pb.go | 18 ++++++------ state/db.go | 18 +++++++----- state/mut.go | 13 ++++---- 6 files changed, 100 insertions(+), 43 deletions(-) create mode 100644 common/config.go diff --git a/common/config.go b/common/config.go new file mode 100644 index 0000000..40fa15a --- /dev/null +++ b/common/config.go @@ -0,0 +1,67 @@ +package common + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "sync/atomic" +) + +// Config carries the daemon's startup params. The server needs the git root to +// open repos and the rest to answer the Status RPC (which also hands gitctl the +// db_uri so it can connect directly for inspection reads). +type Config struct { + Root string + RPCAddr string + SSHAddr string + ManageAddr string + AuthURL string + DBUri string + SkipAuth bool + + prepared atomic.Bool + driver string + dsn string +} + +func (c *Config) RepoRoot() string { + return filepath.Join(c.Root, "repos") +} + +// Prepare checks that the config is valid and creates the repo root if needed. +func (c *Config) Prepare() error { + var err error + if c.DBUri == "" && c.SkipAuth == false { + return errors.New("simplegit: -db is required unless -skip-auth (e.g. sqlite://state.db)") + } + if err := os.MkdirAll(c.RepoRoot(), 0o755); err != nil { + return fmt.Errorf("simplegit: Failed to create data root: %s (%v)", c.RepoRoot(), err) + } + c.driver, c.dsn, c.DBUri, err = ParseDBURI(c.Root, c.DBUri) + if err != nil { + return errors.New("simplegit: ParseDBURI failed") + } + if c.RPCAddr == "" { + return errors.New("simplegit: -rpc is required (the HTTP server is this binary's only listener)") + } + if c.ManageAddr == "" { + return fmt.Errorf("*manageAddr == nil") + } + c.prepared.Store(true) + return nil +} + +func (c *Config) Driver() string { + if !c.prepared.Load() { + panic("Config.Driver called before Prepare") + } + return c.driver +} + +func (c *Config) DSN() string { + if !c.prepared.Load() { + panic("Config.DSN called before Prepare") + } + return c.dsn +} diff --git a/gitrpc/gitrpc.proto b/gitrpc/gitrpc.proto index c92372d..79606e4 100644 --- a/gitrpc/gitrpc.proto +++ b/gitrpc/gitrpc.proto @@ -277,10 +277,10 @@ message ACLDeleteRequest { // directly for inspection reads; it carries credentials when postgres, so the // manage port must stay localhost-only/trusted. message StatusResponse { - string repo_root = 1; // -root (git repo root) - string rpc_addr = 2; // -rpc listen ("" if n/a) - string ssh_addr = 3; // -ssh listen ("" if disabled) - string manage_addr = 4; // -manage listen ("" if disabled) + string root = 1; // -root (git root) + string rpc_addr = 2; // -rpc listen + string ssh_addr = 3; // -ssh listen + string manage_addr = 4; // -manage listen string auth_url = 5; // -auth RBAC center ("" if skip-auth) string db_uri = 6; // -db state DB URI bool skip_auth = 7; // -skip-auth diff --git a/gitrpc/server.go b/gitrpc/server.go index bf0e893..b46163b 100644 --- a/gitrpc/server.go +++ b/gitrpc/server.go @@ -20,28 +20,15 @@ import ( "simplegit/state" ) -// Config carries the daemon's startup params. The server needs the git root to -// open repos and the rest to answer the Status RPC (which also hands gitctl the -// db_uri so it can connect directly for inspection reads). -type Config struct { - Root string - RPCAddr string - SSHAddr string - ManageAddr string - AuthURL string - DBUri string - SkipAuth bool -} - type Server struct { gitRepoRoot string - cfg Config + cfg *common.Config startedAt time.Time middleware *MiddlewareClient mut state.IStateMut } -func NewServer(cfg Config, middleware *MiddlewareClient, mut state.IStateMut) *Server { +func NewServer(cfg *common.Config, middleware *MiddlewareClient, mut state.IStateMut) *Server { return &Server{ gitRepoRoot: cfg.Root, cfg: cfg, @@ -511,7 +498,7 @@ func (s *Server) ACLDelete(ctx context.Context, req *connect.Request[v1.ACLDelet func (s *Server) Status(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[v1.StatusResponse], error) { return connect.NewResponse(&v1.StatusResponse{ - RepoRoot: s.gitRepoRoot, + Root: s.cfg.Root, RpcAddr: s.cfg.RPCAddr, SshAddr: s.cfg.SSHAddr, ManageAddr: s.cfg.ManageAddr, diff --git a/gitrpc/v1/gitrpc.pb.go b/gitrpc/v1/gitrpc.pb.go index 81c7caa..7f61b6f 100644 --- a/gitrpc/v1/gitrpc.pb.go +++ b/gitrpc/v1/gitrpc.pb.go @@ -1472,10 +1472,10 @@ func (x *ACLDeleteRequest) GetAclId() int64 { // manage port must stay localhost-only/trusted. type StatusResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - RepoRoot string `protobuf:"bytes,1,opt,name=repo_root,json=repoRoot,proto3" json:"repo_root,omitempty"` // -root (git repo root) - RpcAddr string `protobuf:"bytes,2,opt,name=rpc_addr,json=rpcAddr,proto3" json:"rpc_addr,omitempty"` // -rpc listen ("" if n/a) - SshAddr string `protobuf:"bytes,3,opt,name=ssh_addr,json=sshAddr,proto3" json:"ssh_addr,omitempty"` // -ssh listen ("" if disabled) - ManageAddr string `protobuf:"bytes,4,opt,name=manage_addr,json=manageAddr,proto3" json:"manage_addr,omitempty"` // -manage listen ("" if disabled) + Root string `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` // -root (git root) + RpcAddr string `protobuf:"bytes,2,opt,name=rpc_addr,json=rpcAddr,proto3" json:"rpc_addr,omitempty"` // -rpc listen + SshAddr string `protobuf:"bytes,3,opt,name=ssh_addr,json=sshAddr,proto3" json:"ssh_addr,omitempty"` // -ssh listen + ManageAddr string `protobuf:"bytes,4,opt,name=manage_addr,json=manageAddr,proto3" json:"manage_addr,omitempty"` // -manage listen AuthUrl string `protobuf:"bytes,5,opt,name=auth_url,json=authUrl,proto3" json:"auth_url,omitempty"` // -auth RBAC center ("" if skip-auth) DbUri string `protobuf:"bytes,6,opt,name=db_uri,json=dbUri,proto3" json:"db_uri,omitempty"` // -db state DB URI SkipAuth bool `protobuf:"varint,7,opt,name=skip_auth,json=skipAuth,proto3" json:"skip_auth,omitempty"` // -skip-auth @@ -1515,9 +1515,9 @@ func (*StatusResponse) Descriptor() ([]byte, []int) { return file_gitrpc_gitrpc_proto_rawDescGZIP(), []int{23} } -func (x *StatusResponse) GetRepoRoot() string { +func (x *StatusResponse) GetRoot() string { if x != nil { - return x.RepoRoot + return x.Root } return "" } @@ -3512,9 +3512,9 @@ const file_gitrpc_gitrpc_proto_rawDesc = "" + "\x03pat\x18\x02 \x01(\tR\x03pat\x12\x12\n" + "\x04perm\x18\x03 \x01(\tR\x04perm\")\n" + "\x10ACLDeleteRequest\x12\x15\n" + - "\x06acl_id\x18\x01 \x01(\x03R\x05aclId\"\xb5\x02\n" + - "\x0eStatusResponse\x12\x1b\n" + - "\trepo_root\x18\x01 \x01(\tR\brepoRoot\x12\x19\n" + + "\x06acl_id\x18\x01 \x01(\x03R\x05aclId\"\xac\x02\n" + + "\x0eStatusResponse\x12\x12\n" + + "\x04root\x18\x01 \x01(\tR\x04root\x12\x19\n" + "\brpc_addr\x18\x02 \x01(\tR\arpcAddr\x12\x19\n" + "\bssh_addr\x18\x03 \x01(\tR\asshAddr\x12\x1f\n" + "\vmanage_addr\x18\x04 \x01(\tR\n" + diff --git a/state/db.go b/state/db.go index 39e7fad..449e230 100644 --- a/state/db.go +++ b/state/db.go @@ -27,6 +27,7 @@ import ( "encoding/pem" "errors" "fmt" + "log" "os" "path/filepath" "simplegit/common" @@ -76,7 +77,7 @@ var ( type LocalState struct { engine *xorm.Engine - root string + cfg *common.Config // skipAuth bypasses the DB: AccessBy* always allow, RepoPath resolves any // path under root. engine is nil in this mode. skipAuth bool @@ -92,17 +93,19 @@ func init() { sql.Register(sqliteDriver, &sqlite.Driver{}) } -func OpenWithDSN(root string, driver string, dsn string) (*LocalState, error) { - engine, err := InitEngine(driver, dsn) +func Open(cfg *common.Config) (*LocalState, error) { + engine, err := InitEngine(cfg.Driver(), cfg.DSN()) if err != nil { return nil, fmt.Errorf("state: init engine: %w", err) } - if driver == sqliteDriver { + if cfg.Driver() == sqliteDriver { engine.SetMaxOpenConns(1) } + log.Printf("state: RepoRoot: %s", cfg.RepoRoot()) + log.Printf("state: DB engine %s opened, %s", cfg.Driver(), cfg.DSN()) return &LocalState{ engine: engine, - root: root, + cfg: cfg, }, nil } @@ -164,7 +167,7 @@ func (s *LocalState) loadOrCreateHostKey(ty string) (ssh.Signer, error) { default: return nil, errors.New("loadOrCreateHostKey") } - hostKeyPath := filepath.Join(s.root, fmt.Sprintf("host_key_%v.pem", ty)) + hostKeyPath := filepath.Join(s.cfg.Root, fmt.Sprintf("host_key_%v.pem", ty)) if data, err := os.ReadFile(hostKeyPath); err == nil { return ssh.ParsePrivateKey(data) } else if !os.IsNotExist(err) { @@ -214,13 +217,14 @@ func (s *LocalState) findRepo(owner, name string) (*Repo, error) { // repo is registered in the DB -- the DB is the authority, not the filesystem. // In skip-auth mode the DB check is skipped (any path under root resolves). func (s *LocalState) RepoPath(owner, name string) (string, error) { + name = normalizeName(name) rel := relPath(owner, name) if !s.skipAuth { if _, err := s.findRepo(owner, name); err != nil { return "", err } } - return common.Resolve(s.root, rel) + return common.Resolve(s.cfg.RepoRoot(), rel) } // Open returns a gitcmd.Repository for owner/name. The repo must be registered diff --git a/state/mut.go b/state/mut.go index 5e2bddf..08ca3f5 100644 --- a/state/mut.go +++ b/state/mut.go @@ -46,11 +46,11 @@ func (s *LocalState) nsDir(ns string) (string, error) { if ns == "" || strings.Contains(ns, "..") { return "", fmt.Errorf("invalid namespace %q", ns) } - abs, err := filepath.Abs(filepath.Join(s.root, ns)) + abs, err := filepath.Abs(filepath.Join(s.cfg.RepoRoot(), ns)) if err != nil { return "", err } - rootAbs, err := filepath.Abs(s.root) + rootAbs, err := filepath.Abs(s.cfg.RepoRoot()) if err != nil { return "", err } @@ -106,8 +106,7 @@ func (s *LocalState) nextNamespaceID() (int64, error) { // namespace must already exist (have at least one repo) so NamespaceID can be // resolved; NameID is freshly assigned. skip-auth: disk-only (git init, no DB). func (s *LocalState) CreateRepo(ns, name string) error { - name = normalizeName(name) - path, err := common.Resolve(s.root, relPath(ns, name)) + path, err := s.RepoPath(ns, name) if err != nil { return err } @@ -164,7 +163,7 @@ func (s *LocalState) ExistRepo(ns, name string) error { // DeleteRepo removes the repo row, its repo-targeted ACL grants, and the // on-disk bare directory. Hard-deletes. skip-auth: disk-only. func (s *LocalState) DeleteRepo(ns, name string) error { - path, _ := common.Resolve(s.root, relPath(ns, name)) + path, _ := s.RepoPath(ns, name) if s.skipAuth { if path != "" { _ = os.RemoveAll(path) @@ -199,11 +198,11 @@ func (s *LocalState) MoveRepo(old, new string) error { if err != nil { return err } - oldPath, err := common.Resolve(s.root, relPath(oldNs, oldName)) + oldPath, err := s.RepoPath(oldNs, oldName) if err != nil { return err } - newPath, err := common.Resolve(s.root, relPath(newNs, newName)) + newPath, err := s.RepoPath(newNs, newName) if err != nil { return err }