* Replace /api/v1/resources with /api/v1/attachments for image uploads
* Upload attachments as JSON with base64-encoded content field
* After memo creation, link each attachment via PATCH /api/v1/attachments/{id}
* Rewrite markdown image URLs to use /file/attachments/{id} pattern
* Fix XSS: sanitize marked.parse output with a DOM-based allowlist sanitizer
* Fix SSRF: validate img.src scheme (http/https only) before fetching
* Fix stack overflow: use chunked base64 encoding for large images
* Update CLAUDE.md to document new attachment flow
75 lines
5.3 KiB
HTML
75 lines
5.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>Memos Clipper — Settings</title>
|
|
<link rel="stylesheet" href="style.css" />
|
|
<link rel="stylesheet" href="settings.css" />
|
|
</head>
|
|
<body>
|
|
<div class="page">
|
|
<header class="p-6 border-b border-gray-100 dark:border-gray-800 bg-white dark:bg-[#1a1a1f]">
|
|
<div class="logo flex items-center space-x-3 text-2xl font-bold text-emerald-500">
|
|
<img src="icons/icon48.png" width="32" height="32" alt="Memos Logo">
|
|
<span class="dark:text-gray-200">Memos Clipper Settings</span>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="max-w-3xl mx-auto p-6 space-y-8">
|
|
<div class="card bg-white dark:bg-[#1a1a1f] rounded-xl shadow-sm border border-gray-100 dark:border-gray-800 p-6 space-y-6">
|
|
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200 border-b dark:border-gray-800 pb-2">Connection</h2>
|
|
<label class="block space-y-2">
|
|
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Memos Instance URL</span>
|
|
<input type="url" id="memos-url" placeholder="https://memos.example.com" class="w-full px-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-emerald-100 dark:focus:ring-emerald-900/30 dark:bg-transparent dark:text-inherit outline-none transition" />
|
|
<small class="block text-xs text-gray-400 dark:text-gray-500">The base URL of your usememos instance (no trailing slash)</small>
|
|
</label>
|
|
<label class="block space-y-2">
|
|
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">API Token</span>
|
|
<input type="password" id="api-token" placeholder="Your access token" class="w-full px-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-emerald-100 dark:focus:ring-emerald-900/30 dark:bg-transparent dark:text-inherit outline-none transition" />
|
|
<small class="block text-xs text-gray-400 dark:text-gray-500">Settings → Account → Access Tokens in your Memos instance</small>
|
|
</label>
|
|
<div class="actions flex space-x-3 pt-2">
|
|
<button id="test-btn" class="secondary flex-1 bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 font-medium py-2 px-4 rounded-lg border border-gray-200 dark:border-gray-700 transition">Test Connection</button>
|
|
<button id="save-btn" class="flex-1 bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-2 px-4 rounded-lg shadow-sm transition">Save Settings</button>
|
|
</div>
|
|
<div id="status" class="status hidden p-3 rounded-lg text-sm"></div>
|
|
</div>
|
|
|
|
<div class="card bg-white dark:bg-[#1a1a1f] rounded-xl shadow-sm border border-gray-100 dark:border-gray-800 p-6 space-y-6">
|
|
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200 border-b dark:border-gray-800 pb-2">Defaults</h2>
|
|
<label class="block space-y-2">
|
|
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Default visibility</span>
|
|
<select id="visibility" class="w-full px-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-emerald-100 dark:focus:ring-emerald-900/30 bg-white dark:bg-transparent dark:text-inherit outline-none transition">
|
|
<option value="PRIVATE">Private</option>
|
|
<option value="PROTECTED">Protected</option>
|
|
<option value="PUBLIC">Public</option>
|
|
</select>
|
|
</label>
|
|
<label class="block space-y-2">
|
|
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Default clip mode</span>
|
|
<select id="clip-mode" class="w-full px-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-emerald-100 dark:focus:ring-emerald-900/30 bg-white dark:bg-transparent dark:text-inherit outline-none transition">
|
|
<option value="page">Full page (article extraction)</option>
|
|
<option value="selection">Selection only</option>
|
|
</select>
|
|
</label>
|
|
<div class="space-y-3 pt-2">
|
|
<label class="flex items-center space-x-3 cursor-pointer group">
|
|
<input type="checkbox" id="include-images" checked class="w-4 h-4 text-emerald-500 border-gray-300 dark:border-gray-700 rounded focus:ring-emerald-400 transition" />
|
|
<span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition">Upload page images as attachments</span>
|
|
</label>
|
|
<label class="flex items-center space-x-3 cursor-pointer group">
|
|
<input type="checkbox" id="include-tags" class="w-4 h-4 text-emerald-500 border-gray-300 dark:border-gray-700 rounded focus:ring-emerald-400 transition" />
|
|
<span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition">Automatically add #clipped tag</span>
|
|
</label>
|
|
</div>
|
|
<div class="actions pt-4">
|
|
<button id="save-defaults-btn" class="w-full bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-2 px-4 rounded-lg shadow-sm transition">Save Defaults</button>
|
|
</div>
|
|
<div id="defaults-status" class="status hidden p-3 rounded-lg text-sm"></div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
<script src="/settings.js" type="module"></script>
|
|
</body>
|
|
</html>
|