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
5 changes: 5 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ When improving unit test coverage, run only the unit tests since running e2e tes

## Svelte 5

Make sure to use the latest Svelte 5 features and syntax. Use the Context7 API to get the current project documentation for Svelte 5.

This project uses Svelte 5. Use the Svelte 5 runes syntax instead of the older `$:` syntax.

Use the new Svelte 5 syntax for $props()

Use `onclick` instead of `on:click` for event handlers.

## Semantic Classes

Expand Down
12 changes: 5 additions & 7 deletions STRIPE_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ Add the following environment variables to your `.env` file in the app directory

```bash
# Stripe API keys
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key
STRIPE_SECRET_KEY=sk_test_your_secret_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret
PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key
PRIVATE_STRIPE_SECRET_KEY=sk_test_your_secret_key
PRIVATE_STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret

# Supabase Service Role Key (for webhook operations)
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
SUPABASE_URL=your_supabase_url
PRIVATE_SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
PUBLIC_SUPABASE_URL=your_supabase_url
```

> Note: For backward compatibility, the application also supports the legacy `VITE_` prefixed versions of these variables, but the non-prefixed versions are recommended.

## Stripe Product and Price Setup

1. Log in to your Stripe Dashboard: https://dashboard.stripe.com/
Expand Down
19 changes: 10 additions & 9 deletions app/.env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Supabase configuration
# Replace with your actual Supabase project URL and anon key
VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Supabase settings
PUBLIC_SUPABASE_URL=https://your-supabase-url.supabase.co
PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
PRIVATE_SUPABASE_SERVICE_ROLE_KEY=your-supabase-service-role-key

# Stripe settings
PRIVATE_STRIPE_API_KEY=your-stripe-secret-key
PUBLIC_STRIPE_PUBLISHABLE_KEY=your-stripe-publishable-key
PRIVATE_STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret

# Stripe API keys
STRIPE_PUBLISHABLE_KEY=your-publishable-key
STRIPE_SECRET_KEY=your-secret-key
STRIPE_WEBHOOK_SECRET=your-webhook-secret
# Site settings
PUBLIC_SITE_URL=http://localhost:5173
3 changes: 2 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.49.4",
"@vitest/coverage-v8": "3.0.8",
"dexie": "^4.0.11"
"dexie": "^4.0.11",
"stripe": "^18.1.0"
},
"devDependencies": {
"@eslint/compat": "^1.2.8",
Expand Down
26 changes: 26 additions & 0 deletions app/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions app/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
import { SupabaseClient, Session, type AMREntry } from "@supabase/supabase-js";
import type { Database } from "$lib/database/DatabaseDefinitions";

declare global {
namespace App {
interface Locals {
supabase: SupabaseClient<Database>;
supabaseServiceRole: SupabaseClient<Database>;
getSession: () => Promise<{
session: Session | null;
}>;
}

interface PageData {
session: Session | null;
}
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
Expand Down
53 changes: 53 additions & 0 deletions app/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// src/hooks.server.ts
import { PRIVATE_SUPABASE_SERVICE_ROLE_KEY } from "$env/static/private";
import {
PUBLIC_SUPABASE_URL,
PUBLIC_SUPABASE_ANON_KEY,
} from "$env/static/public";
import { createServerClient } from "@supabase/ssr";
import { createClient } from "@supabase/supabase-js";
import type { Handle } from "@sveltejs/kit";
import { sequence } from "@sveltejs/kit/hooks";

export const supabase: Handle = async ({ event, resolve }) => {
event.locals.supabase = createServerClient(
PUBLIC_SUPABASE_URL,
PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
getAll: () => {
return event.cookies
.getAll()
.map(({ name, value }) => ({ name, value }));
},
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) => {
event.cookies.set(name, value, { ...options, path: "/" });
});
},
},
},
);

event.locals.supabaseServiceRole = createClient(
PUBLIC_SUPABASE_URL,
PRIVATE_SUPABASE_SERVICE_ROLE_KEY,
{ auth: { persistSession: false } },
);

// Add a helper method for getting the session
event.locals.getSession = async () => {
const {
data: { session },
} = await event.locals.supabase.auth.getSession();
return { session };
};

return resolve(event, {
filterSerializedResponseHeaders(name) {
return name === "content-range" || name === "x-supabase-api-version";
},
});
};

export const handle: Handle = sequence(supabase);
132 changes: 132 additions & 0 deletions app/src/lib/database/DatabaseDefinitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
export type Json =
| string
| number
| boolean
| null
| { [key: string]: Json | undefined }
| Json[];

export interface Database {
public: {
Tables: {
pricing_plans: {
Row: {
id: string;
name: string;
description: string | null;
features: Json | null;
stripe_price_id: string | null;
stripe_product_id: string | null;
amount: number;
currency: string;
interval: string;
active: boolean;
created_at: string | null;
updated_at: string | null;
};
Insert: {
id?: string;
name: string;
description?: string | null;
features?: Json | null;
stripe_price_id?: string | null;
stripe_product_id?: string | null;
amount: number;
currency?: string;
interval: string;
active?: boolean;
created_at?: string | null;
updated_at?: string | null;
};
Update: {
id?: string;
name?: string;
description?: string | null;
features?: Json | null;
stripe_price_id?: string | null;
stripe_product_id?: string | null;
amount?: number;
currency?: string;
interval?: string;
active?: boolean;
created_at?: string | null;
updated_at?: string | null;
};
};
stripe_customers: {
Row: {
user_id: string;
stripe_customer_id: string;
created_at: string | null;
updated_at: string | null;
};
Insert: {
user_id: string;
stripe_customer_id: string;
created_at?: string | null;
updated_at?: string | null;
};
Update: {
user_id?: string;
stripe_customer_id?: string;
created_at?: string | null;
updated_at?: string | null;
};
};
subscriptions: {
Row: {
id: string;
user_id: string;
stripe_subscription_id: string | null;
stripe_price_id: string | null;
stripe_product_id: string | null;
status: string;
cancel_at_period_end: boolean | null;
current_period_start: string | null;
current_period_end: string | null;
created_at: string | null;
updated_at: string | null;
ended_at: string | null;
};
Insert: {
id?: string;
user_id: string;
stripe_subscription_id?: string | null;
stripe_price_id?: string | null;
stripe_product_id?: string | null;
status: string;
cancel_at_period_end?: boolean | null;
current_period_start?: string | null;
current_period_end?: string | null;
created_at?: string | null;
updated_at?: string | null;
ended_at?: string | null;
};
Update: {
id?: string;
user_id?: string;
stripe_subscription_id?: string | null;
stripe_price_id?: string | null;
stripe_product_id?: string | null;
status?: string;
cancel_at_period_end?: boolean | null;
current_period_start?: string | null;
current_period_end?: string | null;
created_at?: string | null;
updated_at?: string | null;
ended_at?: string | null;
};
};
// Include other tables from your database here
};
Views: {
[_ in never]: never;
};
Functions: {
[_ in never]: never;
};
Enums: {
[_ in never]: never;
};
};
}
Loading
Loading