feat: implement logging functionality and improve API endpoint handling in backend; refactor frontend to use dynamic API base
All checks were successful
Build and publish Docker image / build-and-push (release) Successful in 14s

This commit is contained in:
2025-11-12 00:55:44 +01:00
parent 04f0cc6dbf
commit f3d7408e14
5 changed files with 134 additions and 34 deletions

View File

@@ -2,6 +2,7 @@
import { onMount } from "svelte";
import CounterCard from "./CounterCard.svelte";
import AddCounterCard from "./AddCounterCard.svelte";
import { API_COUNTERS } from "./config";
type Counter = {
id: number;
@@ -13,19 +14,17 @@
let counters: Counter[] = [];
let loading = true;
// API endpoint
const API = "http://localhost:3000/api/counters";
// Use a relative API base so it works no matter the host/origin
const API = API_COUNTERS;
const CACHE_KEY = "tally-counters-cache";
async function fetchCounters() {
loading = true;
// Try to load from cache first
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
counters = JSON.parse(cached);
loading = false;
}
// Always fetch fresh data in the background
const res = await fetch(API);
const fresh = await res.json();
counters = fresh;

View File

@@ -1,5 +1,7 @@
<script lang="ts">
import { clickOutside } from "./clickOutside";
import { API_BASE } from "./config";
import { onDestroy } from 'svelte';
export let counter: {
id: number;
@@ -21,16 +23,38 @@
export let onSetEditImage: (file: File | null) => void;
export let onCancelEdit: (id: number) => void;
// For previewing the new image
let previewUrl: string | null = null;
$: if (editImage) {
previewUrl = URL.createObjectURL(editImage);
} else {
previewUrl = null;
// Build a safe image URL:
function imageUrl(img?: string | null) {
if (!img) return null;
// already absolute
if (img.startsWith("http") || img.startsWith("//")) return img;
// ensure no double slashes when API_BASE is set
const base = (API_BASE || "").replace(/\/$/, "");
if (base) return `${base}${img}`;
return img; // relative path like "/uploads/..."
}
const BACKEND_URL = "http://localhost:3000";
// preview management (revoke previous object URL)
let previewUrl: string | null = null;
let _prevObjectUrl: string | null = null;
$: {
if (editImage) {
const u = URL.createObjectURL(editImage);
if (_prevObjectUrl) URL.revokeObjectURL(_prevObjectUrl);
previewUrl = u;
_prevObjectUrl = u;
} else {
if (_prevObjectUrl) {
URL.revokeObjectURL(_prevObjectUrl);
_prevObjectUrl = null;
}
previewUrl = null;
}
}
onDestroy(() => {
if (_prevObjectUrl) URL.revokeObjectURL(_prevObjectUrl);
});
</script>
<div
@@ -56,11 +80,7 @@
{#if previewUrl}
<img src={previewUrl} alt="Preview" class="card-img" />
{:else if counter.image}
<img
src={`${BACKEND_URL}${counter.image}`}
alt={counter.name}
class="card-img"
/>
<img src={imageUrl(counter.image)} alt={counter.name} class="card-img" />
{:else}
<div class="card-img placeholder"></div>
{/if}
@@ -89,10 +109,10 @@
<div class="card-img-container">
{#if counter.image}
<img
src={`${BACKEND_URL}${counter.image}`}
alt={counter.name}
class="card-img"
/>
src={imageUrl(counter.image)}
alt={counter.name}
class="card-img"
/>
{:else}
<div class="card-img placeholder">No image</div>
{/if}

3
frontend/src/config.ts Normal file
View File

@@ -0,0 +1,3 @@
export const API_BASE = import.meta.env.VITE_API_URL ?? ''; // empty = same origin
export const API_COUNTERS = `${API_BASE}/api/counters`;
export const UPLOADS_BASE = `${API_BASE}/uploads`;

View File

@@ -1,7 +1,21 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
// https://vite.dev/config/
export default defineConfig({
plugins: [svelte()],
})
server: {
port: Number(process.env.VITE_PORT) || 5173,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
secure: false,
},
'/uploads': {
target: 'http://localhost:3000',
changeOrigin: true,
secure: false,
},
},
},
});