diff --git a/src/routes/api/shifts/start/+server.ts b/src/routes/api/shifts/start/+server.ts
index 62584aa..a22aba7 100644
--- a/src/routes/api/shifts/start/+server.ts
+++ b/src/routes/api/shifts/start/+server.ts
@@ -4,74 +4,68 @@ import { operators, shifts } from '$lib/server/db/schema';
import { eq, and, isNull } from 'drizzle-orm';
import { verifyPasscode, checkRateLimit, clearRateLimit, logAuthFailure, createOperatorSession, rehashIfPlaintext } from '$lib/server/auth';
import { dev } from '$app/environment';
-import { logger } from '$lib/server/logger';
import type { RequestHandler } from './$types';
export const POST: RequestHandler = async ({ request, cookies, getClientAddress }) => {
- try {
- const clientIp = getClientAddress();
- const rateCheck = await checkRateLimit(`shift-start:${clientIp}`);
- if (!rateCheck.allowed) {
- return json(
- { error: 'Too many login attempts. Try again later.' },
- { status: 429, headers: { 'Retry-After': String(rateCheck.retryAfterSeconds) } }
- );
- }
+ const clientIp = getClientAddress();
+ const rateCheck = await checkRateLimit(`shift-start:${clientIp}`);
+ if (!rateCheck.allowed) {
+ return json(
+ { error: 'Too many login attempts. Try again later.' },
+ { status: 429, headers: { 'Retry-After': String(rateCheck.retryAfterSeconds) } }
+ );
+ }
- const { operatorId, passcode } = await request.json();
+ const { operatorId, passcode } = await request.json();
- if (!operatorId || !passcode) {
- return json({ error: 'Operator ID and passcode required' }, { status: 400 });
- }
+ if (!operatorId || !passcode) {
+ return json({ error: 'Operator ID and passcode required' }, { status: 400 });
+ }
- const [operator] = await db
- .select()
- .from(operators)
- .where(and(eq(operators.id, operatorId), eq(operators.isActive, true)));
+ const [operator] = await db
+ .select()
+ .from(operators)
+ .where(and(eq(operators.id, operatorId), eq(operators.isActive, true)));
- if (!operator) {
- logAuthFailure('/api/shifts/start', String(operatorId), clientIp);
- return json({ error: 'Invalid credentials' }, { status: 401 });
- }
+ if (!operator) {
+ logAuthFailure('/api/shifts/start', String(operatorId), clientIp);
+ return json({ error: 'Invalid credentials' }, { status: 401 });
+ }
- const valid = await verifyPasscode(passcode, operator.passcode);
- if (!valid) {
- logAuthFailure('/api/shifts/start', String(operatorId), clientIp);
- return json({ error: 'Invalid credentials' }, { status: 401 });
- }
+ const valid = await verifyPasscode(passcode, operator.passcode);
+ if (!valid) {
+ logAuthFailure('/api/shifts/start', String(operatorId), clientIp);
+ return json({ error: 'Invalid credentials' }, { status: 401 });
+ }
- await clearRateLimit(`shift-start:${clientIp}`);
- rehashIfPlaintext('operators', operatorId, operator.passcode).catch(() => {});
+ await clearRateLimit(`shift-start:${clientIp}`);
+ rehashIfPlaintext('operators', operatorId, operator.passcode).catch(() => {});
- const [existingShift] = await db
- .select()
- .from(shifts)
- .where(and(eq(shifts.operatorId, operatorId), isNull(shifts.endedAt)));
+ const [existingShift] = await db
+ .select()
+ .from(shifts)
+ .where(and(eq(shifts.operatorId, operatorId), isNull(shifts.endedAt)));
- let shift = existingShift;
- if (!existingShift) {
- const [newShift] = await db
- .insert(shifts)
- .values({
- operatorId
- })
- .returning();
- shift = newShift;
- }
+ let shift = existingShift;
+ if (!existingShift) {
+ const [newShift] = await db
+ .insert(shifts)
+ .values({
+ operatorId
+ })
+ .returning();
+ shift = newShift;
+ }
- const sessionToken = await createOperatorSession(operatorId);
- cookies.set('operatorSession', sessionToken, {
- path: '/',
- httpOnly: true,
- sameSite: 'lax',
- secure: !dev,
- maxAge: 60 * 60 * 24
- });
+ const sessionToken = await createOperatorSession(operatorId);
+ cookies.set('operatorSession', sessionToken, {
+ path: '/',
+ httpOnly: true,
+ sameSite: 'lax',
+ secure: !dev,
+ maxAge: 60 * 60 * 24
+ });
- const { passcode: _, ...safeOperator } = operator;
- return json({ operator: safeOperator, shift });
- } catch (err) {
- logger.error({ err, endpoint: '/api/shifts/start' }, 'shift_start_error');
- return json({ error: String(err) }, { status: 500 });
- }
+ const { passcode: _, ...safeOperator } = operator;
+ return json({ operator: safeOperator, shift });
};
diff --git a/src/routes/home/+page.svelte b/src/routes/home/+page.svelte
new file mode 100644
index 0000000..da1f5e4
--- /dev/null
+++ b/src/routes/home/+page.svelte
@@ -0,0 +1,86 @@
+
+ Rental Manager
+
+
+
+
+
diff --git a/src/routes/privacy/+page.svelte b/src/routes/privacy/+page.svelte
new file mode 100644
index 0000000..7c9b27c
--- /dev/null
+++ b/src/routes/privacy/+page.svelte
@@ -0,0 +1,148 @@
+
+ Privacy Policy - Rental Manager
+
+
+
+
+
+ arrow_back
+ Back to Home
+
+
+
Privacy Policy
+
Last updated: February 2026
+
+
+ Overview
+
+ Rental Manager is an internal equipment rental and point-of-sale management system.
+ This policy describes how we handle data within the application.
+
+
+
+
+ Data We Collect
+
+ The application stores data necessary for rental operations, including:
+
+
+ - Operator names and authentication credentials
+ - Guide names and availability status
+ - Equipment inventory and rental records
+ - Sales transaction records
+ - Shift logs and operational data
+
+
+
+
+ Google API Usage
+
+ This application uses Google APIs (Sheets and Drive) solely to export shift reports.
+ We only access data you explicitly authorize and do not share it with third parties.
+ Our use of Google API data adheres to the
+
+ Google API Services User Data Policy, including the Limited Use requirements.
+
+
+
+
+ Data Storage
+
+ All data is stored on a secure server. We do not sell, share, or transfer data to
+ third parties beyond what is described above.
+
+
+
+
+ Contact
+
+ For questions about this policy, contact the system administrator.
+
+
+
+
+
+
+
+