Add My Documents panel to workbench + user-docs API
- api/user-docs.php: GET/DELETE shared dbn_user_docs table (SSO users only) connects to dobetternorge DB via DBN_DB_* env vars - workbench.php: My Documents panel (section 05) for SSO/free-tier users; shows docs uploaded from either AI chat or tools, links to AI Chat for upload - workbench.js: fetch + render doc list, delete with Qdrant cleanup - tools.css: workbench-docs panel + item styles - i18n.php: my_docs_* strings in all 4 languages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* GET /api/user-docs.php — list SSO user's uploaded documents
|
||||
* DELETE /api/user-docs.php?id=X — remove a document
|
||||
*
|
||||
* Only available for SSO users (dbn_tools_sso_uid set in session).
|
||||
* Reads from the shared dobetternorge.dbn_user_docs table, keyed by sso_uid.
|
||||
* Requires DBN_DB_* env vars pointing at the dobetternorge database.
|
||||
*/
|
||||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||||
|
||||
dbnToolsRequireMethod('GET', 'DELETE');
|
||||
|
||||
if (!dbnToolsIsAuthenticated()) {
|
||||
http_response_code(401);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['ok' => false, 'error' => 'Not authenticated.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Only SSO users have shared docs
|
||||
$ssoUid = (string)($_SESSION['dbn_tools_sso_uid'] ?? '');
|
||||
if ($ssoUid === '') {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['ok' => true, 'docs' => [], 'reason' => 'sso_only']);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// ── Connect to the shared dobetternorge DB (dbn_user_docs lives here) ─────────
|
||||
function dbnSharedDb(): ?PDO
|
||||
{
|
||||
static $pdo = null;
|
||||
if ($pdo !== null) return $pdo;
|
||||
$host = dbnToolsEnv('DBN_DB_HOST', dbnToolsEnv('DBNM_DB_HOST', 'localhost'));
|
||||
$name = dbnToolsEnv('DBN_DB_NAME', dbnToolsEnv('DBNM_DB_NAME', 'dobetternorge'));
|
||||
$user = dbnToolsEnv('DBN_DB_USER', dbnToolsEnv('DBNM_DB_USER', 'root'));
|
||||
$pass = dbnToolsEnv('DBN_DB_PASS', dbnToolsEnv('DBNM_DB_PASS', ''));
|
||||
try {
|
||||
$pdo = new PDO("mysql:host={$host};dbname={$name};charset=utf8mb4", $user, $pass, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_TIMEOUT => 5,
|
||||
]);
|
||||
return $pdo;
|
||||
} catch (Throwable) { return null; }
|
||||
}
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// ── DELETE ────────────────────────────────────────────────────────────────────
|
||||
if ($method === 'DELETE') {
|
||||
$docId = trim($_GET['id'] ?? '');
|
||||
if ($docId === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['ok' => false, 'error' => 'Missing id parameter.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = dbnSharedDb();
|
||||
if ($db) {
|
||||
$stmt = $db->prepare('SELECT id FROM dbn_user_docs WHERE id = ? AND user_id = ?');
|
||||
$stmt->execute([$docId, $ssoUid]);
|
||||
if ($stmt->fetch()) {
|
||||
$db->prepare('DELETE FROM dbn_user_docs WHERE id = ? AND user_id = ?')
|
||||
->execute([$docId, $ssoUid]);
|
||||
|
||||
// Delete Qdrant points for this doc
|
||||
$qdrantUrl = 'http://10.0.2.10:6333';
|
||||
$body = [
|
||||
'filter' => [
|
||||
'must' => [
|
||||
['key' => 'doc_id', 'match' => ['value' => $docId]],
|
||||
['key' => 'user_id', 'match' => ['value' => $ssoUid]],
|
||||
],
|
||||
],
|
||||
];
|
||||
$ch = curl_init("$qdrantUrl/collections/dbn_user_docs/points/delete");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_POSTFIELDS => json_encode($body),
|
||||
]);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
}
|
||||
}
|
||||
echo json_encode(['ok' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── GET ───────────────────────────────────────────────────────────────────────
|
||||
$db = dbnSharedDb();
|
||||
if (!$db) {
|
||||
echo json_encode(['ok' => true, 'docs' => []]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare(
|
||||
'SELECT id, filename, file_type, chunk_count, source, created_at
|
||||
FROM dbn_user_docs
|
||||
WHERE user_id = ? AND status = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 50'
|
||||
);
|
||||
$stmt->execute([$ssoUid, 'ready']);
|
||||
$rows = $stmt->fetchAll();
|
||||
|
||||
$docs = array_map(static fn($r) => [
|
||||
'doc_id' => $r['id'],
|
||||
'filename' => $r['filename'],
|
||||
'file_type' => $r['file_type'],
|
||||
'chunk_count' => (int)$r['chunk_count'],
|
||||
'source' => $r['source'],
|
||||
'created_at' => $r['created_at'],
|
||||
], $rows);
|
||||
|
||||
echo json_encode(['ok' => true, 'docs' => $docs]);
|
||||
Reference in New Issue
Block a user