Improve handling of non-existing or malformed JSON conf-files

Change-Id: Ia89be3459d60a11302ddd18a0c57a01b38996137
diff --git a/.gitignore b/.gitignore
index 5818b15..dfb29e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,13 @@
 /cache
 /kalamar.secret*
+super_client_info
 /start-test
 /todo.org
 /fixtures.txt
 /sandbox
 /Sandbox
-/node_modules
+node_modules
+/local/
 /cache/data
 /public
 /kalamar.*.conf
diff --git a/Changes b/Changes
index 970fc6d..59f97fc 100644
--- a/Changes
+++ b/Changes
@@ -1,6 +1,8 @@
         - Expose colors and forms to plugins (diewald)
         - Support additional activity-toggle in buttons (diewald)
         - Authorization service with POST (support API version 1.1) (hebasta)
+        - Enhance handling of improper json files (diewald)
+        - Support API version 1.1 (diewald)
 
 0.64 2026-02-14
         - Improve 'Plugins' mounting (diewald)
diff --git a/lib/Kalamar/Plugin/Auth.pm b/lib/Kalamar/Plugin/Auth.pm
index cf06321..a2154c2 100644
--- a/lib/Kalamar/Plugin/Auth.pm
+++ b/lib/Kalamar/Plugin/Auth.pm
@@ -56,9 +56,27 @@
   # Get client_id and client_secret from client file
   if ($param->{client_file} || $main::ENV{KALAMAR_CLIENT_FILE}) {
     $param->{client_file} ||= $main::ENV{KALAMAR_CLIENT_FILE};
-    my $client_json = decode_json(path($param->{client_file})->slurp);
-    $param->{client_id} //= $client_json->{client_id};
-    $param->{client_secret} //= $client_json->{client_secret};
+
+    # Load client file
+    if (-e $param->{client_file}) {
+      my $client_file = path($param->{client_file})->slurp;
+
+      eval {
+        my $client_json = decode_json($client_file);
+
+        if ($client_json) {
+          $param->{client_id} //= $client_json->{client_id};
+          $param->{client_secret} //= $client_json->{client_secret};
+        }
+      };
+
+      if ($@) {
+          $app->log->error('provided client file syntax invalid:' . $param->{client_file} . '; ' . $@);
+      };
+    }
+    else {
+      $app->log->error('provided client file does not exist: ' . $param->{client_file});
+    };
   };
 
   # Get the client id and the client_secret as a requirement
diff --git a/lib/Kalamar/Plugin/Plugins.pm b/lib/Kalamar/Plugin/Plugins.pm
index a2a81d3..17dee1b 100644
--- a/lib/Kalamar/Plugin/Plugins.pm
+++ b/lib/Kalamar/Plugin/Plugins.pm
@@ -21,12 +21,23 @@
   if ($param->{default_file}) {
 
     # Read default plugins file
+    unless (-e $param->{default_file}) {
+      $app->log->error('provided default_plugins file does not exist: ' . $param->{default_file});
+      return;
+    };
+
     my $default = path($param->{default_file});
 
     # Use correct working directory
     $default = $app->home->child($default) unless $default->is_abs;
 
-    @json_array = @{ decode_json $default->slurp };
+    eval {
+      @json_array = @{ decode_json $default->slurp };
+    };
+
+    if ($@) {
+      $app->log->error('provided plugin file syntax invalid:' . $param->{default_file} . '; ' . $@);
+    };
   };
 
   # There are default plugins to be registered
diff --git a/t/plugin/auth-oauth.t b/t/plugin/auth-oauth.t
index 04fcc3c..f83bd2a 100644
--- a/t/plugin/auth-oauth.t
+++ b/t/plugin/auth-oauth.t
@@ -507,14 +507,11 @@
   ->header_is('Pragma','no-cache')
   ;
 
-my $loglines = '';
-$t->app->log->on(
-  message => sub {
-    my ($log, $level, @lines) = @_;
-    if ($level eq 'warn') {
-      $loglines = join ',', @lines;
-    };
-  });
+
+my $log_output = '';
+open my $log_fh, '>', \$log_output or die $!;
+$t->app->log->handle($log_fh);
+$t->app->log->level('warn');
 
 $t->get_ok('/settings/marketplace')
   ->status_is(200)
@@ -523,7 +520,7 @@
   ->element_exists_not('ul.plugin_in-list')
   ;
 
-is($loglines, '', 'Check log is fine');
+is($log_output, '', 'Check log is fine');
 
 $csrf = $t->post_ok('/settings/oauth/register' => form => {
   name => 'MyApp',
@@ -1382,7 +1379,43 @@
   ->element_exists('aside.settings')
   ;
 
+$log_output = '';
 
+# Test non-existing client file (app should initialize without crashing)
+$t = Test::Mojo->new('Kalamar');
+$t->app->log->handle($log_fh);
+$t->app->log->level('error');
+$t->app->plugin('Mount' => {
+  $mount_point => $fixtures_path->child('mock.pl')
+});
+
+$t->app->plugin('Auth' => {
+    client_file => '/nonexistent/client.json',
+    oauth2 => 1
+  }
+);
+ok($t->app, 'App initializes with non-existing client file');
+like($log_output, qr'provided client file does not exist', 'Check log is fine');
+
+$log_output = '';
+
+# Test malformed JSON client file (app should initialize without crashing)
+my $bad_client_file = tempfile();
+$bad_client_file->spew('{invalid json}');
+$t = Test::Mojo->new('Kalamar');
+$t->app->log->handle($log_fh);
+$t->app->log->level('error');
+$t->app->plugin('Mount' => {
+  $mount_point => $fixtures_path->child('mock.pl')
+});
+
+$t->app->plugin('Auth' => {
+    client_file => $bad_client_file->to_string,
+    oauth2 => 1
+  }
+);
+ok($t->app, 'App initializes with malformed JSON client file');
+like($log_output, qr'provided client file syntax invalid', 'Check log is fine');
 done_testing;
 __END__
 
diff --git a/t/plugin/plugins.t b/t/plugin/plugins.t
index 93ebc29..d68730e 100644
--- a/t/plugin/plugins.t
+++ b/t/plugin/plugins.t
@@ -69,4 +69,28 @@
   ->json_is('/1/embed/0/title','Full Text')
   ;
 
+# Test non-existing file
+my $t2 = Test::Mojo->new('Kalamar');
+my $log_output = '';
+open my $log_fh, '>', \$log_output or die $!;
+$t2->app->log->handle($log_fh);
+$t2->app->log->level('error');
+$t2->app->plugin('Plugins' => {
+  default_plugins => '/nonexistent/file.json'
+});
+like($log_output, qr/provided default_plugins file does not exist/, 'Non-existing file logged');
+
+# Test malformed JSON
+$log_output = '';
+my $t3 = Test::Mojo->new('Kalamar');
+my $bad_json = tempfile();
+$bad_json->spew('{invalid json}');
+#$t3->app->log->level('error');
+$t3->app->log->handle($log_fh);
+$t3->app->log->level('error');
+$t3->app->plugin('Plugins' => {
+  default_plugins => $bad_json->to_string
+});
+like($log_output, qr/provided plugin file syntax invalid/, 'Malformed JSON logged');
+
 done_testing;