release(v2.0.0): feat(pro): client portals + portal login flow
This commit is contained in:
27
public/api/pro/portals/get.php
Normal file
27
public/api/pro/portals/get.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
// public/api/pro/portals/get.php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/PortalController.php';
|
||||
|
||||
try {
|
||||
$slug = isset($_GET['slug']) ? (string)$_GET['slug'] : '';
|
||||
|
||||
// For v1: we do NOT require auth here; this is just metadata,
|
||||
// real ACL/access control must still be enforced at upload/download endpoints.
|
||||
$portal = PortalController::getPortalBySlug($slug);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'portal' => $portal,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(404);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
32
public/api/pro/portals/list.php
Normal file
32
public/api/pro/portals/list.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// public/api/pro/portals/list.php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||
|
||||
try {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
AdminController::requireAuth();
|
||||
AdminController::requireAdmin();
|
||||
|
||||
$ctrl = new AdminController();
|
||||
$portals = $ctrl->getProPortals();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'portals' => $portals,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} catch (Throwable $e) {
|
||||
$code = $e instanceof InvalidArgumentException ? 400 : 500;
|
||||
http_response_code($code);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
108
public/api/pro/portals/publicMeta.php
Normal file
108
public/api/pro/portals/publicMeta.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
// public/api/pro/portals/publicMeta.php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
|
||||
// --- Basic Pro checks ---
|
||||
if (!defined('FR_PRO_ACTIVE') || !FR_PRO_ACTIVE) {
|
||||
http_response_code(404);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'FileRise Pro is not active.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$slug = isset($_GET['slug']) ? trim((string)$_GET['slug']) : '';
|
||||
if ($slug === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Missing portal slug.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Locate portals.json written by saveProPortals() ---
|
||||
$bundleDir = defined('FR_PRO_BUNDLE_DIR') ? (string)FR_PRO_BUNDLE_DIR : '';
|
||||
if ($bundleDir === '' || !is_dir($bundleDir)) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Pro bundle directory not found.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$jsonPath = rtrim($bundleDir, "/\\") . '/portals.json';
|
||||
if (!is_file($jsonPath)) {
|
||||
http_response_code(404);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'No portals defined.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$raw = @file_get_contents($jsonPath);
|
||||
if ($raw === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Could not read portals store.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode($raw, true);
|
||||
if (!is_array($data)) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Invalid portals store.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$portals = $data['portals'] ?? [];
|
||||
if (!is_array($portals) || !isset($portals[$slug]) || !is_array($portals[$slug])) {
|
||||
http_response_code(404);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Portal not found.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$portal = $portals[$slug];
|
||||
|
||||
// Optional: handle expiry if you’re using expiresAt as ISO date string
|
||||
if (!empty($portal['expiresAt'])) {
|
||||
$ts = strtotime((string)$portal['expiresAt']);
|
||||
if ($ts !== false && $ts < time()) {
|
||||
http_response_code(410); // Gone
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'This portal has expired.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Only expose the bits the login page needs (no folder, email, etc.)
|
||||
$public = [
|
||||
'slug' => $slug,
|
||||
'label' => (string)($portal['label'] ?? ''),
|
||||
'title' => (string)($portal['title'] ?? ''),
|
||||
'introText' => (string)($portal['introText'] ?? ''),
|
||||
'brandColor' => (string)($portal['brandColor'] ?? ''),
|
||||
'footerText' => (string)($portal['footerText'] ?? ''),
|
||||
];
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'portal' => $public,
|
||||
]);
|
||||
51
public/api/pro/portals/save.php
Normal file
51
public/api/pro/portals/save.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// public/api/pro/portals/save.php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||
|
||||
try {
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
AdminController::requireAuth();
|
||||
AdminController::requireAdmin();
|
||||
AdminController::requireCsrf();
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$body = json_decode($raw, true);
|
||||
if (!is_array($body)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid JSON body']);
|
||||
return;
|
||||
}
|
||||
|
||||
$portals = $body['portals'] ?? null;
|
||||
if (!is_array($portals)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid or missing "portals" payload']);
|
||||
return;
|
||||
}
|
||||
|
||||
$ctrl = new AdminController();
|
||||
$ctrl->saveProPortals($portals);
|
||||
|
||||
echo json_encode(['success' => true], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} catch (Throwable $e) {
|
||||
$code = $e instanceof InvalidArgumentException ? 400 : 500;
|
||||
http_response_code($code);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
64
public/api/pro/portals/submissions.php
Normal file
64
public/api/pro/portals/submissions.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
|
||||
try {
|
||||
// --- Basic auth / admin check (keep it simple & consistent with your other admin APIs)
|
||||
@session_start();
|
||||
|
||||
$username = (string)($_SESSION['username'] ?? '');
|
||||
$isAdmin = !empty($_SESSION['isAdmin']) || (!empty($_SESSION['admin']) && $_SESSION['admin'] === '1');
|
||||
|
||||
if ($username === '' || !$isAdmin) {
|
||||
http_response_code(403);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Forbidden',
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Snapshot done, release lock for concurrency
|
||||
@session_write_close();
|
||||
|
||||
if (!defined('FR_PRO_ACTIVE') || !FR_PRO_ACTIVE || !defined('FR_PRO_BUNDLE_DIR') || !FR_PRO_BUNDLE_DIR) {
|
||||
throw new RuntimeException('FileRise Pro is not active.');
|
||||
}
|
||||
|
||||
$slug = isset($_GET['slug']) ? trim((string)$_GET['slug']) : '';
|
||||
if ($slug === '') {
|
||||
throw new InvalidArgumentException('Missing slug.');
|
||||
}
|
||||
|
||||
// Use your ProPortalSubmissions helper from the bundle
|
||||
$proSubmissionsPath = rtrim((string)FR_PRO_BUNDLE_DIR, "/\\") . '/ProPortalSubmissions.php';
|
||||
if (!is_file($proSubmissionsPath)) {
|
||||
throw new RuntimeException('ProPortalSubmissions.php not found in Pro bundle.');
|
||||
}
|
||||
require_once $proSubmissionsPath;
|
||||
|
||||
$store = new ProPortalSubmissions((string)FR_PRO_BUNDLE_DIR);
|
||||
$submissions = $store->listBySlug($slug, 200);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'slug' => $slug,
|
||||
'submissions' => $submissions,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Server error: ' . $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
91
public/api/pro/portals/submitForm.php
Normal file
91
public/api/pro/portals/submitForm.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// public/api/pro/portals/submitForm.php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/PortalController.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||
|
||||
try {
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// For now, portal forms still require a logged-in user
|
||||
AdminController::requireAuth();
|
||||
AdminController::requireCsrf();
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$body = json_decode($raw, true);
|
||||
if (!is_array($body)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid JSON body']);
|
||||
return;
|
||||
}
|
||||
|
||||
$slug = isset($body['slug']) ? trim((string)$body['slug']) : '';
|
||||
if ($slug === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing portal slug']);
|
||||
return;
|
||||
}
|
||||
|
||||
$form = isset($body['form']) && is_array($body['form']) ? $body['form'] : [];
|
||||
$name = trim((string)($form['name'] ?? ''));
|
||||
$email = trim((string)($form['email'] ?? ''));
|
||||
$reference = trim((string)($form['reference'] ?? ''));
|
||||
$notes = trim((string)($form['notes'] ?? ''));
|
||||
|
||||
// Make sure portal exists and is not expired
|
||||
$portal = PortalController::getPortalBySlug($slug);
|
||||
|
||||
if (!defined('FR_PRO_ACTIVE') || !FR_PRO_ACTIVE || !defined('FR_PRO_BUNDLE_DIR') || !FR_PRO_BUNDLE_DIR) {
|
||||
throw new RuntimeException('FileRise Pro is not active.');
|
||||
}
|
||||
|
||||
$subPath = rtrim((string)FR_PRO_BUNDLE_DIR, "/\\") . '/ProPortalSubmissions.php';
|
||||
if (!is_file($subPath)) {
|
||||
throw new RuntimeException('ProPortalSubmissions.php not found in Pro bundle.');
|
||||
}
|
||||
require_once $subPath;
|
||||
|
||||
$submittedBy = (string)($_SESSION['username'] ?? '');
|
||||
$payload = [
|
||||
'slug' => $slug,
|
||||
'portalLabel' => $portal['label'] ?? '',
|
||||
'folder' => $portal['folder'] ?? '',
|
||||
'form' => [
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'reference' => $reference,
|
||||
'notes' => $notes,
|
||||
],
|
||||
'submittedBy' => $submittedBy,
|
||||
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
|
||||
'userAgent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
||||
'createdAt' => gmdate('c'),
|
||||
];
|
||||
|
||||
$store = new ProPortalSubmissions(FR_PRO_BUNDLE_DIR);
|
||||
$ok = $store->store($slug, $payload);
|
||||
if (!$ok) {
|
||||
throw new RuntimeException('Failed to store portal submission.');
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} catch (Throwable $e) {
|
||||
$code = $e instanceof InvalidArgumentException ? 400 : 500;
|
||||
http_response_code($code);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
Reference in New Issue
Block a user