Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee88ebecf2 | |||
| 20d8d8d4b4 |
6
db/db.go
6
db/db.go
@@ -59,6 +59,12 @@ func Open(path string) (*sql.DB, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQLite supports concurrent readers but only one writer. With WAL mode,
|
||||||
|
// reads don't block writes, but multiple Go connections competing for
|
||||||
|
// the write lock causes SQLITE_BUSY under concurrent load. Limit to one
|
||||||
|
// connection to serialize all access and eliminate busy errors.
|
||||||
|
database.SetMaxOpenConns(1)
|
||||||
|
|
||||||
// Ensure permissions are correct even if the file already existed.
|
// Ensure permissions are correct even if the file already existed.
|
||||||
if err := os.Chmod(path, 0600); err != nil {
|
if err := os.Chmod(path, 0600); err != nil {
|
||||||
_ = database.Close()
|
_ = database.Close()
|
||||||
|
|||||||
@@ -48,21 +48,45 @@ type Server struct {
|
|||||||
listener net.Listener
|
listener net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options configures optional behavior for the gRPC server.
|
||||||
|
type Options struct {
|
||||||
|
// PreInterceptors run before the logging and auth interceptors.
|
||||||
|
// Use for lifecycle gates like seal checks that should reject
|
||||||
|
// requests before any auth validation occurs.
|
||||||
|
PreInterceptors []grpc.UnaryServerInterceptor
|
||||||
|
|
||||||
|
// PostInterceptors run after auth but before the handler.
|
||||||
|
// Use for audit logging, rate limiting, or other cross-cutting
|
||||||
|
// concerns that need access to the authenticated identity.
|
||||||
|
PostInterceptors []grpc.UnaryServerInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a gRPC server with TLS (if certFile and keyFile are
|
// New creates a gRPC server with TLS (if certFile and keyFile are
|
||||||
// non-empty) and an interceptor chain: logging → auth → handler.
|
// non-empty) and an interceptor chain:
|
||||||
|
//
|
||||||
|
// [pre-interceptors] → logging → auth → [post-interceptors] → handler
|
||||||
//
|
//
|
||||||
// The auth interceptor uses methods to determine the access level for
|
// The auth interceptor uses methods to determine the access level for
|
||||||
// each RPC. Methods not in any map are denied by default.
|
// each RPC. Methods not in any map are denied by default.
|
||||||
//
|
//
|
||||||
// If certFile and keyFile are empty, TLS is skipped (for testing).
|
// If certFile and keyFile are empty, TLS is skipped (for testing).
|
||||||
func New(certFile, keyFile string, authenticator *auth.Authenticator, methods MethodMap, logger *slog.Logger) (*Server, error) {
|
// opts is optional; pass nil for the default chain (logging + auth only).
|
||||||
chain := grpc.ChainUnaryInterceptor(
|
func New(certFile, keyFile string, authenticator *auth.Authenticator, methods MethodMap, logger *slog.Logger, opts *Options) (*Server, error) {
|
||||||
|
var interceptors []grpc.UnaryServerInterceptor
|
||||||
|
if opts != nil {
|
||||||
|
interceptors = append(interceptors, opts.PreInterceptors...)
|
||||||
|
}
|
||||||
|
interceptors = append(interceptors,
|
||||||
loggingInterceptor(logger),
|
loggingInterceptor(logger),
|
||||||
authInterceptor(authenticator, methods),
|
authInterceptor(authenticator, methods),
|
||||||
)
|
)
|
||||||
|
if opts != nil {
|
||||||
|
interceptors = append(interceptors, opts.PostInterceptors...)
|
||||||
|
}
|
||||||
|
chain := grpc.ChainUnaryInterceptor(interceptors...)
|
||||||
|
|
||||||
var opts []grpc.ServerOption
|
var serverOpts []grpc.ServerOption
|
||||||
opts = append(opts, chain)
|
serverOpts = append(serverOpts, chain)
|
||||||
|
|
||||||
if certFile != "" && keyFile != "" {
|
if certFile != "" && keyFile != "" {
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
@@ -73,11 +97,11 @@ func New(certFile, keyFile string, authenticator *auth.Authenticator, methods Me
|
|||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
MinVersion: tls.VersionTLS13,
|
MinVersion: tls.VersionTLS13,
|
||||||
}
|
}
|
||||||
opts = append(opts, grpc.Creds(credentials.NewTLS(tlsCfg)))
|
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsCfg)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
GRPCServer: grpc.NewServer(opts...),
|
GRPCServer: grpc.NewServer(serverOpts...),
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ func TestNewWithoutTLS(t *testing.T) {
|
|||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
a := testAuth(t, srv.URL)
|
a := testAuth(t, srv.URL)
|
||||||
|
|
||||||
s, err := New("", "", a, testMethods, slog.Default())
|
s, err := New("", "", a, testMethods, slog.Default(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("New: %v", err)
|
t.Fatalf("New: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user