PHP Classes

File: public_html/webdoors/gemini-capsule/api.php

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   public_html/webdoors/gemini-capsule/api.php   Download  
File: public_html/webdoors/gemini-capsule/api.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 8,528 bytes
 

Contents

Class file image Download
<?php

/**
 * Gemini Capsule WebDoor ? API
 *
 * Handles CRUD for the current user's Gemini capsule files.
 *
 * Actions (GET ?action=<name>):
 * file_list ? list user's files with publish status
 * file_load ? GET ?filename= ? load a file's content
 * capsule_url ? return the user's public capsule URL
 *
 * Actions (POST ?action=<name>, JSON body):
 * file_save ? {filename, content} ? upsert a file
 * file_delete ? {filename} ? delete a file
 * file_publish ? {filename, is_published} ? toggle published flag
 */

require_once __DIR__ . '/../_doorsdk/php/helpers.php';

use
BinktermPHP\Config;

header('Content-Type: application/json; charset=utf-8');

// ?? Auth ???????????????????????????????????????????????????????????????????????
$user = WebDoorSDK\requireAuth();

// ?? Door enabled check ?????????????????????????????????????????????????????????
if (!WebDoorSDK\isDoorEnabled('gemini-capsule')) {
   
WebDoorSDK\jsonError('Gemini Capsule is not enabled', 403);
}

$userId = (int)($user['user_id'] ?? $user['id'] ?? 0);
$action = $_GET['action'] ?? '';

// ?? Route ??????????????????????????????????????????????????????????????????????
switch ($action) {
    case
'file_list':
       
handleFileList($userId);
        break;

    case
'file_load':
       
handleFileLoad($userId);
        break;

    case
'file_save':
       
handleFileSave($userId);
        break;

    case
'file_delete':
       
handleFileDelete($userId);
        break;

    case
'file_publish':
       
handleFilePublish($userId);
        break;

    case
'capsule_url':
       
handleCapsuleUrl($user);
        break;

    default:
       
WebDoorSDK\jsonError('Unknown action', 400);
}

// ?? Helpers ????????????????????????????????????????????????????????????????????

/**
 * Validate a capsule filename.
 * Allowed: alphanumeric, dash, underscore, with .gmi or .gemini extension.
 */
function isValidFilename(string $filename): bool
{
    return (bool)
preg_match('/^[a-zA-Z0-9_\-]+\.(gmi|gemini)$/', $filename)
        &&
strlen($filename) <= 100;
}

// ?? Action: file_list ??????????????????????????????????????????????????????????

/**
 * List all capsule files for the current user.
 */
function handleFileList(int $userId): void
{
   
$db = WebDoorSDK\getDatabase();
   
$stmt = $db->prepare(
       
'SELECT filename, is_published, updated_at
         FROM gemini_capsule_files
         WHERE user_id = ?
         ORDER BY filename'
   
);
   
$stmt->execute([$userId]);
   
$files = $stmt->fetchAll(\PDO::FETCH_ASSOC);

   
// Normalise boolean from PostgreSQL
   
foreach ($files as &$file) {
       
$file['is_published'] = ($file['is_published'] === true || $file['is_published'] === 't');
    }
    unset(
$file);

   
WebDoorSDK\jsonResponse(['files' => $files]);
}

// ?? Action: file_load ??????????????????????????????????????????????????????????

/**
 * Load a single file's content.
 */
function handleFileLoad(int $userId): void
{
   
$filename = trim($_GET['filename'] ?? '');

    if (
$filename === '') {
       
WebDoorSDK\jsonError('filename parameter is required', 400);
    }

    if (!
isValidFilename($filename)) {
       
WebDoorSDK\jsonError('Invalid filename', 400);
    }

   
$db = WebDoorSDK\getDatabase();
   
$stmt = $db->prepare(
       
'SELECT filename, content, is_published
         FROM gemini_capsule_files
         WHERE user_id = ? AND filename = ?'
   
);
   
$stmt->execute([$userId, $filename]);
   
$file = $stmt->fetch(\PDO::FETCH_ASSOC);

    if (!
$file) {
       
WebDoorSDK\jsonError('File not found', 404);
    }

   
$file['is_published'] = ($file['is_published'] === true || $file['is_published'] === 't');

   
WebDoorSDK\jsonResponse($file);
}

// ?? Action: file_save ??????????????????????????????????????????????????????????

/**
 * Create or update a capsule file (upsert).
 */
function handleFileSave(int $userId): void
{
   
$input = json_decode((string)file_get_contents('php://input'), true) ?? [];
   
$filename = trim((string)($input['filename'] ?? ''));
   
$content = (string)($input['content'] ?? '');

    if (
$filename === '') {
       
WebDoorSDK\jsonError('filename is required', 400);
    }

    if (!
isValidFilename($filename)) {
       
WebDoorSDK\jsonError('Invalid filename ? use letters, numbers, dashes, underscores with .gmi or .gemini extension', 400);
    }

    if (
strlen($content) > 524288) {
       
WebDoorSDK\jsonError('File content exceeds the 512 KB limit', 400);
    }

   
$db = WebDoorSDK\getDatabase();
   
$stmt = $db->prepare(
       
'INSERT INTO gemini_capsule_files (user_id, filename, content, updated_at)
         VALUES (?, ?, ?, NOW())
         ON CONFLICT (user_id, filename)
         DO UPDATE SET content = EXCLUDED.content, updated_at = NOW()'
   
);
   
$stmt->execute([$userId, $filename, $content]);

   
WebDoorSDK\jsonResponse(['success' => true, 'filename' => $filename]);
}

// ?? Action: file_delete ????????????????????????????????????????????????????????

/**
 * Delete a capsule file.
 */
function handleFileDelete(int $userId): void
{
   
$input = json_decode((string)file_get_contents('php://input'), true) ?? [];
   
$filename = trim((string)($input['filename'] ?? ''));

    if (
$filename === '') {
       
WebDoorSDK\jsonError('filename is required', 400);
    }

    if (!
isValidFilename($filename)) {
       
WebDoorSDK\jsonError('Invalid filename', 400);
    }

   
$db = WebDoorSDK\getDatabase();
   
$stmt = $db->prepare(
       
'DELETE FROM gemini_capsule_files WHERE user_id = ? AND filename = ?'
   
);
   
$stmt->execute([$userId, $filename]);

   
WebDoorSDK\jsonResponse(['success' => true]);
}

// ?? Action: file_publish ???????????????????????????????????????????????????????

/**
 * Toggle the published flag for a capsule file.
 */
function handleFilePublish(int $userId): void
{
   
$input = json_decode((string)file_get_contents('php://input'), true) ?? [];
   
$filename = trim((string)($input['filename'] ?? ''));
   
$isPublished = (bool)($input['is_published'] ?? false);

    if (
$filename === '') {
       
WebDoorSDK\jsonError('filename is required', 400);
    }

    if (!
isValidFilename($filename)) {
       
WebDoorSDK\jsonError('Invalid filename', 400);
    }

   
$db = WebDoorSDK\getDatabase();
   
$stmt = $db->prepare(
       
'UPDATE gemini_capsule_files
         SET is_published = ?, updated_at = NOW()
         WHERE user_id = ? AND filename = ?'
   
);
   
$stmt->execute([$isPublished ? 'true' : 'false', $userId, $filename]);

    if (
$stmt->rowCount() === 0) {
       
WebDoorSDK\jsonError('File not found', 404);
    }

   
WebDoorSDK\jsonResponse(['success' => true, 'is_published' => $isPublished]);
}

// ?? Action: capsule_url ????????????????????????????????????????????????????????

/**
 * Return the Gemini URL for the current user's capsule.
 */
function handleCapsuleUrl(array $user): void
{
   
$host = 'localhost';
   
$siteUrl = Config::getSiteUrl();
   
$parsed = parse_url($siteUrl);
    if (!empty(
$parsed['host'])) {
       
$host = $parsed['host'];
    }

   
$username = $user['username'] ?? '';
   
WebDoorSDK\jsonResponse([
       
'url' => "gemini://{$host}/home/{$username}/",
    ]);
}