@shipindays/shipindays 0.1.2 → 0.1.4
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.
Potentially problematic release.
This version of @shipindays/shipindays might be problematic. Click here for more details.
- package/README.md +1 -1
- package/index.js +281 -252
- package/package.json +1 -27
- package/templates/blocks/auth/nextauth/package.json +8 -8
- package/templates/blocks/auth/nextauth/src/app/api/auth/[...nextauth]/route.ts +19 -0
- package/templates/blocks/auth/nextauth/src/app/api/auth/login/route.ts +40 -0
- package/templates/blocks/auth/nextauth/src/app/api/auth/logout/route.ts +12 -0
- package/templates/blocks/auth/nextauth/src/app/api/auth/signup/route.ts +61 -0
- package/templates/blocks/auth/nextauth/src/app/layout.tsx +35 -0
- package/templates/blocks/auth/nextauth/src/app/types/next-auth.d.ts +18 -0
- package/templates/blocks/auth/nextauth/src/lib/auth/index.ts +141 -0
- package/templates/blocks/auth/nextauth/src/middleware.ts +40 -0
- package/templates/blocks/auth/nextauth/src/types/next-auth.d.ts +0 -0
- package/templates/blocks/auth/supabase/package.json +5 -5
- package/templates/blocks/auth/supabase/src/app/api/auth/login/route.ts +28 -0
- package/templates/blocks/auth/supabase/src/app/api/auth/logout/route.ts +13 -0
- package/templates/blocks/auth/supabase/src/app/api/auth/signup/route.ts +42 -0
- package/templates/blocks/auth/supabase/src/lib/auth/index.ts +31 -54
- package/templates/blocks/auth/supabase/src/lib/supabase/client.ts +30 -0
- package/templates/blocks/auth/supabase/src/lib/supabase/server.ts +40 -0
- package/templates/blocks/auth/supabase/src/middleware.ts +27 -14
- package/templates/blocks/email/resend/{index.ts → src/lib/resend/index.ts} +27 -15
- package/templates/blocks/auth/nextauth/app/api/auth/[...nextauth]/route.ts +0 -19
- package/templates/blocks/auth/nextauth/lib/auth/index.ts +0 -98
- package/templates/blocks/auth/nextauth/middleware.ts +0 -30
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// FILE: src/lib/supabase/client.ts
|
|
4
|
+
// ROUTE: not a route — imported by "use client" components ONLY
|
|
5
|
+
// ROLE: creates a browser-side Supabase singleton
|
|
6
|
+
//
|
|
7
|
+
// Use this in:
|
|
8
|
+
// - Client components (anything with "use client" at the top)
|
|
9
|
+
// - Login/signup forms
|
|
10
|
+
// - OAuth buttons
|
|
11
|
+
//
|
|
12
|
+
// NEVER use this in server components or API routes — use server.ts instead.
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
import { createBrowserClient } from "@supabase/ssr";
|
|
16
|
+
|
|
17
|
+
// Singleton — reuses the same instance across the app
|
|
18
|
+
// Prevents multiple GoTrue clients warning in dev
|
|
19
|
+
let client: ReturnType<typeof createBrowserClient> | undefined;
|
|
20
|
+
|
|
21
|
+
export function createSupabaseBrowserClient() {
|
|
22
|
+
if (client) return client;
|
|
23
|
+
|
|
24
|
+
client = createBrowserClient(
|
|
25
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
26
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return client;
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// FILE: src/lib/supabase/server.ts
|
|
2
|
+
// ROUTE: not a route — imported by server components and API routes ONLY
|
|
3
|
+
// ROLE: creates a Supabase client that reads session cookies server-side
|
|
4
|
+
//
|
|
5
|
+
// Use this in:
|
|
6
|
+
// - Server components (app/dashboard/page.tsx)
|
|
7
|
+
// - API routes (app/api/*)
|
|
8
|
+
// - middleware.ts
|
|
9
|
+
//
|
|
10
|
+
// NEVER use this in "use client" components — use client.ts instead.
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
import { createServerClient } from "@supabase/ssr";
|
|
14
|
+
import { cookies } from "next/headers";
|
|
15
|
+
|
|
16
|
+
export async function createSupabaseServerClient() {
|
|
17
|
+
const cookieStore = await cookies();
|
|
18
|
+
|
|
19
|
+
return createServerClient(
|
|
20
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
21
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
22
|
+
{
|
|
23
|
+
cookies: {
|
|
24
|
+
getAll() {
|
|
25
|
+
return cookieStore.getAll();
|
|
26
|
+
},
|
|
27
|
+
setAll(cookiesToSet) {
|
|
28
|
+
try {
|
|
29
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
30
|
+
cookieStore.set(name, value, options),
|
|
31
|
+
);
|
|
32
|
+
} catch {
|
|
33
|
+
// called from a Server Component — safe to ignore
|
|
34
|
+
// middleware handles cookie refresh instead
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// FILE: src/middleware.ts
|
|
2
|
-
// ROUTE: runs on every request before
|
|
2
|
+
// ROUTE: runs on every request before page renders
|
|
3
3
|
// ROLE: Supabase Auth middleware
|
|
4
4
|
//
|
|
5
5
|
// INJECTED BY CLI when user picks "Supabase Auth"
|
|
6
|
-
//
|
|
6
|
+
// Overwrites base placeholder at src/middleware.ts
|
|
7
7
|
//
|
|
8
8
|
// Does two things:
|
|
9
9
|
// 1. Refreshes the Supabase session cookie on every request (required by Supabase SSR)
|
|
@@ -16,29 +16,35 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
16
16
|
export async function middleware(req: NextRequest) {
|
|
17
17
|
let res = NextResponse.next({ request: req });
|
|
18
18
|
|
|
19
|
-
// Create Supabase client that can
|
|
19
|
+
// Create Supabase client that can refresh session cookies
|
|
20
20
|
const supabase = createServerClient(
|
|
21
21
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
22
22
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
23
23
|
{
|
|
24
24
|
cookies: {
|
|
25
|
-
getAll()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
getAll() {
|
|
26
|
+
return req.cookies.getAll();
|
|
27
|
+
},
|
|
28
|
+
setAll(cookiesToSet) {
|
|
29
|
+
cookiesToSet.forEach(({ name, value }) =>
|
|
30
|
+
req.cookies.set(name, value),
|
|
31
|
+
);
|
|
29
32
|
res = NextResponse.next({ request: req });
|
|
30
|
-
|
|
31
|
-
res.cookies.set(name, value, options)
|
|
33
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
34
|
+
res.cookies.set(name, value, options),
|
|
32
35
|
);
|
|
33
36
|
},
|
|
34
37
|
},
|
|
35
|
-
}
|
|
38
|
+
},
|
|
36
39
|
);
|
|
37
40
|
|
|
38
|
-
// IMPORTANT: always call getUser() in middleware
|
|
39
|
-
|
|
41
|
+
// IMPORTANT: always call getUser() in middleware
|
|
42
|
+
// This refreshes the session and keeps cookies up to date
|
|
43
|
+
const {
|
|
44
|
+
data: { user },
|
|
45
|
+
} = await supabase.auth.getUser();
|
|
40
46
|
|
|
41
|
-
// Protect /dashboard
|
|
47
|
+
// Protect /dashboard and all nested routes
|
|
42
48
|
const isProtected = req.nextUrl.pathname.startsWith("/dashboard");
|
|
43
49
|
if (isProtected && !user) {
|
|
44
50
|
const loginUrl = new URL("/login", req.url);
|
|
@@ -46,9 +52,16 @@ export async function middleware(req: NextRequest) {
|
|
|
46
52
|
return NextResponse.redirect(loginUrl);
|
|
47
53
|
}
|
|
48
54
|
|
|
55
|
+
// Redirect logged-in users away from /login and /signup
|
|
56
|
+
const isAuthPage =
|
|
57
|
+
req.nextUrl.pathname === "/login" || req.nextUrl.pathname === "/signup";
|
|
58
|
+
if (isAuthPage && user) {
|
|
59
|
+
return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
60
|
+
}
|
|
61
|
+
|
|
49
62
|
return res;
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
export const config = {
|
|
53
66
|
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
|
54
|
-
};
|
|
67
|
+
};
|
|
@@ -14,12 +14,18 @@ const resend = new Resend(process.env.RESEND_API_KEY);
|
|
|
14
14
|
const FROM = "You <hello@yourdomain.com>"; // TODO: replace with your verified Resend domain
|
|
15
15
|
|
|
16
16
|
// ─── sendWelcomeEmail ─────────────────────────────────────────────────────────
|
|
17
|
-
export async function sendWelcomeEmail({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
export async function sendWelcomeEmail({
|
|
18
|
+
to,
|
|
19
|
+
name,
|
|
20
|
+
}: {
|
|
21
|
+
to: string;
|
|
22
|
+
name: string;
|
|
23
|
+
}) {
|
|
24
|
+
await resend.emails.send({
|
|
25
|
+
from: FROM,
|
|
26
|
+
to,
|
|
27
|
+
subject: "Welcome! 🎉",
|
|
28
|
+
html: `
|
|
23
29
|
<div style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:40px 20px">
|
|
24
30
|
<h1 style="font-size:24px;color:#111">Hey ${name}, welcome aboard!</h1>
|
|
25
31
|
<p style="color:#555;line-height:1.7">Your account is ready. Click below to get started.</p>
|
|
@@ -29,16 +35,22 @@ export async function sendWelcomeEmail({ to, name }: { to: string; name: string
|
|
|
29
35
|
</a>
|
|
30
36
|
</div>
|
|
31
37
|
`,
|
|
32
|
-
|
|
38
|
+
});
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
// ─── sendPasswordResetEmail ───────────────────────────────────────────────────
|
|
36
|
-
export async function sendPasswordResetEmail({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
export async function sendPasswordResetEmail({
|
|
43
|
+
to,
|
|
44
|
+
resetUrl,
|
|
45
|
+
}: {
|
|
46
|
+
to: string;
|
|
47
|
+
resetUrl: string;
|
|
48
|
+
}) {
|
|
49
|
+
await resend.emails.send({
|
|
50
|
+
from: FROM,
|
|
51
|
+
to,
|
|
52
|
+
subject: "Reset your password",
|
|
53
|
+
html: `
|
|
42
54
|
<div style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:40px 20px">
|
|
43
55
|
<h1 style="font-size:24px;color:#111">Reset your password</h1>
|
|
44
56
|
<p style="color:#555;line-height:1.7">Click below to reset your password. Link expires in 1 hour.</p>
|
|
@@ -49,5 +61,5 @@ export async function sendPasswordResetEmail({ to, resetUrl }: { to: string; res
|
|
|
49
61
|
<p style="margin-top:24px;color:#999;font-size:12px">If you didn't request this, ignore this email.</p>
|
|
50
62
|
</div>
|
|
51
63
|
`,
|
|
52
|
-
|
|
53
|
-
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// FILE: src/app/api/auth/[...nextauth]/route.ts
|
|
2
|
-
// ROUTE: /api/auth/* (GET + POST)
|
|
3
|
-
// ROLE: NextAuth v5 route handler
|
|
4
|
-
//
|
|
5
|
-
// INJECTED BY CLI — only exists when user picks NextAuth.
|
|
6
|
-
// Supabase block does NOT have this file so it never gets created.
|
|
7
|
-
//
|
|
8
|
-
// Handles all NextAuth endpoints automatically:
|
|
9
|
-
// GET /api/auth/session
|
|
10
|
-
// GET /api/auth/csrf
|
|
11
|
-
// GET /api/auth/providers
|
|
12
|
-
// POST /api/auth/signin
|
|
13
|
-
// POST /api/auth/signout
|
|
14
|
-
// GET /api/auth/callback/:provider ← OAuth redirect comes back here
|
|
15
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
-
|
|
17
|
-
import { handlers } from "@/lib/auth";
|
|
18
|
-
|
|
19
|
-
export const { GET, POST } = handlers;
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
// FILE: src/lib/auth/index.ts
|
|
2
|
-
// ROUTE: not a route — imported by middleware, dashboard, API routes
|
|
3
|
-
// ROLE: NextAuth v5 implementation
|
|
4
|
-
//
|
|
5
|
-
// INJECTED BY CLI when user picks "NextAuth v5"
|
|
6
|
-
// Replaces base placeholder at src/lib/auth/index.ts
|
|
7
|
-
//
|
|
8
|
-
// NextAuth DOES need an API route: src/app/api/auth/[...nextauth]/route.ts
|
|
9
|
-
// That file is also included in this block and gets copied automatically.
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
import NextAuth from "next-auth";
|
|
13
|
-
import GitHub from "next-auth/providers/github";
|
|
14
|
-
import Google from "next-auth/providers/google";
|
|
15
|
-
import Credentials from "next-auth/providers/credentials";
|
|
16
|
-
import { redirect } from "next/navigation";
|
|
17
|
-
|
|
18
|
-
// ─── NextAuth config ──────────────────────────────────────────────────────────
|
|
19
|
-
// handlers → used by /api/auth/[...nextauth]/route.ts
|
|
20
|
-
// auth → used by middleware + server components to read the session
|
|
21
|
-
// signIn → used by login page
|
|
22
|
-
// signOut → used by logout button
|
|
23
|
-
export const { handlers, auth, signIn: nextAuthSignIn, signOut: nextAuthSignOut } = NextAuth({
|
|
24
|
-
providers: [
|
|
25
|
-
Google({
|
|
26
|
-
clientId: process.env.AUTH_GOOGLE_ID!,
|
|
27
|
-
clientSecret: process.env.AUTH_GOOGLE_SECRET!,
|
|
28
|
-
}),
|
|
29
|
-
GitHub({
|
|
30
|
-
clientId: process.env.AUTH_GITHUB_ID!,
|
|
31
|
-
clientSecret: process.env.AUTH_GITHUB_SECRET!,
|
|
32
|
-
}),
|
|
33
|
-
Credentials({
|
|
34
|
-
name: "credentials",
|
|
35
|
-
credentials: {
|
|
36
|
-
email: { label: "Email", type: "email" },
|
|
37
|
-
password: { label: "Password", type: "password" },
|
|
38
|
-
},
|
|
39
|
-
async authorize(credentials) {
|
|
40
|
-
// TODO: validate credentials against your database
|
|
41
|
-
// Example with your User model:
|
|
42
|
-
// const user = await User.findOne({ email: credentials.email })
|
|
43
|
-
// const valid = await bcrypt.compare(credentials.password, user.password)
|
|
44
|
-
// if (!valid) return null
|
|
45
|
-
// return { id: user._id, email: user.email, name: user.name }
|
|
46
|
-
return null;
|
|
47
|
-
},
|
|
48
|
-
}),
|
|
49
|
-
],
|
|
50
|
-
|
|
51
|
-
callbacks: {
|
|
52
|
-
// Attach user id to the JWT token so we can read it in session
|
|
53
|
-
async jwt({ token, user }) {
|
|
54
|
-
if (user) token.id = user.id;
|
|
55
|
-
return token;
|
|
56
|
-
},
|
|
57
|
-
// Attach user id from token to the session object
|
|
58
|
-
async session({ session, token }) {
|
|
59
|
-
if (token && session.user) session.user.id = token.id as string;
|
|
60
|
-
return session;
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
pages: {
|
|
65
|
-
signIn: "/login", // redirect here when auth is required
|
|
66
|
-
error: "/login", // redirect here on auth errors
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
session: { strategy: "jwt" },
|
|
70
|
-
secret: process.env.AUTH_SECRET,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
-
// THE CONTRACT — same 3 functions as Supabase block
|
|
75
|
-
// Rest of app calls these, never touches NextAuth directly
|
|
76
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
// ─── getCurrentUser ───────────────────────────────────────────────────────────
|
|
79
|
-
// Returns the logged-in user or null.
|
|
80
|
-
export async function getCurrentUser() {
|
|
81
|
-
const session = await auth();
|
|
82
|
-
if (!session?.user) return null;
|
|
83
|
-
return session.user;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ─── requireUser ─────────────────────────────────────────────────────────────
|
|
87
|
-
// Returns the logged-in user OR redirects to /login.
|
|
88
|
-
export async function requireUser() {
|
|
89
|
-
const user = await getCurrentUser();
|
|
90
|
-
if (!user) redirect("/login");
|
|
91
|
-
return user;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ─── signOut ──────────────────────────────────────────────────────────────────
|
|
95
|
-
// Signs the user out and redirects to home.
|
|
96
|
-
export async function signOut() {
|
|
97
|
-
await nextAuthSignOut({ redirectTo: "/" });
|
|
98
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// FILE: src/middleware.ts
|
|
2
|
-
// ROUTE: runs on every request before the page renders
|
|
3
|
-
// ROLE: NextAuth v5 middleware
|
|
4
|
-
//
|
|
5
|
-
// INJECTED BY CLI when user picks "NextAuth v5"
|
|
6
|
-
// Replaces base placeholder at src/middleware.ts
|
|
7
|
-
//
|
|
8
|
-
// Reads the NextAuth JWT and redirects unauthenticated users
|
|
9
|
-
// away from /dashboard to /login.
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
import { auth } from "@/lib/auth";
|
|
13
|
-
import { NextResponse } from "next/server";
|
|
14
|
-
|
|
15
|
-
export default auth((req) => {
|
|
16
|
-
const isLoggedIn = !!req.auth?.user;
|
|
17
|
-
const isProtected = req.nextUrl.pathname.startsWith("/dashboard");
|
|
18
|
-
|
|
19
|
-
if (isProtected && !isLoggedIn) {
|
|
20
|
-
const loginUrl = new URL("/login", req.url);
|
|
21
|
-
loginUrl.searchParams.set("callbackUrl", req.nextUrl.pathname);
|
|
22
|
-
return NextResponse.redirect(loginUrl);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return NextResponse.next();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export const config = {
|
|
29
|
-
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
|
30
|
-
};
|