66 "grout/cfw"
77 "grout/cfw/allium"
88 "grout/cfw/muos"
9+ "grout/cfw/onion"
910 "grout/internal"
1011 "grout/internal/environment"
1112 "grout/internal/fileutil"
@@ -46,6 +47,8 @@ func setup() SetupResult {
4647 mappingBytes , mappingErr = muos .GetInputMappingBytes ()
4748 case cfw .Allium :
4849 mappingBytes , mappingErr = allium .GetInputMappingBytes ()
50+ case cfw .Onion :
51+ mappingBytes , mappingErr = onion .GetInputMappingBytes ()
4952 }
5053 if mappingBytes != nil && mappingErr == nil {
5154 gaba .SetInputMappingBytes (mappingBytes )
@@ -117,8 +120,8 @@ func setup() SetupResult {
117120 if config == nil {
118121 config = & internal.Config {
119122 ShowRegularCollections : true ,
120- ApiTimeout : 30 * time .Minute ,
121- DownloadTimeout : 60 * time .Minute ,
123+ ApiTimeout : internal . DurationSeconds ( 30 * time .Second ) ,
124+ DownloadTimeout : internal . DurationSeconds ( 60 * time .Minute ) ,
122125 }
123126 }
124127 config .Language = selectedLanguage
@@ -130,6 +133,7 @@ func setup() SetupResult {
130133 loginConfig , loginErr := ui .LoginFlow (romm.Host {})
131134 if loginErr != nil {
132135 logger .Error ("Login flow failed" , "error" , loginErr )
136+ gaba .Close ()
133137 log .SetOutput (os .Stderr )
134138 log .Fatalf ("Login failed: %v" , loginErr )
135139 }
@@ -176,7 +180,7 @@ func setup() SetupResult {
176180 screen := ui .NewPlatformMappingScreen ()
177181 result , err := screen .Draw (ui.PlatformMappingInput {
178182 Host : config .Hosts [0 ],
179- ApiTimeout : config .ApiTimeout ,
183+ ApiTimeout : config .ApiTimeout . Duration () ,
180184 CFW : currentCFW ,
181185 RomDirectory : cfw .GetRomDirectory (),
182186 AutoSelect : false ,
@@ -198,32 +202,111 @@ func setup() SetupResult {
198202 }
199203
200204 var platforms []romm.Platform
201- var loadErr error
202205
203206 splashBytes , _ := resources .GetSplashImageBytes ()
204207
205208 for {
209+ var connErr error
210+ var authErr error
211+ var loadErr error
212+
206213 gaba .ProcessMessage ("" , gaba.ProcessMessageOptions {
207214 ImageBytes : splashBytes ,
208215 ImageWidth : 768 ,
209216 ImageHeight : 540 ,
210217 }, func () (interface {}, error ) {
211- // Load platform bindings from RomM server (non-fatal if it fails)
212- if err := config .LoadPlatformsBinding (config .Hosts [0 ], config .ApiTimeout ); err != nil {
218+ host := config .Hosts [0 ]
219+
220+ // Step 1: Validate server connectivity
221+ client := romm .NewClient (host .URL (), romm .WithInsecureSkipVerify (host .InsecureSkipVerify ), romm .WithTimeout (internal .ValidationTimeout ))
222+ if err := client .ValidateConnection (); err != nil {
223+ connErr = err
224+ return nil , nil
225+ }
226+
227+ // Step 2: Validate credentials/token
228+ authClient := romm .NewClientFromHost (host , internal .LoginTimeout )
229+ if host .HasTokenAuth () {
230+ if err := authClient .ValidateToken (); err != nil {
231+ authErr = err
232+ return nil , nil
233+ }
234+ // Re-fetch username if missing
235+ if host .Username == "" {
236+ if user , err := authClient .GetCurrentUser (); err == nil {
237+ host .Username = user .Username
238+ config .Hosts [0 ] = host
239+ internal .SaveConfig (config )
240+ }
241+ }
242+ } else {
243+ if err := authClient .Login (host .Username , host .Password ); err != nil {
244+ authErr = err
245+ return nil , nil
246+ }
247+ }
248+
249+ // Step 3: Load platforms
250+ if err := config .LoadPlatformsBinding (config .Hosts [0 ], config .ApiTimeout .Duration ()); err != nil {
213251 logger .Debug ("Failed to load platform bindings" , "error" , err )
214252 }
215253
216254 var err error
217- platforms , err = internal .GetMappedPlatforms (config .Hosts [0 ], config .DirectoryMappings , config .ApiTimeout )
255+ platforms , err = internal .GetMappedPlatforms (config .Hosts [0 ], config .DirectoryMappings , config .ApiTimeout . Duration () )
218256 if err != nil {
219257 loadErr = err
220- return nil , err
258+ return nil , nil
221259 }
222- loadErr = nil
223260 platforms = internal .SortPlatformsByOrder (platforms , config .PlatformOrder )
224261 return nil , nil
225262 })
226263
264+ // Handle connectivity failure
265+ if connErr != nil {
266+ logger .Warn ("Server connectivity failed" , "error" , connErr )
267+ errorMessage := classifyStartupError (connErr )
268+ errorMsg := i18n .Localize (errorMessage , nil )
269+ retry := showStartupError (errorMsg )
270+ if ! retry {
271+ gaba .Close ()
272+ os .Exit (1 )
273+ }
274+ continue
275+ }
276+
277+ // Handle auth failure — show message and offer re-login
278+ if authErr != nil {
279+ logger .Warn ("Auth validation failed" , "error" , authErr )
280+
281+ var msg string
282+ if config .Hosts [0 ].HasTokenAuth () {
283+ msg = i18n .Localize (& goi18n.Message {ID : "startup_error_token_invalid" , Other : "Your API token is invalid or expired.\n Please set up a new one." }, nil )
284+ } else {
285+ msg = i18n .Localize (& goi18n.Message {ID : "startup_error_credentials_invalid" , Other : "Your credentials are invalid.\n Please log in again." }, nil )
286+ }
287+
288+ gaba .ConfirmationMessage (msg , []gaba.FooterHelpItem {
289+ {ButtonName : "A" , HelpText : i18n .Localize (& goi18n.Message {ID : "button_continue" , Other : "Continue" }, nil )},
290+ }, gaba.MessageOptions {})
291+
292+ loginConfig , loginErr := ui .LoginFlow (config .Hosts [0 ])
293+ if loginErr != nil {
294+ logger .Error ("Re-login failed" , "error" , loginErr )
295+ gaba .Close ()
296+ log .SetOutput (os .Stderr )
297+ log .Fatalf ("Login failed: %v" , loginErr )
298+ }
299+ config .Hosts = loginConfig .Hosts
300+ config .PlatformsBinding = loginConfig .PlatformsBinding
301+ internal .SaveConfig (config )
302+
303+ // Re-initialize cache manager with new credentials
304+ if err := cache .InitCacheManager (config .Hosts [0 ], config ); err != nil {
305+ logger .Error ("Failed to re-initialize cache manager" , "error" , err )
306+ }
307+ continue
308+ }
309+
227310 if loadErr == nil {
228311 break
229312 }
0 commit comments