@alinsafawi/aegis-auth 0.1.8 → 0.2.0

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.
Files changed (2) hide show
  1. package/dist/index.js +137 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23443,6 +23443,60 @@ export default config
23443
23443
  }
23444
23444
 
23445
23445
  // src/generators/env.ts
23446
+ function generateEnvFile(cookiePrefix, infra, features) {
23447
+ const lines = [
23448
+ `# \u2500\u2500\u2500 Aegis Auth \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
23449
+ `# Fill in every value before running: npx prisma migrate dev --name init`,
23450
+ ``,
23451
+ `# Generate with (PowerShell): [Convert]::ToBase64String((1..32|%{[byte](Get-Random -Max 256)}))`,
23452
+ `# Generate with (Unix): openssl rand -base64 32`,
23453
+ `AEGIS_JWT_SECRET=`,
23454
+ ``,
23455
+ `# Set to the previous secret when rotating \u2014 gives active sessions a grace period`,
23456
+ `# AEGIS_JWT_SECRET_PREVIOUS=`,
23457
+ ``,
23458
+ `# \u2500\u2500\u2500 Database \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
23459
+ `# Neon: postgresql://user:pass@host/db?sslmode=require`,
23460
+ `# Supabase: postgresql://postgres:pass@db.xxx.supabase.co:5432/postgres`,
23461
+ `# Local: postgresql://postgres:password@localhost:5432/mydb`,
23462
+ `DATABASE_URL=`,
23463
+ ``
23464
+ ];
23465
+ if (features.emailVerification || features.passwordReset || features.accountLockout) {
23466
+ lines.push(
23467
+ `# \u2500\u2500\u2500 Email (SMTP) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
23468
+ `# Works with Resend, Sendgrid, Postmark, Gmail, etc.`
23469
+ );
23470
+ if (infra.setupSmtp && infra.smtpHost) {
23471
+ lines.push(
23472
+ `AEGIS_SMTP_HOST=${infra.smtpHost}`,
23473
+ `AEGIS_SMTP_PORT=${infra.smtpPort ?? 587}`,
23474
+ `AEGIS_SMTP_USER=${infra.smtpUser ?? ""}`,
23475
+ `AEGIS_SMTP_PASS=`,
23476
+ `AEGIS_SMTP_FROM=${infra.smtpFrom ?? ""}`
23477
+ );
23478
+ } else {
23479
+ lines.push(
23480
+ `AEGIS_SMTP_HOST=`,
23481
+ `AEGIS_SMTP_PORT=587`,
23482
+ `AEGIS_SMTP_USER=`,
23483
+ `AEGIS_SMTP_PASS=`,
23484
+ `AEGIS_SMTP_FROM=`
23485
+ );
23486
+ }
23487
+ lines.push(``);
23488
+ }
23489
+ if (infra.rateLimitProvider === "upstash") {
23490
+ lines.push(
23491
+ `# \u2500\u2500\u2500 Upstash Redis (rate limiting) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
23492
+ `# Get these from upstash.com \u2014 free tier available`,
23493
+ `UPSTASH_REDIS_REST_URL=`,
23494
+ `UPSTASH_REDIS_REST_TOKEN=`,
23495
+ ``
23496
+ );
23497
+ }
23498
+ return lines.join("\n");
23499
+ }
23446
23500
  function generateEnvExample(cookiePrefix, infra, features) {
23447
23501
  const lines = [
23448
23502
  `# \u2500\u2500\u2500 Aegis Auth \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
@@ -23553,11 +23607,12 @@ export default function RootPage() {
23553
23607
  }
23554
23608
  function generateMiddleware(cookiePrefix, language) {
23555
23609
  return `import { createAuthMiddleware } from '@alinsafawi/aegis-auth-next'
23556
- import config from './auth.config'
23610
+ import authConfig from './auth.config'
23557
23611
 
23558
- export default createAuthMiddleware(config)
23612
+ export default createAuthMiddleware(authConfig)
23559
23613
 
23560
23614
  export const config = {
23615
+ runtime: 'nodejs',
23561
23616
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
23562
23617
  }
23563
23618
  `;
@@ -23672,7 +23727,11 @@ export default function LoginPage() {
23672
23727
  function generateNextConfig() {
23673
23728
  return `import type { NextConfig } from 'next'
23674
23729
 
23675
- const nextConfig: NextConfig = {}
23730
+ const nextConfig: NextConfig = {
23731
+ experimental: {
23732
+ nodeMiddleware: true,
23733
+ },
23734
+ }
23676
23735
 
23677
23736
  export default nextConfig
23678
23737
  `;
@@ -23710,6 +23769,7 @@ function generatePackageJson(name, packageManager) {
23710
23769
  "react-dom": "^19.0.0"
23711
23770
  },
23712
23771
  devDependencies: {
23772
+ "@alinsafawi/aegis-auth": "latest",
23713
23773
  "@types/node": "^20.0.0",
23714
23774
  "@types/react": "^19.0.0",
23715
23775
  "@types/react-dom": "^19.0.0",
@@ -23939,7 +23999,8 @@ dist
23939
23999
  const e = language === "typescript" ? "tsx" : "jsx";
23940
24000
  await writeFile(`src/app/(auth)/login/page.${e}`, generateLoginPage(app.appName, style.primaryColor, language));
23941
24001
  });
23942
- await step("Updating .env.example", async () => {
24002
+ await step("Creating .env and .env.example", async () => {
24003
+ await writeFile(".env", generateEnvFile(app.cookiePrefix, infra, features));
23943
24004
  await writeFile(".env.example", generateEnvExample(app.cookiePrefix, infra, features));
23944
24005
  });
23945
24006
  if (!isDry && mode === "new") {
@@ -23957,13 +24018,13 @@ dist
23957
24018
  }] : [],
23958
24019
  {
23959
24020
  n: 1,
23960
- title: "Fill in .env.local",
24021
+ title: `Fill in ${import_chalk8.default.yellow(".env")} ${import_chalk8.default.dim("(created for you \u2014 open it and add your values)")}`,
23961
24022
  lines: [
23962
- `${import_chalk8.default.cyan("AEGIS_JWT_SECRET=")} ${import_chalk8.default.dim(process.platform === "win32" ? "\u2190 [Convert]::ToBase64String((1..32|%{[byte](Get-Random -Max 256)}))" : "\u2190 openssl rand -base64 32")}`,
23963
- `${import_chalk8.default.cyan("DATABASE_URL=")}`,
23964
- features.emailVerification || features.passwordReset ? `${import_chalk8.default.cyan("AEGIS_SMTP_HOST=")}` : "",
23965
- infra.rateLimitProvider === "upstash" ? `${import_chalk8.default.cyan("UPSTASH_REDIS_REST_URL=")}` : ""
23966
- ].filter(Boolean)
24023
+ `${import_chalk8.default.cyan("DATABASE_URL=")} ${import_chalk8.default.dim("\u2190 your Postgres connection string (Neon, Supabase, local)")}`,
24024
+ `${import_chalk8.default.cyan("AEGIS_JWT_SECRET=")} ${import_chalk8.default.dim(process.platform === "win32" ? "\u2190 [Convert]::ToBase64String((1..32|%{[byte](Get-Random -Max 256)}))" : "\u2190 openssl rand -base64 32")}`,
24025
+ ...features.emailVerification || features.passwordReset ? [`${import_chalk8.default.cyan("AEGIS_SMTP_HOST=")} ${import_chalk8.default.dim("\u2190 and SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM")}`] : [],
24026
+ ...infra.rateLimitProvider === "upstash" ? [`${import_chalk8.default.cyan("UPSTASH_REDIS_REST_URL=")} ${import_chalk8.default.dim("\u2190 and UPSTASH_REDIS_REST_TOKEN")}`] : []
24027
+ ]
23967
24028
  },
23968
24029
  {
23969
24030
  n: 2,
@@ -24151,20 +24212,34 @@ async function runSeed() {
24151
24212
  p9.cancel("No auth.config.ts found. Run: npx aegis-auth init first.");
24152
24213
  process.exit(1);
24153
24214
  }
24154
- console.log(
24155
- `
24156
- ${import_chalk12.default.dim("This creates the first user account in your database.")}
24157
- `
24158
- );
24215
+ const hasPrisma = import_fs_extra5.default.existsSync(import_path5.default.join(cwd, "node_modules", ".prisma", "client"));
24216
+ if (!hasPrisma) {
24217
+ p9.cancel("Prisma client not generated. Run: npx prisma generate first.");
24218
+ process.exit(1);
24219
+ }
24220
+ console.log(`
24221
+ ${import_chalk12.default.dim("Creates the first user account in your database.")}
24222
+ `);
24159
24223
  const role = await p9.text({
24160
- message: "Role ID to seed",
24224
+ message: "Role ID",
24161
24225
  placeholder: "admin",
24162
24226
  validate: (v) => !v ? "Required" : void 0
24163
24227
  });
24164
24228
  if (p9.isCancel(role)) process.exit(0);
24229
+ const loginField = await p9.select({
24230
+ message: "Login field for this role",
24231
+ options: [
24232
+ { value: "email", label: "email" },
24233
+ { value: "username", label: "username" }
24234
+ ]
24235
+ });
24236
+ if (p9.isCancel(loginField)) process.exit(0);
24165
24237
  const identifier = await p9.text({
24166
- message: "Email or username",
24167
- validate: (v) => !v ? "Required" : void 0
24238
+ message: loginField === "email" ? "Email address" : "Username",
24239
+ validate: (v) => {
24240
+ if (!v) return "Required";
24241
+ if (loginField === "email" && !v.includes("@")) return "Enter a valid email address";
24242
+ }
24168
24243
  });
24169
24244
  if (p9.isCancel(identifier)) process.exit(0);
24170
24245
  const password3 = await p9.password({
@@ -24172,25 +24247,50 @@ async function runSeed() {
24172
24247
  validate: (v) => v.length < 8 ? "At least 8 characters" : void 0
24173
24248
  });
24174
24249
  if (p9.isCancel(password3)) process.exit(0);
24175
- console.log();
24176
- console.log(
24177
- ` ${import_chalk12.default.yellow("\u26A0")} ${import_chalk12.default.dim("Seed functionality requires your app to be running with Prisma connected.")}`
24178
- );
24179
- console.log(
24180
- ` ${import_chalk12.default.dim("Add this to a script in your project or run via ts-node:")}`
24181
- );
24182
- console.log();
24183
- console.log(
24184
- import_chalk12.default.dim(
24185
- ` import { PrismaClient } from '@prisma/client'
24186
- import { hashPassword } from '@alinsafawi/aegis-auth-core'
24187
- const prisma = new PrismaClient()
24188
- await prisma.${role}.create({
24189
- data: { email: '${identifier}', passwordHash: await hashPassword('${password3}') }
24190
- })`
24191
- )
24192
- );
24193
- console.log();
24250
+ const spin = p9.spinner();
24251
+ spin.start("Creating user\u2026");
24252
+ try {
24253
+ const envPath = import_path5.default.join(cwd, ".env");
24254
+ if (import_fs_extra5.default.existsSync(envPath)) {
24255
+ const raw = import_fs_extra5.default.readFileSync(envPath, "utf8");
24256
+ for (const line of raw.split("\n")) {
24257
+ const trimmed = line.trim();
24258
+ if (!trimmed || trimmed.startsWith("#")) continue;
24259
+ const eq = trimmed.indexOf("=");
24260
+ if (eq === -1) continue;
24261
+ const key = trimmed.slice(0, eq).trim();
24262
+ const val = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
24263
+ if (!process.env[key]) process.env[key] = val;
24264
+ }
24265
+ }
24266
+ const { PrismaClient } = require(import_path5.default.join(cwd, "node_modules", ".prisma", "client"));
24267
+ const { hashPassword } = require(import_path5.default.join(cwd, "node_modules", "@alinsafawi", "aegis-auth-core", "dist", "index.js"));
24268
+ const prisma = new PrismaClient();
24269
+ const modelKey = role.charAt(0).toLowerCase() + role.slice(1);
24270
+ const passwordHash = await hashPassword(password3);
24271
+ await prisma[modelKey].create({
24272
+ data: {
24273
+ [loginField]: identifier,
24274
+ passwordHash
24275
+ }
24276
+ });
24277
+ await prisma.$disconnect();
24278
+ spin.stop(`${import_chalk12.default.green("\u2713")} User created`);
24279
+ console.log();
24280
+ console.log(` ${import_chalk12.default.bold("Role")} ${import_chalk12.default.magentaBright(role)}`);
24281
+ console.log(` ${import_chalk12.default.bold(loginField)} ${import_chalk12.default.cyan(identifier)}`);
24282
+ console.log();
24283
+ } catch (err) {
24284
+ spin.stop(`${import_chalk12.default.red("\u2717")} Failed`);
24285
+ console.log();
24286
+ if (err?.code === "P2002") {
24287
+ console.log(` ${import_chalk12.default.red("A user with that ${loginField} already exists.")}`);
24288
+ } else {
24289
+ console.log(` ${import_chalk12.default.red(err?.message ?? String(err))}`);
24290
+ }
24291
+ console.log();
24292
+ process.exit(1);
24293
+ }
24194
24294
  }
24195
24295
 
24196
24296
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alinsafawi/aegis-auth",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "The shield your Next.js app deserves — full-stack auth in minutes",
5
5
  "bin": {
6
6
  "aegis-auth": "dist/index.js"