next-ts-cli 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/index.js +94 -0
- package/package.json +97 -0
- package/template/base/.cusror/mpc.json +8 -0
- package/template/base/.husky/commit-msg +1 -0
- package/template/base/.husky/pre-commit +1 -0
- package/template/base/LICENSE +21 -0
- package/template/base/README.md +0 -0
- package/template/base/app/apple-icon.png +0 -0
- package/template/base/app/favicon.ico +0 -0
- package/template/base/app/globals.css +9 -0
- package/template/base/app/layout.tsx +61 -0
- package/template/base/app/loading.tsx +7 -0
- package/template/base/app/manifest.ts +33 -0
- package/template/base/app/opengraph-image.png +0 -0
- package/template/base/app/page.tsx +7 -0
- package/template/base/app/robots.ts +28 -0
- package/template/base/app/sitemap.ts +17 -0
- package/template/base/app/twitter-image.png +0 -0
- package/template/base/biome.jsonc +272 -0
- package/template/base/commitlint.config.ts +25 -0
- package/template/base/hooks/use-hydration.tsx +16 -0
- package/template/base/jest.config.js +18 -0
- package/template/base/jest.setup.js +2 -0
- package/template/base/lib/fonts.ts +14 -0
- package/template/base/lib/indexing.ts +14 -0
- package/template/base/lib/microdata.ts +51 -0
- package/template/base/lib/utils.ts +6 -0
- package/template/base/next.config.ts +7 -0
- package/template/base/package-lock.json +9296 -0
- package/template/base/package.json +59 -0
- package/template/base/postcss.config.js +5 -0
- package/template/base/providers/MicrodataScript.tsx +18 -0
- package/template/base/public/.gitkeep +3 -0
- package/template/base/test/index.test.tsx +9 -0
- package/template/base/tsconfig.json +38 -0
- package/template/extras/better-auth/api/auth/[...all]/route.ts +7 -0
- package/template/extras/better-auth/base-auth.ts +12 -0
- package/template/extras/better-auth/with-drizzle-auth.ts +31 -0
- package/template/extras/clerk/layout.tsx +89 -0
- package/template/extras/clerk/proxy.ts +21 -0
- package/template/extras/docker/.dockerignore +60 -0
- package/template/extras/docker/Dockerfile +51 -0
- package/template/extras/docker/docker-compose.prod.yml +34 -0
- package/template/extras/drizzle/db/index.ts +9 -0
- package/template/extras/drizzle/db/schema.ts +7 -0
- package/template/extras/drizzle/drizzle.config.ts +15 -0
- package/template/extras/neon/index.ts +10 -0
- package/template/extras/shadcnui/components.json +21 -0
- package/template/extras/shadcnui/globals.css +71 -0
- package/template/extras/stripe/checkout_session/route.ts +60 -0
- package/template/extras/stripe/stripe.ts +17 -0
- package/template/extras/stripe/webhook/stripe/route.ts +89 -0
- package/template/extras/supabase/client.ts +8 -0
- package/template/extras/supabase/getAuth.ts +50 -0
- package/template/extras/supabase/proxy.ts +70 -0
- package/template/extras/supabase/server.ts +34 -0
- package/template/extras/supabase/storage.ts +90 -0
- package/template/extras/vercel-ai/route.ts +12 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* =============================================================================================
|
|
2
|
+
|
|
3
|
+
This page is a minimal boilerplate for managing the Stripe subscription system with your website.
|
|
4
|
+
|
|
5
|
+
If you need a non-subscription based system, the webhook configuration will be differnt,
|
|
6
|
+
so check the Stripe documentation for more information.
|
|
7
|
+
|
|
8
|
+
================================================================================================ */
|
|
9
|
+
import { headers } from "next/headers";
|
|
10
|
+
import { NextResponse } from "next/server";
|
|
11
|
+
import { stripe } from "@/lib/stripe";
|
|
12
|
+
import type Stripe from "stripe";
|
|
13
|
+
|
|
14
|
+
export async function POST(req: Request) {
|
|
15
|
+
const body = await req.text();
|
|
16
|
+
const headersList = await headers();
|
|
17
|
+
const signature = headersList.get("Stripe-Signature") as string;
|
|
18
|
+
|
|
19
|
+
let event: Stripe.Event;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
event = stripe().webhooks.constructEvent(
|
|
23
|
+
body,
|
|
24
|
+
signature,
|
|
25
|
+
process.env.STRIPE_WEBHOOK_SECRET as string
|
|
26
|
+
);
|
|
27
|
+
} catch (error: unknown) {
|
|
28
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
29
|
+
return new NextResponse(`Webhook Error: ${message}`, { status: 400 });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
switch (event.type) {
|
|
34
|
+
// When a customer completes checkout
|
|
35
|
+
case "checkout.session.completed": {
|
|
36
|
+
try {
|
|
37
|
+
// Do something
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return new NextResponse(`Error: ${e}`, { status: 500 });
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// When a new subscription is created
|
|
45
|
+
case "customer.subscription.created": {
|
|
46
|
+
const subscription = event.data.object as Stripe.Subscription;
|
|
47
|
+
// Do something, usually create record in DB
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// When a subscription is updated
|
|
52
|
+
case "customer.subscription.updated": {
|
|
53
|
+
const subscription = event.data.object as Stripe.Subscription;
|
|
54
|
+
// Do something, usually update record in DB, when subscription is renewed/stopped, etc.
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// When a subscription is specifically cancelled/deleted
|
|
59
|
+
case "customer.subscription.deleted": {
|
|
60
|
+
const subscription = event.data.object as Stripe.Subscription;
|
|
61
|
+
// Do something, usually delete record in DB, when subscription is cancelled/deleted, etc.
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// When a trial is about to end (3 days before)
|
|
66
|
+
case "customer.subscription.trial_will_end": {
|
|
67
|
+
const subscription = event.data.object as Stripe.Subscription;
|
|
68
|
+
// Do something, usually warn the user via mail or something else
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Invoice events for payment tracking
|
|
73
|
+
case "invoice.payment_succeeded": {
|
|
74
|
+
// Do something, usually log invoices info somewhere
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// When a payment fails
|
|
79
|
+
case "invoice.payment_failed": {
|
|
80
|
+
// Do something, usually warn the user via mail or something else
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return new NextResponse(null, { status: 200 });
|
|
86
|
+
} catch (_) {
|
|
87
|
+
return new NextResponse("Webhook handler error", { status: 500 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createClient } from "@/lib/supabase/server";
|
|
2
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
3
|
+
import { cookies } from "next/headers";
|
|
4
|
+
|
|
5
|
+
export type ProfileModel = {
|
|
6
|
+
id: string;
|
|
7
|
+
fullName: string;
|
|
8
|
+
email: string;
|
|
9
|
+
avatar: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type UseAuthProps =
|
|
13
|
+
| {
|
|
14
|
+
isLoggedIn: true;
|
|
15
|
+
user: ProfileModel;
|
|
16
|
+
supabase: SupabaseClient;
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
isLoggedIn: false;
|
|
20
|
+
user: null;
|
|
21
|
+
supabase: SupabaseClient;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Utility hook to check if the user is logged in or not
|
|
26
|
+
*
|
|
27
|
+
* It works **only Server Side**
|
|
28
|
+
* @returns {UseAuthProps} Auth object {@link UseAuthProps}
|
|
29
|
+
*/
|
|
30
|
+
export const getAuth = async (): Promise<UseAuthProps> => {
|
|
31
|
+
const cookie = cookies();
|
|
32
|
+
const supabase = createClient(cookie);
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
data: { user },
|
|
36
|
+
} = await supabase.auth.getUser();
|
|
37
|
+
|
|
38
|
+
if (user) {
|
|
39
|
+
const userModel: ProfileModel = {
|
|
40
|
+
id: user.id,
|
|
41
|
+
fullName: user.user_metadata?.name ?? user.user_metadata?.user_name,
|
|
42
|
+
email: user.email || "",
|
|
43
|
+
avatar: user.user_metadata.avatar_url || "",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return { isLoggedIn: true, user: userModel, supabase };
|
|
47
|
+
} else {
|
|
48
|
+
return { isLoggedIn: false, user: null, supabase };
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createServerClient } from "@supabase/ssr";
|
|
2
|
+
import { NextResponse, type NextRequest } from "next/server";
|
|
3
|
+
|
|
4
|
+
export async function updateSession(request: NextRequest) {
|
|
5
|
+
let supabaseResponse = NextResponse.next({
|
|
6
|
+
request,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// With Fluid compute, don't put this client in a global environment
|
|
11
|
+
// variable. Always create a new one on each request.
|
|
12
|
+
const supabase = createServerClient(
|
|
13
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
14
|
+
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
|
|
15
|
+
{
|
|
16
|
+
cookies: {
|
|
17
|
+
getAll() {
|
|
18
|
+
return request.cookies.getAll();
|
|
19
|
+
},
|
|
20
|
+
setAll(cookiesToSet) {
|
|
21
|
+
cookiesToSet.forEach(({ name, value }) =>
|
|
22
|
+
request.cookies.set(name, value),
|
|
23
|
+
);
|
|
24
|
+
supabaseResponse = NextResponse.next({
|
|
25
|
+
request,
|
|
26
|
+
});
|
|
27
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
28
|
+
supabaseResponse.cookies.set(name, value, options),
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Do not run code between createServerClient and
|
|
36
|
+
// supabase.auth.getClaims(). A simple mistake could make it very hard to debug
|
|
37
|
+
// issues with users being randomly logged out.
|
|
38
|
+
|
|
39
|
+
// IMPORTANT: If you remove getClaims() and you use server-side rendering
|
|
40
|
+
// with the Supabase client, your users may be randomly logged out.
|
|
41
|
+
const { data } = await supabase.auth.getClaims();
|
|
42
|
+
const user = data?.claims;
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
request.nextUrl.pathname !== "/" &&
|
|
46
|
+
!user &&
|
|
47
|
+
!request.nextUrl.pathname.startsWith("/login") &&
|
|
48
|
+
!request.nextUrl.pathname.startsWith("/auth")
|
|
49
|
+
) {
|
|
50
|
+
// no user, potentially respond by redirecting the user to the login page
|
|
51
|
+
const url = request.nextUrl.clone();
|
|
52
|
+
url.pathname = "/auth/login";
|
|
53
|
+
return NextResponse.redirect(url);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// IMPORTANT: You *must* return the supabaseResponse object as it is.
|
|
57
|
+
// If you're creating a new response object with NextResponse.next() make sure to:
|
|
58
|
+
// 1. Pass the request in it, like so:
|
|
59
|
+
// const myNewResponse = NextResponse.next({ request })
|
|
60
|
+
// 2. Copy over the cookies, like so:
|
|
61
|
+
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
|
|
62
|
+
// 3. Change the myNewResponse object to fit your needs, but avoid changing
|
|
63
|
+
// the cookies!
|
|
64
|
+
// 4. Finally:
|
|
65
|
+
// return myNewResponse
|
|
66
|
+
// If this is not done, you may be causing the browser and server to go out
|
|
67
|
+
// of sync and terminate the user's session prematurely!
|
|
68
|
+
|
|
69
|
+
return supabaseResponse;
|
|
70
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createServerClient } from "@supabase/ssr";
|
|
2
|
+
import { cookies } from "next/headers";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Especially important if using Fluid compute: Don't put this client in a
|
|
6
|
+
* global variable. Always create a new client within each function when using
|
|
7
|
+
* it.
|
|
8
|
+
*/
|
|
9
|
+
export async function createClient() {
|
|
10
|
+
const cookieStore = await cookies();
|
|
11
|
+
|
|
12
|
+
return createServerClient(
|
|
13
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
14
|
+
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
|
|
15
|
+
{
|
|
16
|
+
cookies: {
|
|
17
|
+
getAll() {
|
|
18
|
+
return cookieStore.getAll();
|
|
19
|
+
},
|
|
20
|
+
setAll(cookiesToSet) {
|
|
21
|
+
try {
|
|
22
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
23
|
+
cookieStore.set(name, value, options),
|
|
24
|
+
);
|
|
25
|
+
} catch {
|
|
26
|
+
// The `setAll` method was called from a Server Component.
|
|
27
|
+
// This can be ignored if you have proxy refreshing
|
|
28
|
+
// user sessions.
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* ==============================
|
|
2
|
+
Utility functions for Supabase storage.
|
|
3
|
+
======================================
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createClient } from "./client";
|
|
7
|
+
|
|
8
|
+
export interface UploadResult {
|
|
9
|
+
url: string;
|
|
10
|
+
path: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DeleteResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Upload a file to Supabase storage.
|
|
20
|
+
* @param bucket - The name of the bucket to upload the file to.
|
|
21
|
+
* @param file - The file to upload.
|
|
22
|
+
* @param fileName - The name of the file to upload.
|
|
23
|
+
* @returns The URL of the uploaded file and the path of the file.
|
|
24
|
+
* @throws An error if the file could not be uploaded.
|
|
25
|
+
* @example
|
|
26
|
+
* const result = await uploadFile("my-bucket", "my-file.txt", "my-file.txt");
|
|
27
|
+
* if (result.url) {
|
|
28
|
+
* console.log("File uploaded successfully");
|
|
29
|
+
* } else {
|
|
30
|
+
* console.error(result.error);
|
|
31
|
+
* }
|
|
32
|
+
*/
|
|
33
|
+
export const uploadFile = async (
|
|
34
|
+
bucket: string,
|
|
35
|
+
file: File,
|
|
36
|
+
fileName: string
|
|
37
|
+
): Promise<{ url: string; path: string } | { error: string }> => {
|
|
38
|
+
try {
|
|
39
|
+
const supabase = createClient();
|
|
40
|
+
|
|
41
|
+
// Upload file to Supabase storage
|
|
42
|
+
const { error } = await supabase.storage.from(bucket).upload(fileName, file, {
|
|
43
|
+
cacheControl: "3600",
|
|
44
|
+
upsert: false,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (error) {
|
|
48
|
+
return { error: error.message };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { data: urlData } = supabase.storage.from(bucket).getPublicUrl(fileName);
|
|
52
|
+
|
|
53
|
+
return { url: urlData.publicUrl, path: fileName };
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return { error: error instanceof Error ? error.message : "Unknown error" };
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Delete a file from Supabase storage.
|
|
61
|
+
* @param bucket - The name of the bucket to delete the file from.
|
|
62
|
+
* @param fileName - The name of the file to delete.
|
|
63
|
+
* @returns True if the file was deleted successfully, false otherwise.
|
|
64
|
+
* @throws An error if the file could not be deleted.
|
|
65
|
+
* @example
|
|
66
|
+
* const result = await deleteFile("my-bucket", "my-file.txt");
|
|
67
|
+
* if (result.success) {
|
|
68
|
+
* console.log("File deleted successfully");
|
|
69
|
+
* } else {
|
|
70
|
+
* console.error(result.error);
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
73
|
+
export const deleteFile = async (
|
|
74
|
+
bucket: string,
|
|
75
|
+
fileName: string
|
|
76
|
+
): Promise<{ success: boolean } | { error: string }> => {
|
|
77
|
+
try {
|
|
78
|
+
const supabase = createClient();
|
|
79
|
+
|
|
80
|
+
const { error } = await supabase.storage.from(bucket).remove([fileName]);
|
|
81
|
+
|
|
82
|
+
if (error) {
|
|
83
|
+
return { error: error.message };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { success: true };
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return { error: error instanceof Error ? error.message : "Unknown error" };
|
|
89
|
+
}
|
|
90
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { streamText, UIMessage, convertToModelMessages } from 'ai';
|
|
2
|
+
|
|
3
|
+
export async function POST(req: Request) {
|
|
4
|
+
const { messages }: { messages: UIMessage[] } = await req.json();
|
|
5
|
+
|
|
6
|
+
const result = streamText({
|
|
7
|
+
model: "openai/gpt-5.2-chat-latest", // or any other model you want to use
|
|
8
|
+
messages: await convertToModelMessages(messages),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return result.toUIMessageStreamResponse();
|
|
12
|
+
}
|