#!/usr/local/cpanel/3rdparty/bin/perl

#                                      Copyright 2026 WebPros International, LLC
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.

package scripts::rebuild_api_readonly_cache;

use strict;
use warnings;

use Getopt::Long qw(:config auto_help);
use Pod::Usage;

use Cpanel::API::ReadOnlyMap::PluginCache ();

=head1 NAME

scripts/rebuild_api_readonly_cache - Rebuild the plugin read-only API overlay cache

=head1 SYNOPSIS

    scripts/rebuild_api_readonly_cache [--quiet]

Regenerates C</var/cpanel/caches/api_readonly_plugins.cache> wholesale from
every installed plugin's C</var/cpanel/plugins/*/api-readonly.uapi.yaml>
manifest. This is the read-only classification overlay that
L<Cpanel::API::ReadOnly> consults (alongside the compiled core map) to enforce
read-only API token scope for plugin UAPI methods.

Invoked from each plugin's install/upgrade (C<pkg.postinst>) and removal
(C<pkg.postrm>) scriptlet, and once per cPanel update via the
C<install/RebuildApiReadonlyCache.pm> task. Safe to run any time (idempotent).
Must run as root.

The per-update task closes a deployment-ordering gap: a plugin installed before
this script existed ran a guarded scriptlet that no-op'd, so its read-only
methods would otherwise stay denied (fail-closed) until a reinstall. The update
task rebuilds from all installed manifests so they register on the next update.

The rebuild is fail-closed: a malformed or unreadable plugin manifest is skipped
with a warning, never aborting the rebuild, and a manifest entry that collides
with a core classification is ignored (core-wins). The overlay only ever adds
read-only allowances; it can never widen write access.

=head1 OPTIONS

=over 4

=item B<--quiet>

Suppress the summary line on success.

=item B<--help>

Show this help and exit.

=back

=cut

__PACKAGE__->script(@ARGV) if !caller;

sub script {
    my ( $pkg, @argv ) = @_;

    local @ARGV = @argv;

    my $quiet = 0;
    GetOptions( 'quiet' => \$quiet ) or pod2usage(2);

    if ( $> != 0 ) {
        die "rebuild_api_readonly_cache must run as root\n";
    }

    my $overlay = Cpanel::API::ReadOnlyMap::PluginCache->rebuild();

    if ( !$quiet ) {
        printf "Wrote %s: %d plugin read-only entries\n",
          $Cpanel::API::ReadOnlyMap::PluginCache::CACHE_PATH,
          scalar keys %$overlay;
    }

    return 0;
}

1;
