@@ -64,62 +64,56 @@ type ActiveTransfer struct {
6464 Speed float64 `json:"speed"`
6565}
6666
67- // NewApp creates a new application instance
68- func NewApp () * App {
69- logger , _ := zap .NewProduction ()
67+ // NewApp creates a new application instance using the provided configuration.
68+ // cfg must not be nil; callers should load it via config.Load or config.LoadDefaults.
69+ // Returns an error if logger creation or directory setup fails.
70+ func NewApp (cfg * config.Config ) (* App , error ) {
71+ if cfg == nil {
72+ return nil , errors .New ("config must not be nil" )
73+ }
74+
75+ // Build logger from configuration.
76+ var zapLogger * zap.Logger
77+ var err error
78+ if cfg .IsDevelopment () {
79+ zapLogger , err = zap .NewDevelopment ()
80+ } else {
81+ zapLogger , err = zap .NewProduction ()
82+ }
83+ if err != nil {
84+ return nil , fmt .Errorf ("creating logger: %w" , err )
85+ }
86+
7087 ctx , cancel := context .WithCancel (context .Background ())
7188
72- // Create data directories
89+ // Create data directories needed by the embedded Postgres runtime and scripts.
7390 dirs := []string {
7491 "./data" ,
7592 "./data/postgres" ,
7693 "./data/scripts" ,
7794 "./data/keys" ,
7895 }
79-
8096 for _ , dir := range dirs {
8197 if err := os .MkdirAll (dir , 0755 ); err != nil {
82- logger . Fatal ( "Failed to create directory" ,
83- zap . String ( "dir" , dir ),
84- zap . Error ( err ) )
98+ cancel ()
99+ _ = zapLogger . Sync ()
100+ return nil , fmt . Errorf ( "creating directory %q: %w" , dir , err )
85101 }
86102 }
87103
88- // Create default configuration
89- cfg := & config.Config {
90- Database : config.DatabaseConfig {
91- Type : "postgres" ,
92- Port : 5433 ,
93- MaxConnections : 10 ,
94- MinConnections : 2 ,
95- SSLMode : "disable" ,
96- },
97- P2P : config.P2PConfig {
98- Port : 4001 ,
99- MaxPeers : 50 ,
100- MinPeers : 5 ,
101- PeerTimeout : 30 * time .Second ,
102- BootstrapPeers : []string {},
103- },
104- Scripts : config.ScriptConfig {
105- ScriptDir : "./data/scripts" ,
106- PythonPath : "python3" ,
107- MaxExecTime : 5 * time .Minute ,
108- MaxMemoryMB : 512 ,
109- AllowedPkgs : []string {"pandas" , "numpy" , "requests" },
110- },
111- Security : config.SecurityConfig {
112- KeyFile : "./data/keys/host.key" ,
113- },
104+ // Ensure the script directory points at the embedded data layout when the
105+ // config is still using the generic default value.
106+ if cfg .Scripts .ScriptDir == "scripts" {
107+ cfg .Scripts .ScriptDir = "./data/scripts"
114108 }
115109
116110 return & App {
117111 ctx : ctx ,
118112 cancel : cancel ,
119- logger : logger ,
113+ logger : zapLogger ,
120114 config : cfg ,
121115 cleanup : make ([]func () error , 0 ),
122- }
116+ }, nil
123117}
124118
125119func (a * App ) startup (ctx context.Context ) {
@@ -134,39 +128,62 @@ func (a *App) startup(ctx context.Context) {
134128
135129 // Initialize embedded database
136130 if err := a .initEmbeddedDB (); err != nil {
137- a .logger .Fatal ("Failed to initialize embedded database" , zap .Error (err ))
131+ a .logger .Error ("Failed to initialize embedded database" , zap .Error (err ))
138132 return
139133 }
140134
141135 // Initialize database connection
142136 if err := a .initDatabase (ctx ); err != nil {
143- a .logger .Fatal ("Failed to initialize database" , zap .Error (err ))
137+ a .logger .Error ("Failed to initialize database" , zap .Error (err ))
138+ a .cleanupEmbeddedDB ()
144139 return
145140 }
146141
147142 // Initialize database schema for a fresh embedded database instance
148143 if err := data .NewSchemaManager (a .conn ).InitializeSchema (ctx ); err != nil {
149- a .logger .Fatal ("Failed to initialize database schema" , zap .Error (err ))
144+ a .logger .Error ("Failed to initialize database schema" , zap .Error (err ))
145+ a .cleanupDBResources (ctx )
150146 return
151147 }
152148
153149 // Initialize repository
154150 a .repo , err = data .NewPostgresRepository (ctx , a .conn , a .logger )
155151 if err != nil {
156- a .logger .Fatal ("Failed to create repository" , zap .Error (err ))
152+ a .logger .Error ("Failed to create repository" , zap .Error (err ))
153+ a .cleanupDBResources (ctx )
157154 return
158155 }
159156
160157 // Initialize and start services
161158 if err := a .initServices (ctx ); err != nil {
162- a .logger .Fatal ("Failed to initialize services" , zap .Error (err ))
159+ a .logger .Error ("Failed to initialize services" , zap .Error (err ))
160+ a .cleanupDBResources (ctx )
163161 return
164162 }
165163
166164 a .running = true
167165 a .logger .Info ("Application started successfully" )
168166}
169167
168+ // cleanupEmbeddedDB stops the embedded Postgres instance if it was started.
169+ func (a * App ) cleanupEmbeddedDB () {
170+ if a .embedded != nil {
171+ if err := a .embedded .Stop (); err != nil {
172+ a .logger .Error ("Failed to stop embedded database during cleanup" , zap .Error (err ))
173+ }
174+ }
175+ }
176+
177+ // cleanupDBResources closes the database connection and stops the embedded
178+ // Postgres instance. Called on partial startup failure.
179+ func (a * App ) cleanupDBResources (ctx context.Context ) {
180+ if a .conn != nil {
181+ a .conn .Close (ctx )
182+ a .conn = nil
183+ }
184+ a .cleanupEmbeddedDB ()
185+ }
186+
170187func (a * App ) initEmbeddedDB () error {
171188 pg := postgres .NewDatabase (
172189 postgres .DefaultConfig ().
0 commit comments