Siti Plugin Repo

Deze repository bevat een Vite/React frontend en een Express backend die samen een centrale licentie- en pluginbeheeromgeving vormen. Je beheert hier WordPress-plugins, gekoppelde licenties en gebruikers (inclusief admin-rollen), en biedt een HTTP API waar je eigen plugins tegenaan kunnen praten voor licentievalidatie en update-informatie.

Inhoud

  1. Architectuuroverzicht
  2. Installatie & configuratie
  3. Gebruikers & rollen
  4. API-overzicht
  5. Integratie met je WordPress-plugin
  6. Handige tips

Architectuuroverzicht

Component Omschrijving
server/ Express-app die licenties, gebruikers en plugin metadata serveert. Praat met MariaDB en Git providers.
src/ React frontend voor licentiebeheer, inclusief admin flows en in-app authenticatie.
MariaDB Externe database met tabellen users, licenses, license_hostnames. Licentiegegevens staan niet meer in JSON-bestanden.
Git providers Manifest-/release-/commitdata wordt opgehaald bij GitHub of Gitea, afhankelijk van entries in server/repos.json.

Belangrijke eigenschappen:

  • Vanaf de eerste run wordt het schema automatisch gecreëerd in MariaDB.
  • De allereerste gebruiker die zich registreert wordt automatisch admin.
  • Admins kunnen gebruikers aanmaken en licenties namens andere gebruikers genereren.
  • Een licentie hoort bij de eerste hostname die zich meldt; elke volgende hostname wordt geweigerd.

Installatie & configuratie

Vereisten

  • Node.js 18+
  • MariaDB 10.6+ (extern of lokaal)
  • Optioneel Docker (voor containerized deployment)

Stap 1 .env

Gebruik .env.example als startpunt en zet hem om naar .env:

cp .env.example .env

Stel daarna minimaal de volgende variabelen in:

DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=sitiapp
DB_PASSWORD=supergeheim
DB_NAME=siti_plugin_repo
JWT_SECRET=een-lang-en-random-geheim
JWT_EXPIRES_IN=7d

Let op: geef de backend toegang tot een bestaande database. Het schema wordt automatisch ingericht, maar de database zelf moet bestaan.

Stap 2 Dependencies installeren

npm install

Stap 3 Ontwikkelservers starten

# Backend API + licentiebeheer UI
npm run dev:server

# Frontend (Vite dev server)
npm run dev

Vite proxyt /api/* naar localhost:3001, dus beide processen moeten draaien.

Docker Compose

Wil je alles containerizen, gebruik dan docker-compose.yml. De Compose configuratie bevat alleen de applicatiecontainer; MariaDB blijft een externe service. Voorbeeld:

DB_HOST=database.internal
DB_PORT=3306
DB_USER=sitiapp
DB_PASSWORD=my-secret
DB_NAME=siti_plugin_repo
JWT_SECRET=prod-secret
docker compose up --build

Gebruikers & rollen

  • Eerste gebruiker → admin (is_admin = 1).
  • Admins kunnen:
    • Overzicht van alle gebruikers downloaden (GET /api/admin/users).
    • Nieuwe gebruikers aanmaken (POST /api/admin/users).
    • Licenties koppelen aan andere gebruikers door userId mee te geven aan POST /api/licenses.
  • Niet-admins:
    • Kunnen alleen hun eigen licenties zien en aanmaken.
    • Kunnen geen andere gebruikers zien of licenties voor anderen beheren.

Authenticatie:

  • JWTs worden uitgegeven door /api/auth/login of /api/auth/register.
  • Het frontend bewaart de token in localStorage en stuurt Authorization: Bearer ….
  • requireAdmin middleware blokkeert niet-admin gebruikers automatisch voor admin-routes.

API-overzicht

Methode & pad Rol Beschrijving
POST /api/auth/register Publiek Maakt gebruiker aan (eerste gebruiker wordt admin).
POST /api/auth/login Publiek Verstrekt JWT.
GET /api/auth/me Auth Retourneert huidige gebruiker.
GET /api/admin/users Admin Geeft lijst met alle gebruikers.
POST /api/admin/users Admin Maakt gebruiker aan (met optionele admin-flag).
GET /api/plugins Publiek Lijst met plugins uit server/repos.json.
GET /api/plugins/:owner/:repo Publiek Details van één plugin.
GET /api/licenses Auth Licenties van ingelogde gebruiker. Admins kunnen ?userId=… meegeven.
POST /api/licenses Auth Licentie aanmaken. Admins: userId om een andere eigenaar te kiezen.
POST /api/licenses/verify Publiek Valideert licentiekey + hostname en geeft huidige versie terug.
POST /api/licenses/download Publiek (met geldige licentie) Verifieert licentie + hostname en streamt een ZIP (versie of latest).

POST /api/licenses/verify is het belangrijkste eindpunt voor WordPress-plugins. Request body:

{
  "key": "SITI-XXXX-XXXX",
  "hostname": "example.com"
}

Response (succes):

{
  "valid": true,
  "hostname": "example.com",
  "boundNow": false,
  "license": {
    "pluginName": "Mijn Plugin",
    "pluginVersion": "1.2.3",
    "primaryHostname": "example.com",
    "key": "SITI-XXXX-XXXX",
    "hostnames": [
      { "hostname": "example.com", "hits": 12, "lastSeenAt": "2026-02-01T11:22:33.000Z" }
    ]
  }
}

Als de hostname nog niet eerder gekoppeld was, wordt boundNow: true. Als een andere site al gekoppeld is, krijg je HTTP 403 met de melding Licentie hoort bij ….

Integratie met je WordPress-plugin

1. Bewaar licentie en API-basis-URL

Maak in je plugin een settingspagina waar de klant:

  • De licentiecode invult (bijv. SITI-ABCD-1234).
  • De URL van je licentie-API configureert (https://repo.jouwdomein.nl).

Sla dit op in wp_options, bijvoorbeeld:

update_option( 'siti_license_key', sanitize_text_field( $_POST['siti_license_key'] ) );
update_option( 'siti_license_api', esc_url_raw( $_POST['siti_license_api'] ) );

2. License check helper

function siti_verify_license() {
    $license = get_option( 'siti_license_key' );
    $api     = untrailingslashit( get_option( 'siti_license_api' ) );

    if ( empty( $license ) || empty( $api ) ) {
        return new WP_Error( 'missing', 'Geen licentie ingesteld.' );
    }

    $hostname = parse_url( home_url(), PHP_URL_HOST );
    $response = wp_remote_post( "{$api}/api/licenses/verify", array(
        'headers' => array( 'Content-Type' => 'application/json' ),
        'body'    => wp_json_encode( array(
            'key'      => $license,
            'hostname' => $hostname,
        ) ),
        'timeout' => 15,
    ) );

    if ( is_wp_error( $response ) ) {
        return $response;
    }

    $code = wp_remote_retrieve_response_code( $response );
    $body = json_decode( wp_remote_retrieve_body( $response ), true );

    if ( 200 !== $code || empty( $body['valid'] ) ) {
        return new WP_Error( 'invalid', $body['error'] ?? 'Licentie ongeldig.' );
    }

    update_option( 'siti_license_last_check', current_time( 'mysql' ) );
    update_option( 'siti_license_version', $body['license']['pluginVersion'] ?? null );

    return $body['license'];
}

Gebruik deze helper in:

  • admin_init (om bij het openen van de plugin-instellingen te valideren).
  • Een WP-Cron job (bijv. dagelijks) om de licentie actief te houden.

3. Updates / downloads doorgeven aan WordPress

Omdat je backend bij verify de pluginVersion retourneert, kun je dat gebruiken om WP te vertellen dat er een update is. Een simpele aanpak:

add_filter( 'pre_set_site_transient_update_plugins', function ( $transient ) {
    $license = siti_verify_license();
    if ( is_wp_error( $license ) ) {
        return $transient;
    }

    $current_version = '1.2.0'; // huidige pluginversie
    $remote_version  = $license['pluginVersion'];

    if ( version_compare( $remote_version, $current_version, '>' ) ) {
        $transient->response['jouw-plugin/jouw-plugin.php'] = (object) array(
            'slug'        => 'jouw-plugin',
            'new_version' => $remote_version,
            'package'     => 'https://jouw-download-url/zip', // eigen distributie
            'tested'      => get_bloginfo( 'version' ),
            'url'         => 'https://jouwwebsite.nl/plugin',
        );
    }

    return $transient;
});

De API levert geen ZIP-bestand; je blijft zelf verantwoordelijk voor het aanbieden van downloads (bijv. via GitHub releases of Gitea). Gebruik package hierboven om naar de daadwerkelijke download te verwijzen.

Download endpoint gebruiken

Wanneer je liever via jouw eigen endpoint distribueert, roep dan POST /api/licenses/download aan vanaf je plugin of update-server. Body:

{
  "key": "SITI-XXXX-XXXX",
  "hostname": "example.com",
  "version": "v1.2.3" // of "latest"
}

Bij succes streamt de API direct een ZIP-bestand. Tip:

  1. Laat WordPress jouw eigen mini-proxy aanspreken (omdat WP_UPgrader meestal een URL verwacht). Je proxy kan de POST doen, headers controleren en de stream doorgeven.
  2. Of gebruik download_url() in WordPress met wp_remote_post en sla de response body tijdelijk op in upload-dir.
  3. Versie latest pakt de nieuwste release (via GitHub/Gitea). Specifieke tags vallen terug op refs/tags/{tag}. Als een tag ontbreekt, wordt de branch/commitnaam gebruikt.

4. Fouten tonen

Laat admins weten wanneer een licentie ongeldig is:

add_action( 'admin_notices', function () {
    $license = siti_verify_license();
    if ( is_wp_error( $license ) ) {
        printf(
            '<div class="notice notice-error"><p>%s</p></div>',
            esc_html( $license->get_error_message() )
        );
    }
} );

Handige tips

  • Houd server/repos.json up-to-date met alle Git repositories waarvan je manifest wilt tonen.
  • Wanneer je licenties migreert vanuit server/licenses.json, schrijf een kleine scriptje dat records in de nieuwe MariaDB-tabellen importeert.
  • Gebruik HTTPS op productie; licentiekeys en hostnames gaan over het netwerk.
  • Voor staging of development kun je JWT_SECRET kort houden, maar in productie moet het lang en random zijn.
  • Log server-output (server.log) in een centralized logging stack om snel te zien wanneer licenties worden geweigerd.

Veel succes met het integreren! Mocht je verdere automatisering willen (bijvoorbeeld automatische updateserver voor ZIPs), breid dit project dan uit met een downloadendpoint dat controleert of een licentie geldig is voordat hij een ZIP serveert.

Description
No description provided
Readme 702 KiB
Languages
JavaScript 92.3%
CSS 7.2%
Dockerfile 0.3%
HTML 0.2%