@shipindays/shipindays 0.1.2

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.
@@ -0,0 +1,75 @@
1
+ // FILE: src/lib/auth/index.ts
2
+ // ROUTE: not a route — imported by middleware, dashboard, any protected page
3
+ // ROLE: Supabase Auth implementation
4
+ //
5
+ // INJECTED BY CLI when user picks "Supabase Auth"
6
+ // Replaces base placeholder at src/lib/auth/index.ts
7
+ //
8
+ // NO API route needed — Supabase handles sessions via cookies automatically.
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+
11
+ import { createServerClient } from "@supabase/ssr";
12
+ import { cookies } from "next/headers";
13
+ import { redirect } from "next/navigation";
14
+
15
+ // ─── Supabase server client ───────────────────────────────────────────────────
16
+ // Creates a Supabase client that reads/writes session cookies server-side.
17
+ // Called fresh on every request — cookies() gives us the current request's cookies.
18
+ export async function createClient() {
19
+ const cookieStore = await cookies();
20
+
21
+ return createServerClient(
22
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
23
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
24
+ {
25
+ cookies: {
26
+ getAll() { return cookieStore.getAll(); },
27
+ setAll(toSet) {
28
+ try {
29
+ toSet.forEach(({ name, value, options }) =>
30
+ cookieStore.set(name, value, options)
31
+ );
32
+ } catch {
33
+ // called from Server Component — safe to ignore
34
+ }
35
+ },
36
+ },
37
+ }
38
+ );
39
+ }
40
+
41
+ // ─── getCurrentUser ───────────────────────────────────────────────────────────
42
+ // Returns the logged-in user or null.
43
+ // Use this when you want to show different UI for logged-in vs logged-out users.
44
+ //
45
+ // Usage in a server component:
46
+ // const user = await getCurrentUser();
47
+ // if (user) { ... }
48
+ export async function getCurrentUser() {
49
+ const supabase = await createClient();
50
+ const { data: { user }, error } = await supabase.auth.getUser();
51
+ if (error || !user) return null;
52
+ return user;
53
+ }
54
+
55
+ // ─── requireUser ─────────────────────────────────────────────────────────────
56
+ // Returns the logged-in user OR redirects to /login.
57
+ // Use this on any protected page/route.
58
+ //
59
+ // Usage in a server component:
60
+ // const user = await requireUser(); // redirects if not logged in
61
+ // // user is guaranteed to exist here
62
+ export async function requireUser() {
63
+ const user = await getCurrentUser();
64
+ if (!user) redirect("/login");
65
+ return user;
66
+ }
67
+
68
+ // ─── signOut ──────────────────────────────────────────────────────────────────
69
+ // Signs the user out and redirects to home.
70
+ // Call this from a server action or API route.
71
+ export async function signOut() {
72
+ const supabase = await createClient();
73
+ await supabase.auth.signOut();
74
+ redirect("/");
75
+ }
@@ -0,0 +1,54 @@
1
+ // FILE: src/middleware.ts
2
+ // ROUTE: runs on every request before the page renders
3
+ // ROLE: Supabase Auth middleware
4
+ //
5
+ // INJECTED BY CLI when user picks "Supabase Auth"
6
+ // Replaces base placeholder at src/middleware.ts
7
+ //
8
+ // Does two things:
9
+ // 1. Refreshes the Supabase session cookie on every request (required by Supabase SSR)
10
+ // 2. Redirects unauthenticated users away from /dashboard to /login
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+
13
+ import { createServerClient } from "@supabase/ssr";
14
+ import { NextRequest, NextResponse } from "next/server";
15
+
16
+ export async function middleware(req: NextRequest) {
17
+ let res = NextResponse.next({ request: req });
18
+
19
+ // Create Supabase client that can read + refresh session cookies
20
+ const supabase = createServerClient(
21
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
22
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
23
+ {
24
+ cookies: {
25
+ getAll() { return req.cookies.getAll(); },
26
+ setAll(toSet) {
27
+ // Update cookies on both the request and response
28
+ toSet.forEach(({ name, value }) => req.cookies.set(name, value));
29
+ res = NextResponse.next({ request: req });
30
+ toSet.forEach(({ name, value, options }) =>
31
+ res.cookies.set(name, value, options)
32
+ );
33
+ },
34
+ },
35
+ }
36
+ );
37
+
38
+ // IMPORTANT: always call getUser() in middleware — this refreshes the session
39
+ const { data: { user } } = await supabase.auth.getUser();
40
+
41
+ // Protect /dashboard — redirect to /login if not authenticated
42
+ const isProtected = req.nextUrl.pathname.startsWith("/dashboard");
43
+ if (isProtected && !user) {
44
+ const loginUrl = new URL("/login", req.url);
45
+ loginUrl.searchParams.set("callbackUrl", req.nextUrl.pathname);
46
+ return NextResponse.redirect(loginUrl);
47
+ }
48
+
49
+ return res;
50
+ }
51
+
52
+ export const config = {
53
+ matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
54
+ };
@@ -0,0 +1,62 @@
1
+ // FILE: src/lib/email/index.ts
2
+ // ROUTE: not a route — imported anywhere that sends email
3
+ // ROLE: Nodemailer provider implementation
4
+ //
5
+ // INJECTED BY CLI when user picks "Nodemailer" as their email provider.
6
+ // Replaces templates/base/src/lib/email/index.ts
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+
9
+ import nodemailer from "nodemailer";
10
+
11
+ // Nodemailer transport — reads SMTP config from env
12
+ // Works with Gmail, Outlook, Mailgun SMTP, any SMTP server
13
+ const transporter = nodemailer.createTransport({
14
+ host: process.env.SMTP_HOST,
15
+ port: Number(process.env.SMTP_PORT ?? 587),
16
+ secure: process.env.SMTP_SECURE === "true",
17
+ auth: {
18
+ user: process.env.SMTP_USER,
19
+ pass: process.env.SMTP_PASS,
20
+ },
21
+ });
22
+
23
+ const FROM = process.env.SMTP_FROM ?? "you@yourdomain.com";
24
+
25
+ // ─── sendWelcomeEmail ─────────────────────────────────────────────────────────
26
+ export async function sendWelcomeEmail({ to, name }: { to: string; name: string }) {
27
+ await transporter.sendMail({
28
+ from: FROM,
29
+ to,
30
+ subject: "Welcome! 🎉",
31
+ html: `
32
+ <div style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:40px 20px">
33
+ <h1 style="font-size:24px;color:#111">Hey ${name}, welcome aboard!</h1>
34
+ <p style="color:#555;line-height:1.7">Your account is ready. Click below to get started.</p>
35
+ <a href="${process.env.NEXT_PUBLIC_APP_URL}/dashboard"
36
+ style="display:inline-block;margin-top:20px;padding:12px 24px;background:#111;color:#fff;border-radius:6px;text-decoration:none">
37
+ Go to Dashboard →
38
+ </a>
39
+ </div>
40
+ `,
41
+ });
42
+ }
43
+
44
+ // ─── sendPasswordResetEmail ───────────────────────────────────────────────────
45
+ export async function sendPasswordResetEmail({ to, resetUrl }: { to: string; resetUrl: string }) {
46
+ await transporter.sendMail({
47
+ from: FROM,
48
+ to,
49
+ subject: "Reset your password",
50
+ html: `
51
+ <div style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:40px 20px">
52
+ <h1 style="font-size:24px;color:#111">Reset your password</h1>
53
+ <p style="color:#555;line-height:1.7">Click below to reset your password. Link expires in 1 hour.</p>
54
+ <a href="${resetUrl}"
55
+ style="display:inline-block;margin-top:20px;padding:12px 24px;background:#111;color:#fff;border-radius:6px;text-decoration:none">
56
+ Reset Password →
57
+ </a>
58
+ <p style="margin-top:24px;color:#999;font-size:12px">If you didn't request this, ignore this email.</p>
59
+ </div>
60
+ `,
61
+ });
62
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "dependencies": {
3
+ "nodemailer": "^6.9.9"
4
+ },
5
+ "devDependencies": {
6
+ "@types/nodemailer": "^6.4.16"
7
+ }
8
+ }
@@ -0,0 +1,53 @@
1
+ // FILE: src/lib/email/index.ts
2
+ // ROUTE: not a route — imported anywhere that sends email
3
+ // ROLE: Resend provider implementation
4
+ //
5
+ // INJECTED BY CLI when user picks "Resend" as their email provider.
6
+ // Replaces templates/base/src/lib/email/index.ts
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+
9
+ import { Resend } from "resend";
10
+
11
+ // Resend client — reads API key from env
12
+ const resend = new Resend(process.env.RESEND_API_KEY);
13
+
14
+ const FROM = "You <hello@yourdomain.com>"; // TODO: replace with your verified Resend domain
15
+
16
+ // ─── sendWelcomeEmail ─────────────────────────────────────────────────────────
17
+ export async function sendWelcomeEmail({ to, name }: { to: string; name: string }) {
18
+ await resend.emails.send({
19
+ from: FROM,
20
+ to,
21
+ subject: "Welcome! 🎉",
22
+ html: `
23
+ <div style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:40px 20px">
24
+ <h1 style="font-size:24px;color:#111">Hey ${name}, welcome aboard!</h1>
25
+ <p style="color:#555;line-height:1.7">Your account is ready. Click below to get started.</p>
26
+ <a href="${process.env.NEXT_PUBLIC_APP_URL}/dashboard"
27
+ style="display:inline-block;margin-top:20px;padding:12px 24px;background:#111;color:#fff;border-radius:6px;text-decoration:none">
28
+ Go to Dashboard →
29
+ </a>
30
+ </div>
31
+ `,
32
+ });
33
+ }
34
+
35
+ // ─── sendPasswordResetEmail ───────────────────────────────────────────────────
36
+ export async function sendPasswordResetEmail({ to, resetUrl }: { to: string; resetUrl: string }) {
37
+ await resend.emails.send({
38
+ from: FROM,
39
+ to,
40
+ subject: "Reset your password",
41
+ html: `
42
+ <div style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:40px 20px">
43
+ <h1 style="font-size:24px;color:#111">Reset your password</h1>
44
+ <p style="color:#555;line-height:1.7">Click below to reset your password. Link expires in 1 hour.</p>
45
+ <a href="${resetUrl}"
46
+ style="display:inline-block;margin-top:20px;padding:12px 24px;background:#111;color:#fff;border-radius:6px;text-decoration:none">
47
+ Reset Password →
48
+ </a>
49
+ <p style="margin-top:24px;color:#999;font-size:12px">If you didn't request this, ignore this email.</p>
50
+ </div>
51
+ `,
52
+ });
53
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "resend": "^4.0.0"
4
+ }
5
+ }