create-apppaaaul 2.0.15 → 2.0.17

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 (47) hide show
  1. package/dist/index.js +2 -4
  2. package/dist/index.js.map +1 -1
  3. package/dist/templates/nextjs-ts-clean/project/%%.gitignore +43 -0
  4. package/dist/templates/nextjs-ts-clean/project/eslint.config.mjs +137 -114
  5. package/dist/templates/nextjs-ts-clean/project/package.json +15 -15
  6. package/dist/templates/nextjs-ts-clean/project/src/scripts/dev-rebase.js +12 -2
  7. package/dist/templates/nextjs-ts-landing-prisma/project/%%.gitignore +46 -0
  8. package/dist/templates/nextjs-ts-landing-prisma/project/.env.test +26 -0
  9. package/dist/templates/nextjs-ts-landing-prisma/project/eslint.config.mjs +137 -114
  10. package/dist/templates/nextjs-ts-landing-prisma/project/package.json +34 -26
  11. package/dist/templates/nextjs-ts-landing-prisma/project/prisma/schema.prisma +74 -14
  12. package/dist/templates/nextjs-ts-landing-prisma/project/src/app/api/auth/[...all]/route.ts +5 -0
  13. package/dist/templates/nextjs-ts-landing-prisma/project/src/app/not-found.tsx +118 -0
  14. package/dist/templates/nextjs-ts-landing-prisma/project/src/lib/auth-client.ts +9 -0
  15. package/dist/templates/nextjs-ts-landing-prisma/project/src/lib/auth-utils.ts +197 -0
  16. package/dist/templates/nextjs-ts-landing-prisma/project/src/lib/auth.ts +49 -0
  17. package/dist/templates/nextjs-ts-landing-prisma/project/src/lib/cloudflare-r2.ts +124 -0
  18. package/dist/templates/nextjs-ts-landing-prisma/project/src/lib/db.ts +24 -5
  19. package/dist/templates/nextjs-ts-landing-prisma/project/src/lib/env.ts +44 -0
  20. package/dist/templates/nextjs-ts-landing-prisma/project/src/middleware.ts +105 -0
  21. package/dist/templates/nextjs-ts-landing-prisma/project/src/scripts/db-seed.ts +16 -0
  22. package/dist/templates/nextjs-ts-landing-prisma/project/src/scripts/dev-rebase.js +12 -2
  23. package/package.json +1 -1
  24. package/dist/templates/nextjs-ts-landing-drizzle/project/README.md +0 -15
  25. package/dist/templates/nextjs-ts-landing-drizzle/project/components.json +0 -21
  26. package/dist/templates/nextjs-ts-landing-drizzle/project/drizzle.config.ts +0 -11
  27. package/dist/templates/nextjs-ts-landing-drizzle/project/eslint.config.mjs +0 -183
  28. package/dist/templates/nextjs-ts-landing-drizzle/project/next.config.mjs +0 -10
  29. package/dist/templates/nextjs-ts-landing-drizzle/project/package.json +0 -61
  30. package/dist/templates/nextjs-ts-landing-drizzle/project/postcss.config.js +0 -3
  31. package/dist/templates/nextjs-ts-landing-drizzle/project/public/next.svg +0 -1
  32. package/dist/templates/nextjs-ts-landing-drizzle/project/public/vercel.svg +0 -1
  33. package/dist/templates/nextjs-ts-landing-drizzle/project/src/app/api/auth/[...nextauth]/route.ts +0 -3
  34. package/dist/templates/nextjs-ts-landing-drizzle/project/src/app/favicon.ico +0 -0
  35. package/dist/templates/nextjs-ts-landing-drizzle/project/src/app/globals.css +0 -161
  36. package/dist/templates/nextjs-ts-landing-drizzle/project/src/app/layout.tsx +0 -25
  37. package/dist/templates/nextjs-ts-landing-drizzle/project/src/app/page.tsx +0 -5
  38. package/dist/templates/nextjs-ts-landing-drizzle/project/src/auth.ts +0 -79
  39. package/dist/templates/nextjs-ts-landing-drizzle/project/src/components/ui/button.tsx +0 -49
  40. package/dist/templates/nextjs-ts-landing-drizzle/project/src/db/index.ts +0 -25
  41. package/dist/templates/nextjs-ts-landing-drizzle/project/src/db/schema.ts +0 -93
  42. package/dist/templates/nextjs-ts-landing-drizzle/project/src/lib/utils.ts +0 -6
  43. package/dist/templates/nextjs-ts-landing-drizzle/project/src/scripts/dev-rebase.js +0 -32
  44. package/dist/templates/nextjs-ts-landing-drizzle/project/tailwind.config.ts +0 -80
  45. package/dist/templates/nextjs-ts-landing-drizzle/project/tsconfig.json +0 -27
  46. package/dist/templates/nextjs-ts-landing-prisma/project/src/app/api/auth/[...nextauth]/route.ts +0 -3
  47. package/dist/templates/nextjs-ts-landing-prisma/project/src/auth.ts +0 -31
@@ -0,0 +1,197 @@
1
+ import { headers } from "next/headers";
2
+
3
+ import { auth } from "./auth";
4
+ import { rateLimiters } from "./rate-limit";
5
+
6
+ export interface AuthUser {
7
+ id: string;
8
+ email: string;
9
+ role: string;
10
+ banned: boolean;
11
+ }
12
+
13
+ export interface AuthResult {
14
+ success: boolean;
15
+ user?: AuthUser;
16
+ error?: string;
17
+ }
18
+
19
+ export interface AuthorizationOptions {
20
+ requireAuth?: boolean;
21
+ allowedRoles?: string[];
22
+ allowSelf?: boolean; // Para permitir acceso a recursos propios
23
+ resourceOwnerId?: string; // ID del propietario del recurso
24
+ }
25
+
26
+ /**
27
+ * Verifica la autenticación del usuario actual
28
+ */
29
+ export async function verifyAuth(): Promise<AuthResult> {
30
+ try {
31
+ const session = await auth.api.getSession({
32
+ headers: await headers(),
33
+ });
34
+
35
+ if (!session?.user) {
36
+ return {
37
+ success: false,
38
+ error: "Authentication required",
39
+ };
40
+ }
41
+
42
+ // Verificar si el usuario está baneado
43
+ if (session.user.banned) {
44
+ return {
45
+ success: false,
46
+ error: "Account banned",
47
+ };
48
+ }
49
+
50
+ return {
51
+ success: true,
52
+ user: {
53
+ id: session.user.id,
54
+ email: session.user.email,
55
+ role: session.user.role ?? "user",
56
+ banned: session.user.banned ?? false,
57
+ },
58
+ };
59
+ } catch (error) {
60
+ console.error("Authentication error:", error);
61
+
62
+ return {
63
+ success: false,
64
+ error: "Authentication failed",
65
+ };
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Verifica autorización basada en opciones
71
+ */
72
+ export async function verifyAuthorization(options: AuthorizationOptions = {}): Promise<AuthResult> {
73
+ const { requireAuth = true, allowedRoles = [], allowSelf = false, resourceOwnerId } = options;
74
+
75
+ if (!requireAuth) {
76
+ return { success: true };
77
+ }
78
+
79
+ const authResult = await verifyAuth();
80
+
81
+ if (!authResult.success || !authResult.user) {
82
+ return authResult;
83
+ }
84
+
85
+ const user = authResult.user;
86
+
87
+ // Verificar roles permitidos
88
+ if (allowedRoles.length > 0 && !allowedRoles.includes(user.role)) {
89
+ // Si se permite acceso propio, verificar si es el propietario del recurso
90
+ if (allowSelf && resourceOwnerId && user.id === resourceOwnerId) {
91
+ return { success: true, user };
92
+ }
93
+
94
+ return {
95
+ success: false,
96
+ error: "Insufficient permissions",
97
+ };
98
+ }
99
+
100
+ return { success: true, user };
101
+ }
102
+
103
+ /**
104
+ * Aplica rate limiting basado en el usuario
105
+ */
106
+ export function checkRateLimit(
107
+ limitType: keyof typeof rateLimiters,
108
+ userId?: string,
109
+ ): { success: boolean; error?: string } {
110
+ const identifier = userId ?? "anonymous";
111
+ const result = rateLimiters[limitType].check(identifier);
112
+
113
+ if (!result.success) {
114
+ return {
115
+ success: false,
116
+ error: "Rate limit exceeded. Please try again later.",
117
+ };
118
+ }
119
+
120
+ return { success: true };
121
+ }
122
+
123
+ /**
124
+ * Middleware de seguridad combinado para server actions
125
+ */
126
+ export function withSecurityMiddleware<T extends unknown[], R>(
127
+ action: (...args: T) => Promise<R>,
128
+ options: AuthorizationOptions & {
129
+ rateLimitType?: keyof typeof rateLimiters;
130
+ } = {},
131
+ ): (...args: T) => Promise<R> {
132
+ return async (...args: T): Promise<R> => {
133
+ // Verificar autorización
134
+ const authResult = await verifyAuthorization(options);
135
+
136
+ if (!authResult.success) {
137
+ throw new Error(authResult.error ?? "Authorization failed");
138
+ }
139
+
140
+ // Aplicar rate limiting si está especificado
141
+ if (options.rateLimitType && authResult.user) {
142
+ const rateLimitResult = checkRateLimit(options.rateLimitType, authResult.user.id);
143
+
144
+ if (!rateLimitResult.success) {
145
+ throw new Error(rateLimitResult.error ?? "Rate limit exceeded");
146
+ }
147
+ }
148
+
149
+ // Ejecutar la acción
150
+ return await action(...args);
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Verifica si el usuario es admin
156
+ */
157
+ export async function requireAdmin(): Promise<AuthUser> {
158
+ const result = await verifyAuthorization({
159
+ allowedRoles: ["admin"],
160
+ });
161
+
162
+ if (!result.success || !result.user) {
163
+ throw new Error(result.error ?? "Admin access required");
164
+ }
165
+
166
+ return result.user;
167
+ }
168
+
169
+ /**
170
+ * Verifica si el usuario está autenticado
171
+ */
172
+ export async function requireAuth(): Promise<AuthUser> {
173
+ const result = await verifyAuth();
174
+
175
+ if (!result.success || !result.user) {
176
+ throw new Error(result.error ?? "Authentication required");
177
+ }
178
+
179
+ return result.user;
180
+ }
181
+
182
+ /**
183
+ * Verifica si el usuario puede acceder a un recurso (admin o propietario)
184
+ */
185
+ export async function requireOwnershipOrAdmin(resourceOwnerId: string): Promise<AuthUser> {
186
+ const result = await verifyAuthorization({
187
+ allowedRoles: ["admin"],
188
+ allowSelf: true,
189
+ resourceOwnerId,
190
+ });
191
+
192
+ if (!result.success || !result.user) {
193
+ throw new Error(result.error ?? "Access denied");
194
+ }
195
+
196
+ return result.user;
197
+ }
@@ -0,0 +1,49 @@
1
+ import { betterAuth } from "better-auth";
2
+ import { prismaAdapter } from "better-auth/adapters/prisma";
3
+ import { emailOTP, admin } from "better-auth/plugins";
4
+ import { PrismaClient } from "@prisma/client";
5
+ import { Resend } from "resend";
6
+ import { env } from "./env";
7
+
8
+ const prisma = new PrismaClient();
9
+ const resend = new Resend(env.RESEND_API_KEY);
10
+
11
+ export const auth = betterAuth({
12
+ baseURL: env.BETTER_AUTH_URL,
13
+ database: prismaAdapter(prisma, {
14
+ provider: "postgresql",
15
+ }),
16
+ emailAndPassword: {
17
+ enabled: true,
18
+ autoSignIn: false,
19
+ },
20
+ socialProviders: {
21
+ google: {
22
+ clientId: env.GOOGLE_CLIENT_ID,
23
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
24
+ prompt: "select_account",
25
+ },
26
+ },
27
+ user: {
28
+ additionalFields: {
29
+ lastName: {
30
+ type: "string",
31
+ required: false,
32
+ },
33
+ username: {
34
+ type: "string",
35
+ required: false,
36
+ },
37
+ role: {
38
+ type: "string",
39
+ required: true,
40
+ defaultValue: "user",
41
+ },
42
+ },
43
+ },
44
+ plugins: [
45
+ admin({
46
+ defaultBanReason: "Has sido baneado por violación de nuestros términos y condiciones",
47
+ }),
48
+ ],
49
+ });
@@ -0,0 +1,124 @@
1
+ import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
2
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
3
+
4
+ // Configuración del cliente S3 para Cloudflare R2
5
+ const createS3Client = () => {
6
+ const endpoint = process.env.CLOUDFLARE_ENDPOINT;
7
+ const accessKeyId = process.env.CLOUDFLARE_ACCESS_KEY;
8
+ const secretAccessKey = process.env.CLOUDFLARE_SECRET_KEY;
9
+
10
+ if (!endpoint || !accessKeyId || !secretAccessKey) {
11
+ throw new Error("Cloudflare R2 environment variables are not properly configured");
12
+ }
13
+
14
+ return new S3Client({
15
+ region: "auto",
16
+ endpoint,
17
+ credentials: {
18
+ accessKeyId,
19
+ secretAccessKey,
20
+ },
21
+ });
22
+ };
23
+
24
+ export interface UploadUrlResponse {
25
+ presignedUrl: string;
26
+ publicUrl: string;
27
+ key: string;
28
+ }
29
+
30
+ export async function generateUploadUrl(
31
+ fileName: string,
32
+ fileType: string,
33
+ userId: string,
34
+ ): Promise<UploadUrlResponse> {
35
+ const s3Client = createS3Client();
36
+ const bucketName = process.env.CLOUDFLARE_BUCKET;
37
+ const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
38
+
39
+ if (!bucketName) {
40
+ throw new Error("CLOUDFLARE_BUCKET environment variable is not set");
41
+ }
42
+
43
+ if (!accountId) {
44
+ throw new Error("CLOUDFLARE_ACCOUNT_ID environment variable is not set");
45
+ }
46
+
47
+ // Generar nombre único para el archivo
48
+ const timestamp = Date.now();
49
+ const sanitizedFileName = fileName.replace(/[^a-zA-Z0-9.-]/g, "_");
50
+ const key = `tickets/${userId}/${timestamp.toString()}-${sanitizedFileName}`;
51
+
52
+ // Crear comando para subir archivo
53
+ const putCommand = new PutObjectCommand({
54
+ Bucket: bucketName,
55
+ Key: key,
56
+ ContentType: fileType,
57
+ });
58
+
59
+ // Generar URL presignada (válida por 5 minutos)
60
+ const presignedUrl = await getSignedUrl(s3Client, putCommand, {
61
+ expiresIn: 300, // 5 minutos
62
+ });
63
+
64
+ // URL pública del archivo (después de ser subido)
65
+ const publicDomain = process.env.CLOUDFLARE_PUBLIC_URL;
66
+
67
+ const publicUrl = publicDomain
68
+ ? `${publicDomain}/${key}`
69
+ : `https://${bucketName}.${accountId}.r2.cloudflarestorage.com/${key}`;
70
+
71
+ return {
72
+ presignedUrl,
73
+ publicUrl,
74
+ key,
75
+ };
76
+ }
77
+
78
+ // Función para eliminar un archivo de R2
79
+ export async function deleteFile(key: string): Promise<void> {
80
+ const s3Client = createS3Client();
81
+ const bucketName = process.env.CLOUDFLARE_BUCKET;
82
+
83
+ if (!bucketName) {
84
+ throw new Error("CLOUDFLARE_BUCKET environment variable is not set");
85
+ }
86
+
87
+ const deleteCommand = new DeleteObjectCommand({
88
+ Bucket: bucketName,
89
+ Key: key,
90
+ });
91
+
92
+ await s3Client.send(deleteCommand);
93
+ }
94
+
95
+ // Función para extraer la key de una URL pública
96
+ export function extractKeyFromUrl(publicUrl: string): string | null {
97
+ try {
98
+ // Obtener el dominio público configurado
99
+ const publicDomain = process.env.CLOUDFLARE_PUBLIC_URL;
100
+
101
+ if (publicDomain && publicUrl.startsWith(publicDomain)) {
102
+ // URL con dominio personalizado: https://images.skinsbrain.com/tickets/user123/1234567890-image.jpg
103
+ return publicUrl.replace(`${publicDomain}/`, "");
104
+ }
105
+
106
+ // URL estándar de R2: https://bucket.accountid.r2.cloudflarestorage.com/tickets/user123/1234567890-image.jpg
107
+ const bucketName = process.env.CLOUDFLARE_BUCKET;
108
+ const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
109
+
110
+ if (bucketName && accountId) {
111
+ const standardDomain = `https://${bucketName}.${accountId}.r2.cloudflarestorage.com/`;
112
+
113
+ if (publicUrl.startsWith(standardDomain)) {
114
+ return publicUrl.replace(standardDomain, "");
115
+ }
116
+ }
117
+
118
+ return null;
119
+ } catch (error) {
120
+ console.error("Error extracting key from URL:", error);
121
+
122
+ return null;
123
+ }
124
+ }
@@ -1,9 +1,28 @@
1
1
  import { PrismaClient } from "@prisma/client";
2
2
 
3
- const globalForPrisma = global as unknown as { prisma: PrismaClient };
3
+ const globalForPrisma = global as unknown as {
4
+ db: PrismaClient | undefined;
5
+ };
4
6
 
5
- export const db = globalForPrisma.prisma || new PrismaClient();
7
+ // Optimized Prisma configuration
8
+ const createPrismaClient = () => {
9
+ return new PrismaClient({
10
+ log: [],
11
+ datasources: {
12
+ db: {
13
+ url: process.env.DATABASE_URL,
14
+ },
15
+ },
16
+ });
17
+ };
6
18
 
7
- if (process.env.NODE_ENV !== "production") {
8
- globalForPrisma.prisma = db;
9
- }
19
+ const db = globalForPrisma.db ?? createPrismaClient();
20
+
21
+ if (process.env.NODE_ENV !== "production") globalForPrisma.db = db;
22
+
23
+ // Graceful shutdown handling
24
+ process.on("beforeExit", () => {
25
+ void db.$disconnect();
26
+ });
27
+
28
+ export default db;
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+
3
+ const envSchema = z.object({
4
+ DATABASE_URL: z.url(),
5
+ BETTER_AUTH_URL: z.url(),
6
+ RESEND_API_KEY: z.string().min(1),
7
+ RESEND_FROM_EMAIL: z.email(),
8
+ GOOGLE_CLIENT_ID: z.string().min(1),
9
+ GOOGLE_CLIENT_SECRET: z.string().min(1),
10
+ NODE_ENV: z.enum(["development", "production"]).default("development"),
11
+ // Cloudflare R2 variables
12
+ CLOUDFLARE_ACCESS_KEY: z.string().min(1).optional(),
13
+ CLOUDFLARE_SECRET_KEY: z.string().min(1).optional(),
14
+ CLOUDFLARE_ENDPOINT: z.url().optional(),
15
+ CLOUDFLARE_BUCKET: z.string().min(1).optional(),
16
+ CLOUDFLARE_ACCOUNT_ID: z.string().min(1).optional(),
17
+ CLOUDFLARE_PUBLIC_URL: z.url().optional(),
18
+ });
19
+
20
+ export type Env = z.infer<typeof envSchema>;
21
+
22
+ function validateEnv(): Env {
23
+ try {
24
+ return envSchema.parse(process.env);
25
+ } catch (error) {
26
+ if (error instanceof z.ZodError) {
27
+ const missingVars = error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`);
28
+
29
+ console.error("❌ Invalid environment variables:");
30
+ missingVars.forEach((err) => console.error(` - ${err}`));
31
+
32
+ process.exit(1);
33
+ }
34
+
35
+ throw error;
36
+ }
37
+ }
38
+
39
+ export const env = validateEnv();
40
+
41
+ // Helper to check if we're in development
42
+ export const isDevelopment = env.NODE_ENV === "development";
43
+
44
+ export const isProduction = env.NODE_ENV === "production";
@@ -0,0 +1,105 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { headers } from "next/headers";
3
+
4
+ import { auth } from "@/lib/auth";
5
+
6
+ // Rutas públicas que no requieren autenticación
7
+ const publicRoutes = [
8
+ "/",
9
+ "/login",
10
+ "/marketplace",
11
+ "/politica-cookies",
12
+ "/politica-privacidad",
13
+ "/terminos-condiciones",
14
+ "/api/auth",
15
+ ];
16
+
17
+ // Rutas que requieren autenticación de usuario (role: user)
18
+ const userRoutes = ["/dashboard"];
19
+
20
+ // Rutas que requieren autenticación de admin (role: admin)
21
+ const adminRoutes = ["/admin"];
22
+
23
+ export async function middleware(request: NextRequest) {
24
+ const { pathname } = request.nextUrl;
25
+
26
+ // Verificar si la ruta es pública
27
+ const isPublicRoute = publicRoutes.some(
28
+ (route) => pathname === route || pathname.startsWith(route + "/"),
29
+ );
30
+
31
+ // Si es una ruta pública, permitir acceso
32
+ if (isPublicRoute) {
33
+ return NextResponse.next();
34
+ }
35
+
36
+ // Obtener sesión
37
+ const session = await auth.api.getSession({
38
+ headers: await headers(),
39
+ });
40
+
41
+ // Si no hay sesión, redirigir al login
42
+ if (!session) {
43
+ return NextResponse.redirect(new URL("/login", request.url));
44
+ }
45
+
46
+ const userRole = session.user.role;
47
+
48
+ // Verificar si el usuario está baneado y perfil incompleto en una sola consulta
49
+ // Solo verificar si no está ya en la página de perfil, resumen o login para evitar bucles
50
+ if (pathname !== "/dashboard" && pathname !== "/login") {
51
+ try {
52
+ const userData = await auth.api.getUser({ query: { id: session.user.id } });
53
+
54
+ if (!userData) {
55
+ // Si no se puede obtener los datos del usuario, redirigir al login
56
+ console.error("User data not found for session:", session.user.id);
57
+
58
+ return NextResponse.redirect(new URL("/login?error=user_not_found", request.url));
59
+ }
60
+ } catch (error) {
61
+ // Si hay error obteniendo los datos del usuario, log detallado y permitir continuar
62
+ console.error("Error checking user status in middleware:", {
63
+ error: error instanceof Error ? error.message : error,
64
+ userId: session.user.id,
65
+ pathname,
66
+ });
67
+ }
68
+ }
69
+
70
+ // Verificar rutas de admin
71
+ const isAdminRoute = adminRoutes.some((route) => pathname.startsWith(route));
72
+
73
+ if (isAdminRoute && userRole !== "admin") {
74
+ // Si no es admin, redirigir al panel de usuario
75
+ return NextResponse.redirect(new URL("/dashboard", request.url));
76
+ }
77
+
78
+ // Verificar rutas de usuario
79
+ const isUserRoute = userRoutes.some((route) => pathname.startsWith(route));
80
+
81
+ if (isUserRoute && userRole !== "user" && userRole !== "admin") {
82
+ // Si no es usuario ni admin, redirigir al login
83
+ return NextResponse.redirect(new URL("/login", request.url));
84
+ }
85
+
86
+ // Si es admin y está intentando acceder a rutas de usuario, permitir
87
+ if (userRole === "admin") {
88
+ return NextResponse.next();
89
+ }
90
+
91
+ // Si es usuario normal, verificar que no esté intentando acceder a rutas de admin
92
+ if (userRole === "user" && isAdminRoute) {
93
+ return NextResponse.redirect(new URL("/dashboard", request.url));
94
+ }
95
+
96
+ return NextResponse.next();
97
+ }
98
+
99
+ export const config = {
100
+ runtime: "nodejs",
101
+ matcher: [
102
+ // Aplicar middleware a todas las rutas excepto archivos estáticos
103
+ "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
104
+ ],
105
+ };
@@ -0,0 +1,16 @@
1
+ import { PrismaClient, Prisma } from "@prisma/client";
2
+
3
+
4
+ const prisma = new PrismaClient();
5
+
6
+ async function main() {
7
+ //Create database data
8
+ console.log("Database seeded");
9
+ }
10
+
11
+ main().catch((error) => {
12
+ console.error(error);
13
+ process.exit(1);
14
+ }).finally(async () => {
15
+ await prisma.$disconnect();
16
+ });
@@ -20,12 +20,22 @@ try {
20
20
 
21
21
  if (isMerged) {
22
22
  run(`git branch -d ${branch}`);
23
- console.log(`✅ Rama ${branch} rebaseada, mergeada en dev y eliminada`);
23
+ // Eliminar la rama del repositorio remoto si existe
24
+ try {
25
+ run(`git push origin --delete ${branch}`);
26
+ console.error(`✅ Rama ${branch} rebaseada, mergeada en dev y eliminada local y remotamente`);
27
+ } catch (remoteDeleteError) {
28
+ console.error(
29
+ `✅ Rama ${branch} rebaseada, mergeada en dev y eliminada localmente (no existía en remoto)`,
30
+ );
31
+ }
24
32
  } else {
25
- console.log(
33
+ console.error(
26
34
  `⚠️ La rama ${branch} todavía no está completamente mergeada en dev, no se elimina.`,
27
35
  );
28
36
  }
37
+ run("git push origin dev");
38
+ run("git remote prune origin");
29
39
  } catch (err) {
30
40
  console.error("❌ Error en el proceso:", err.message);
31
41
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-apppaaaul",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
4
4
  "description": "Create projects as paaauldev would",
5
5
  "main": "index.mjs",
6
6
  "bin": {
@@ -1,15 +0,0 @@
1
- ## Getting Started with {{name}}
2
-
3
- First, run the development server:
4
-
5
- ```bash
6
- npm run dev
7
- # or
8
- yarn dev
9
- # or
10
- pnpm dev
11
- ```
12
-
13
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14
-
15
- You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
@@ -1,21 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
4
- "rsc": true,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "tailwind.config.ts",
8
- "css": "src/app/globals.css",
9
- "baseColor": "neutral",
10
- "cssVariables": true,
11
- "prefix": ""
12
- },
13
- "aliases": {
14
- "components": "@/components",
15
- "utils": "@/lib/utils",
16
- "ui": "@/components/ui",
17
- "lib": "@/lib",
18
- "hooks": "@/hooks"
19
- },
20
- "iconLibrary": "lucide"
21
- }
@@ -1,11 +0,0 @@
1
- import { config } from "dotenv";
2
- import { defineConfig } from "drizzle-kit";
3
- config({ path: ".env" });
4
-
5
- export default defineConfig({
6
- schema: "./src/db/schema.ts",
7
- dialect: "postgresql",
8
- dbCredentials: {
9
- url: process.env.DATABASE_URL!,
10
- },
11
- });