2
0
forked from jmug/cactoide

fix: refactor, docker, dateformatter

This commit is contained in:
Levente Orban
2025-08-27 11:45:41 +02:00
parent c11060deab
commit 91209d9efc
11 changed files with 266 additions and 199 deletions

View File

@@ -1,47 +1,24 @@
# Use Node.js 20 Alpine for smaller image size
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json .
# Install dependencies based on the preferred package manager
COPY package.json package-lock.json* ./
RUN npm ci
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Build the application
RUN npm run build
RUN npm prune --production
FROM node:20-alpine
# Production image, copy all the files and run the app
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 sveltekit
# Copy the built application
COPY --from=builder --chown=sveltekit:nodejs /app/build ./build
COPY --from=builder --chown=sveltekit:nodejs /app/package.json ./package.json
COPY --from=builder --chown=sveltekit:nodejs /app/node_modules ./node_modules
USER sveltekit
COPY --from=builder /app/build build/
COPY --from=builder /app/node_modules node_modules/
COPY package.json .
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
# Start the application
CMD ["node", "build"]
ENV NODE_ENV production
CMD [ "node", "build" ]

328
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,6 @@
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/adapter-netlify": "^5.2.2",
"@sveltejs/kit": "^2.22.0",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
"@tailwindcss/forms": "^0.5.9",
@@ -39,6 +38,7 @@
"vite": "^7.0.4"
},
"dependencies": {
"@sveltejs/adapter-node": "^5.3.1",
"drizzle-orm": "^0.44.5",
"postgres": "^3.4.7"
}

12
src/lib/dateFormatter.ts Normal file
View File

@@ -0,0 +1,12 @@
export const formatDate = (dateString: string): string => {
const date = new Date(dateString);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}/${month}/${day}`;
};
export const formatTime = (timeString: string): string => {
const [hours, minutes] = timeString.split(':');
return `${hours}:${minutes}`;
};

View File

@@ -1,5 +1,6 @@
export type EventType = 'limited' | 'unlimited';
export type EventVisibility = 'public' | 'private';
export type ActionType = 'add' | 'remove';
export interface Event {
id: string;

View File

@@ -2,6 +2,7 @@
import type { Event } from '$lib/types';
import { goto } from '$app/navigation';
import type { PageData } from '../$types';
import { formatTime, formatDate } from '$lib/dateFormatter';
let publicEvents: Event[] = [];
let error = '';
@@ -9,19 +10,6 @@
export let data: PageData;
// Use the server-side data
$: publicEvents = data.events;
function formatDate(dateString: string): string {
const date = new Date(dateString);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}/${month}/${day}`;
}
function formatTime(timeString: string): string {
const [hours, minutes] = timeString.split(':');
return `${hours}:${minutes}`;
}
</script>
<svelte:head>

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import type { Event } from '$lib/types';
import { goto } from '$app/navigation';
import { formatTime, formatDate } from '$lib/dateFormatter';
export let data: { events: Event[] };
@@ -56,19 +57,6 @@
showDeleteModal = false;
eventToDelete = null;
}
function formatDate(dateString: string): string {
const date = new Date(dateString);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}/${month}/${day}`;
}
function formatTime(timeString: string): string {
const [hours, minutes] = timeString.split(':');
return `${hours}:${minutes}`;
}
</script>
<svelte:head>

View File

@@ -117,15 +117,14 @@ export const actions: Actions = {
createdAt: new Date()
});
return { success: true };
return { success: true, type: 'add' };
} catch (err) {
console.error('Error adding RSVP:', err);
return fail(500, { error: 'Failed to add RSVP' });
}
},
removeRSVP: async ({ request, params }) => {
const eventId = params.id;
removeRSVP: async ({ request }) => {
const formData = await request.formData();
const rsvpId = formData.get('rsvpId') as string;
@@ -136,7 +135,7 @@ export const actions: Actions = {
try {
await drizzleQuery.delete(rsvps).where(eq(rsvps.id, rsvpId));
return { success: true };
return { success: true, type: 'remove' };
} catch (err) {
console.error('Error removing RSVP:', err);
return fail(500, { error: 'Failed to remove RSVP' });

View File

@@ -3,6 +3,7 @@
import type { Event, RSVP } from '$lib/types';
import { goto } from '$app/navigation';
import { enhance } from '$app/forms';
import { formatTime, formatDate } from '$lib/dateFormatter';
export let data: { event: Event; rsvps: RSVP[]; userId: string };
export let form;
@@ -34,19 +35,6 @@
const eventId = $page.params.id;
function formatDate(dateString: string, timeString: string): string {
const date = new Date(`${dateString}T${timeString}`);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}/${month}/${day}`;
}
function formatTime(timeString: string): string {
const [hours, minutes] = timeString.split(':');
return `${hours}:${minutes}`;
}
function copyEventLink() {
const url = `${window.location.origin}/event/${eventId}`;
navigator.clipboard.writeText(url).then(() => {
@@ -329,11 +317,19 @@
<!-- Success/Error Messages -->
{#if success}
{#if form?.type === 'add'}
<div
class="fixed right-4 bottom-4 z-40 w-128 rounded-sm border border-green-500/30 bg-green-900/20 p-4 text-green-400"
>
{success}
</div>
{:else if form?.type === 'remove'}
<div
class="fixed right-4 bottom-4 z-40 w-128 rounded-sm border border-yellow-500/30 bg-yellow-900/20 p-4 text-yellow-400"
>
Removed RSVP successfully.
</div>
{/if}
{/if}
{#if error}

View File

@@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-netlify';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
@@ -15,7 +15,10 @@ const config = {
// see "split" mode in https://github.com/sveltejs/kit/tree/main/packages/adapter-netlify
edge: false,
split: false
})
}),
csrf: {
checkOrigin: false
}
}
};

View File

@@ -11,9 +11,4 @@
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// To make changes to top-level options such as include and exclude, we recommend extending
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
}