create-solostack 1.3.4 → 1.3.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-solostack",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "The complete SaaS boilerplate for indie hackers - Next.js 15 with auth, payments, database, and email",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,6 +21,7 @@ async function generateSupabaseAuth(projectPath) {
21
21
  // Create auth directories
22
22
  await ensureDir(path.join(projectPath, 'src/app/auth/callback'));
23
23
  await ensureDir(path.join(projectPath, 'src/app/login'));
24
+ await ensureDir(path.join(projectPath, 'src/app/signup'));
24
25
  await ensureDir(path.join(projectPath, 'src/app/private'));
25
26
 
26
27
  // 1. Client Utility
@@ -73,7 +74,7 @@ export function createClient(cookieStore: ReturnType<typeof cookies>) {
73
74
  `;
74
75
  await writeFile(path.join(projectPath, 'src/utils/supabase/server.ts'), serverUtil);
75
76
 
76
- // 3. MacOS/Middleware Utility
77
+ // 3. Middleware Utility
77
78
  const middlewareUtil = `import { createServerClient, type CookieOptions } from '@supabase/ssr'
78
79
  import { NextResponse, type NextRequest } from 'next/server'
79
80
 
@@ -191,6 +192,7 @@ import { createClient } from '@/utils/supabase/client';
191
192
  import { useRouter } from 'next/navigation';
192
193
  import { useState } from 'react';
193
194
  import { Rocket } from 'lucide-react';
195
+ import Link from 'next/link';
194
196
 
195
197
  export default function LoginPage() {
196
198
  const router = useRouter();
@@ -301,6 +303,13 @@ export default function LoginPage() {
301
303
  {loading ? 'Signing in...' : 'Sign In'}
302
304
  </button>
303
305
  </form>
306
+
307
+ <div className="text-center text-sm">
308
+ <span className="text-zinc-500">Don't have an account? </span>
309
+ <Link href="/signup" className="font-medium text-indigo-400 hover:text-indigo-300">
310
+ Sign up
311
+ </Link>
312
+ </div>
304
313
  </div>
305
314
  </div>
306
315
  );
@@ -308,6 +317,150 @@ export default function LoginPage() {
308
317
  `;
309
318
 
310
319
  await writeFile(path.join(projectPath, 'src/app/login/page.tsx'), loginPage);
320
+
321
+ // 7. Signup Page (Supabase)
322
+ const signupPage = `'use client';
323
+ import { createClient } from '@/utils/supabase/client';
324
+ import { useRouter } from 'next/navigation';
325
+ import { useState } from 'react';
326
+ import { Rocket } from 'lucide-react';
327
+ import Link from 'next/link';
328
+
329
+ export default function SignupPage() {
330
+ const router = useRouter();
331
+ const [email, setEmail] = useState('');
332
+ const [password, setPassword] = useState('');
333
+ const [loading, setLoading] = useState(false);
334
+ const [message, setMessage] = useState('');
335
+
336
+ const supabase = createClient();
337
+
338
+ const handleEmailSignup = async (e: React.FormEvent) => {
339
+ e.preventDefault();
340
+ setLoading(true);
341
+ setMessage('');
342
+
343
+ const { error } = await supabase.auth.signUp({
344
+ email,
345
+ password,
346
+ options: {
347
+ emailRedirectTo: \`\${location.origin}/auth/callback\`,
348
+ },
349
+ });
350
+
351
+ if (error) {
352
+ setMessage(error.message);
353
+ } else {
354
+ setMessage('Check your email for the confirmation link!');
355
+ }
356
+ setLoading(false);
357
+ };
358
+
359
+ const handleGitHubLogin = async () => {
360
+ await supabase.auth.signInWithOAuth({
361
+ provider: 'github',
362
+ options: {
363
+ redirectTo: \`\${location.origin}/auth/callback\`,
364
+ },
365
+ });
366
+ };
367
+
368
+ const handleGoogleLogin = async () => {
369
+ await supabase.auth.signInWithOAuth({
370
+ provider: 'google',
371
+ options: {
372
+ redirectTo: \`\${location.origin}/auth/callback\`,
373
+ },
374
+ });
375
+ };
376
+
377
+ return (
378
+ <div className="flex min-h-screen flex-col items-center justify-center bg-zinc-950 p-4 text-white">
379
+ <div className="w-full max-w-sm space-y-8 rounded-xl border border-zinc-800 bg-zinc-900/50 p-8 shadow-xl backdrop-blur-xl">
380
+ <div className="text-center">
381
+ <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-indigo-600">
382
+ <Rocket className="h-6 w-6 text-white" />
383
+ </div>
384
+ <h2 className="text-2xl font-bold tracking-tight">Create account</h2>
385
+ <p className="mt-2 text-sm text-zinc-400">Sign up to get started</p>
386
+ </div>
387
+
388
+ <div className="space-y-4">
389
+ <button
390
+ onClick={handleGitHubLogin}
391
+ className="w-full rounded-md border border-zinc-700 bg-zinc-800 py-2 text-sm font-medium hover:bg-zinc-700 transition-colors"
392
+ >
393
+ Continue with GitHub
394
+ </button>
395
+ <button
396
+ onClick={handleGoogleLogin}
397
+ className="w-full rounded-md border border-zinc-700 bg-zinc-800 py-2 text-sm font-medium hover:bg-zinc-700 transition-colors"
398
+ >
399
+ Continue with Google
400
+ </button>
401
+ </div>
402
+
403
+ <div className="relative">
404
+ <div className="absolute inset-0 flex items-center">
405
+ <div className="w-full border-t border-zinc-800" />
406
+ </div>
407
+ <div className="relative flex justify-center text-xs uppercase">
408
+ <span className="bg-zinc-900 px-2 text-zinc-500">Or continue with</span>
409
+ </div>
410
+ </div>
411
+
412
+ <form onSubmit={handleEmailSignup} className="space-y-4">
413
+ <div>
414
+ <label className="mb-2 block text-sm font-medium text-zinc-400">Email</label>
415
+ <input
416
+ type="email"
417
+ value={email}
418
+ onChange={(e) => setEmail(e.target.value)}
419
+ className="w-full rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm placeholder:text-zinc-600 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
420
+ required
421
+ />
422
+ </div>
423
+ <div>
424
+ <label className="mb-2 block text-sm font-medium text-zinc-400">Password</label>
425
+ <input
426
+ type="password"
427
+ value={password}
428
+ onChange={(e) => setPassword(e.target.value)}
429
+ className="w-full rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm placeholder:text-zinc-600 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
430
+ placeholder="Min 6 characters"
431
+ minLength={6}
432
+ required
433
+ />
434
+ </div>
435
+
436
+ {message && (
437
+ <p className={message.includes('Check') ? 'text-sm text-green-400' : 'text-sm text-red-400'}>
438
+ {message}
439
+ </p>
440
+ )}
441
+
442
+ <button
443
+ type="submit"
444
+ disabled={loading}
445
+ className="w-full rounded-md bg-indigo-600 py-2 text-sm font-medium text-white hover:bg-indigo-500 disabled:opacity-50"
446
+ >
447
+ {loading ? 'Creating account...' : 'Sign Up'}
448
+ </button>
449
+ </form>
450
+
451
+ <div className="text-center text-sm">
452
+ <span className="text-zinc-500">Already have an account? </span>
453
+ <Link href="/login" className="font-medium text-indigo-400 hover:text-indigo-300">
454
+ Sign in
455
+ </Link>
456
+ </div>
457
+ </div>
458
+ </div>
459
+ );
460
+ }
461
+ `;
462
+
463
+ await writeFile(path.join(projectPath, 'src/app/signup/page.tsx'), signupPage);
311
464
  }
312
465
 
313
466
  async function generateNextAuth(projectPath) {
package/src/index.js CHANGED
@@ -53,7 +53,7 @@ export async function main() {
53
53
  ╚════════════════════════════════════════════════════════════════╝
54
54
  `));
55
55
 
56
- console.log(chalk.gray(` Version ${orangeBright('1.3.4')} • Built by ${orangeBright('Danish Akhtar')} • ${orangeBright('github.com/danish296')}\n`));
56
+ console.log(chalk.gray(` Version ${orangeBright('1.3.5')} • Built by ${orangeBright('Danish Akhtar')} • ${orangeBright('github.com/danish296')}\n`));
57
57
 
58
58
  // Parse command line arguments
59
59
  program