Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 52 additions & 16 deletions public/functions/api/card.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export async function onRequest(event) {
const url = new URL(event.request.url);
const key = url.searchParams.get("key");
const hash = url.searchParams.get("hash");

// SVG Headers
const headers = {
Expand All @@ -11,26 +12,66 @@ export async function onRequest(event) {

const errorSvg = (msg) => new Response(`<svg width="600" height="300" xmlns="http://www.w3.org/2000/svg"><text x="300" y="150" text-anchor="middle" fill="#ef4444" font-family="sans-serif">${msg}</text></svg>`, { headers });

if (!key) {
return errorSvg("Key missing");
if (!key && !hash) {
return errorSvg("Key or Hash missing");
}

try {
const DB = globalThis.RAILROUND_KV;
if (!DB) return errorSvg("KV Error");

const username = await DB.get(`card_key:${key}`);
if (!username) return errorSvg("Invalid Key");

const userKey = `user:${username}`;
const dataRaw = await DB.get(userKey);
let stats = null;
let username = "Traveller";
let isGlobalEnabled = true;

if (hash) {
// Folder Badge Mode
const badgeDataRaw = await DB.get(`badge:${hash}`);
if (!badgeDataRaw) return errorSvg("Invalid Hash");

const badgeData = JSON.parse(badgeDataRaw);
username = badgeData.username || "Traveller";
stats = badgeData.stats;

// Check global switch via User KV
if (badgeData.username) {
const userKey = `user:${badgeData.username}`;
const userDataRaw = await DB.get(userKey);
if (userDataRaw) {
const u = JSON.parse(userDataRaw);
if (u.badge_settings?.enabled === false) {
isGlobalEnabled = false;
}
}
}

} else if (key) {
// Global Badge Mode
username = await DB.get(`card_key:${key}`);
if (!username) return errorSvg("Invalid Key");

const userKey = `user:${username}`;
const dataRaw = await DB.get(userKey);

if (!dataRaw) {
return errorSvg("User data not found");
}

const data = JSON.parse(dataRaw);

// Check Master Switch
if (data.badge_settings?.enabled === false) {
isGlobalEnabled = false;
}

stats = data.latest_5 || { count: 0, dist: 0, lines: 0, latest: [] };
}

if (!dataRaw) {
return errorSvg("User data not found");
if (!isGlobalEnabled) {
return errorSvg("Badge Disabled by User");
}

const data = JSON.parse(dataRaw);
const stats = data.latest_5 || { count: 0, dist: 0, lines: 0, latest: [] };
if (!stats) stats = { count: 0, dist: 0, lines: 0, latest: [] };

const esc = (str) => {
if (!str) return "";
Expand Down Expand Up @@ -92,11 +133,6 @@ export async function onRequest(event) {

<!-- Stats Grid (Top Right, shifted left of icon) -->
<g transform="translate(24, 75)">
<!-- Stats moved to header line to save vertical space? Or kept below?
User said "placing the stats split on the right".
Let's move them up to y=20 relative to header or keep in separate row but compressed.
Let's try putting them on the same visual "block" but aligned.
-->
</g>

<!-- Redesigned Stats (Aligned Right) -->
Expand Down
40 changes: 39 additions & 1 deletion public/functions/api/user/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,55 @@ export async function onRequest(event) {

if (event.request.method === "POST") {
const body = await event.request.json();
const { trips, pins, latest_5, version } = body;
const { trips, pins, latest_5, version, folders, badge_settings } = body;

// Fetch existing to preserve other fields (like password, bindings)
const existingRaw = await DB.get(userKey);
const existing = existingRaw ? JSON.parse(existingRaw) : {};

// --- Folder Badge Sync Logic ---
if (folders && Array.isArray(folders)) {
const oldFolders = existing.folders || [];

// 1. Identify hashes to delete (existed before, but now removed or made private)
// Map current public hashes
const newPublicHashes = new Set(folders.filter(f => f.is_public && f.hash).map(f => f.hash));

const promises = [];

oldFolders.forEach(f => {
if (f.hash && !newPublicHashes.has(f.hash)) {
promises.push(DB.delete(`badge:${f.hash}`));
}
});

// 2. Identify/Update hashes to save
folders.forEach(f => {
if (f.is_public && f.hash && f.stats) {
// Store minimal data needed for the card
const badgeData = {
username: username,
stats: f.stats,
type: 'folder',
updated_at: new Date().toISOString()
};
promises.push(DB.put(`badge:${f.hash}`, JSON.stringify(badgeData)));
}
});

if (promises.length > 0) {
await Promise.allSettled(promises);
}
}
// -------------------------------

const newData = {
...existing,
trips: trips || existing.trips || [],
pins: pins || existing.pins || [],
latest_5: latest_5 || existing.latest_5 || null, // Store the pre-calculated card data
folders: folders || existing.folders || [],
badge_settings: badge_settings || existing.badge_settings || { enabled: true },
version: version || existing.version || null
};

Expand Down
Loading