create-ej-app 2.2.0 → 2.3.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.
- package/cli.js +64 -63
- package/package.json +1 -1
- package/template/advanced/prisma/schema.prisma +1 -0
- package/template/advanced/src/app/(app)/tasks/[id]/actions.ts +36 -38
- package/template/advanced/src/app/api/upload/route.ts +1 -10
- package/template/advanced/src/lib/storageClient.ts +11 -0
- package/template/base/prisma/schema.prisma +1 -0
- package/template/base/src/app/(app)/tasks/[id]/actions.ts +36 -38
- package/template/base/src/app/api/upload/route.ts +1 -10
- package/template/base/src/lib/storageClient.ts +11 -0
package/cli.js
CHANGED
|
@@ -6,83 +6,84 @@ const path = require('path')
|
|
|
6
6
|
const { prompt } = require('enquirer')
|
|
7
7
|
const copydir = require('copy-dir')
|
|
8
8
|
|
|
9
|
-
program
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
initial: 0
|
|
39
|
-
}
|
|
40
|
-
])
|
|
41
|
-
|
|
42
|
-
const { projectName, description, template } = response
|
|
43
|
-
|
|
44
|
-
// Create a directory with the project name
|
|
45
|
-
const projectDir = path.join(process.cwd(), projectName)
|
|
46
|
-
|
|
47
|
-
if (fs.existsSync(projectDir)) {
|
|
48
|
-
console.error(`Error: Directory '${projectName}' already exists.`)
|
|
49
|
-
process.exit(1)
|
|
9
|
+
program.action(async () => {
|
|
10
|
+
const response = await prompt([
|
|
11
|
+
{
|
|
12
|
+
type: 'input',
|
|
13
|
+
name: 'projectName',
|
|
14
|
+
message: 'Enter the project name:',
|
|
15
|
+
initial: 'ej-app'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
type: 'input',
|
|
19
|
+
name: 'description',
|
|
20
|
+
message: 'Enter a description for the project:',
|
|
21
|
+
initial: 'A Legendary App'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'select',
|
|
25
|
+
name: 'template',
|
|
26
|
+
message: 'Select a template:',
|
|
27
|
+
choices: [
|
|
28
|
+
{
|
|
29
|
+
name: 'base',
|
|
30
|
+
message: 'Base setup with ts, shadcn, prisma, auth, editor.js'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'advanced',
|
|
34
|
+
message:
|
|
35
|
+
'Advanced setup with User Types and ACLs (roles & permissions)'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
50
38
|
}
|
|
39
|
+
])
|
|
51
40
|
|
|
52
|
-
|
|
41
|
+
const { projectName, description, template } = response
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
copydir.sync(templateDir, projectDir)
|
|
43
|
+
// Create a directory with the project name
|
|
44
|
+
const projectDir = path.join(process.cwd(), projectName)
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
packageJson.description = description
|
|
63
|
-
fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2))
|
|
46
|
+
if (fs.existsSync(projectDir)) {
|
|
47
|
+
console.error(`Error: Directory '${projectName}' already exists.`)
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
64
50
|
|
|
65
|
-
|
|
66
|
-
const siteConfigFile = path.join(projectDir, 'src', 'lib', 'siteConfig.ts')
|
|
67
|
-
let siteConfig = fs.readFileSync(siteConfigFile, 'utf-8')
|
|
68
|
-
siteConfig = siteConfig.replace('Project Name', projectName)
|
|
69
|
-
siteConfig = siteConfig.replace('Project Description', description)
|
|
70
|
-
fs.writeFileSync(siteConfigFile, siteConfig)
|
|
51
|
+
fs.mkdirSync(projectDir)
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
fs.writeFileSync(path.join(projectDir, '.env'), envExample)
|
|
53
|
+
// Copy all files and subdirectories from your template folder to the new project directory
|
|
54
|
+
const templateDir = path.join(__dirname, 'template', template)
|
|
55
|
+
copydir.sync(templateDir, projectDir)
|
|
76
56
|
|
|
77
|
-
|
|
57
|
+
// Modify package.json in the new project directory to set the project name and description
|
|
58
|
+
const packageJsonFile = path.join(projectDir, 'package.json')
|
|
59
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf-8'))
|
|
60
|
+
packageJson.name = projectName
|
|
61
|
+
packageJson.description = description
|
|
62
|
+
fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2))
|
|
63
|
+
|
|
64
|
+
// Modify src/lib/siteConfig.ts in the new project directory to set the project name and description
|
|
65
|
+
const siteConfigFile = path.join(projectDir, 'src', 'lib', 'siteConfig.ts')
|
|
66
|
+
let siteConfig = fs.readFileSync(siteConfigFile, 'utf-8')
|
|
67
|
+
siteConfig = siteConfig.replace('Project Name', projectName)
|
|
68
|
+
siteConfig = siteConfig.replace('Project Description', description)
|
|
69
|
+
fs.writeFileSync(siteConfigFile, siteConfig)
|
|
70
|
+
|
|
71
|
+
// Copy .env.example to .env
|
|
72
|
+
const envExampleFile = path.join(projectDir, '.env.example')
|
|
73
|
+
const envExample = fs.readFileSync(envExampleFile, 'utf-8')
|
|
74
|
+
fs.writeFileSync(path.join(projectDir, '.env'), envExample)
|
|
75
|
+
|
|
76
|
+
console.log(`Created a new app called '${projectName}' 🎉
|
|
78
77
|
|
|
79
78
|
|
|
80
79
|
To get started, run the following commands:
|
|
81
80
|
|
|
82
81
|
cd ${projectName}
|
|
82
|
+
|
|
83
83
|
npm install
|
|
84
|
+
|
|
84
85
|
npm run dev
|
|
85
86
|
`)
|
|
86
|
-
|
|
87
|
+
})
|
|
87
88
|
|
|
88
89
|
program.parse(process.argv)
|
package/package.json
CHANGED
|
@@ -5,8 +5,41 @@ import { revalidatePath } from 'next/cache'
|
|
|
5
5
|
|
|
6
6
|
import { authGuard } from '@/lib/auth'
|
|
7
7
|
import { prisma } from '@/lib/db'
|
|
8
|
+
import { storageClient } from '@/lib/storageClient'
|
|
8
9
|
import { UnwrapPromise } from '@/types/UnwrapPromise'
|
|
9
10
|
|
|
11
|
+
const handleImages = async (imageIds: string[], taskId: string) => {
|
|
12
|
+
const prevImages = await prisma.resource.findMany({
|
|
13
|
+
where: {
|
|
14
|
+
taskId
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
for (const p of prevImages) {
|
|
19
|
+
if (imageIds.includes(p.id)) continue
|
|
20
|
+
await Promise.all([
|
|
21
|
+
storageClient.deleteFile(p.newFilename),
|
|
22
|
+
prisma.resource.delete({
|
|
23
|
+
where: {
|
|
24
|
+
id: p.id
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (imageIds.length)
|
|
31
|
+
await prisma.resource.updateMany({
|
|
32
|
+
where: {
|
|
33
|
+
id: {
|
|
34
|
+
in: imageIds
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
data: {
|
|
38
|
+
taskId
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
10
43
|
export const getTask = async (id: string) => {
|
|
11
44
|
const session = await authGuard(['Root', 'Normal'], 'TaskView')
|
|
12
45
|
if (!session) throw new Error('Unauthorized')
|
|
@@ -57,25 +90,7 @@ export const createTask = async ({
|
|
|
57
90
|
updatedById: session.user.id
|
|
58
91
|
}
|
|
59
92
|
})
|
|
60
|
-
await
|
|
61
|
-
where: {
|
|
62
|
-
taskId: id
|
|
63
|
-
},
|
|
64
|
-
data: {
|
|
65
|
-
taskId: null
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
if (imageIds.length)
|
|
69
|
-
await prisma.resource.updateMany({
|
|
70
|
-
where: {
|
|
71
|
-
id: {
|
|
72
|
-
in: imageIds
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
data: {
|
|
76
|
-
taskId: id
|
|
77
|
-
}
|
|
78
|
-
})
|
|
93
|
+
await handleImages(imageIds, id)
|
|
79
94
|
|
|
80
95
|
revalidatePath('/tasks')
|
|
81
96
|
revalidatePath(`/tasks/${id}`)
|
|
@@ -110,25 +125,7 @@ export const updateTask = async ({
|
|
|
110
125
|
updatedById: session.user.id
|
|
111
126
|
}
|
|
112
127
|
})
|
|
113
|
-
await
|
|
114
|
-
where: {
|
|
115
|
-
taskId: id
|
|
116
|
-
},
|
|
117
|
-
data: {
|
|
118
|
-
taskId: null
|
|
119
|
-
}
|
|
120
|
-
})
|
|
121
|
-
if (imageIds.length)
|
|
122
|
-
await prisma.resource.updateMany({
|
|
123
|
-
where: {
|
|
124
|
-
id: {
|
|
125
|
-
in: imageIds
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
data: {
|
|
129
|
-
taskId: id
|
|
130
|
-
}
|
|
131
|
-
})
|
|
128
|
+
await handleImages(imageIds, id)
|
|
132
129
|
|
|
133
130
|
revalidatePath('/tasks')
|
|
134
131
|
revalidatePath(`/tasks/${id}`)
|
|
@@ -138,6 +135,7 @@ export const deleteTask = async (id: string) => {
|
|
|
138
135
|
const session = await authGuard(['Root', 'Normal'], 'TaskDelete')
|
|
139
136
|
if (!session) throw new Error('Unauthorized')
|
|
140
137
|
|
|
138
|
+
await handleImages([], id)
|
|
141
139
|
await prisma.task.delete({
|
|
142
140
|
where: {
|
|
143
141
|
id
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
|
|
2
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
3
2
|
|
|
4
|
-
import { env } from '@/env.mjs'
|
|
5
3
|
import { getAuthSession } from '@/lib/auth'
|
|
6
4
|
import { prisma } from '@/lib/db'
|
|
7
|
-
|
|
8
|
-
const storageClient = GCPStorageClient({
|
|
9
|
-
projectId: env.FIREBASE_PROJECT_ID!,
|
|
10
|
-
privateKey: env.FIREBASE_PRIVATE_KEY!,
|
|
11
|
-
clientEmail: env.FIREBASE_CLIENT_EMAIL!,
|
|
12
|
-
bucket: env.FIREBASE_STORAGE_BUCKET!,
|
|
13
|
-
defaultMediaPublic: true
|
|
14
|
-
})
|
|
5
|
+
import { storageClient } from '@/lib/storageClient'
|
|
15
6
|
|
|
16
7
|
export async function POST(request: NextRequest) {
|
|
17
8
|
const session = await getAuthSession()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
|
|
2
|
+
|
|
3
|
+
import { env } from '@/env.mjs'
|
|
4
|
+
|
|
5
|
+
export const storageClient = GCPStorageClient({
|
|
6
|
+
projectId: env.FIREBASE_PROJECT_ID!,
|
|
7
|
+
privateKey: env.FIREBASE_PRIVATE_KEY!,
|
|
8
|
+
clientEmail: env.FIREBASE_CLIENT_EMAIL!,
|
|
9
|
+
bucket: env.FIREBASE_STORAGE_BUCKET!,
|
|
10
|
+
defaultMediaPublic: true
|
|
11
|
+
})
|
|
@@ -5,8 +5,41 @@ import { revalidatePath } from 'next/cache'
|
|
|
5
5
|
|
|
6
6
|
import { getAuthSession } from '@/lib/auth'
|
|
7
7
|
import { prisma } from '@/lib/db'
|
|
8
|
+
import { storageClient } from '@/lib/storageClient'
|
|
8
9
|
import { UnwrapPromise } from '@/types/UnwrapPromise'
|
|
9
10
|
|
|
11
|
+
const handleImages = async (imageIds: string[], taskId: string) => {
|
|
12
|
+
const prevImages = await prisma.resource.findMany({
|
|
13
|
+
where: {
|
|
14
|
+
taskId
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
for (const p of prevImages) {
|
|
19
|
+
if (imageIds.includes(p.id)) continue
|
|
20
|
+
await Promise.all([
|
|
21
|
+
storageClient.deleteFile(p.newFilename),
|
|
22
|
+
prisma.resource.delete({
|
|
23
|
+
where: {
|
|
24
|
+
id: p.id
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (imageIds.length)
|
|
31
|
+
await prisma.resource.updateMany({
|
|
32
|
+
where: {
|
|
33
|
+
id: {
|
|
34
|
+
in: imageIds
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
data: {
|
|
38
|
+
taskId
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
10
43
|
export const getTask = async (id: string) => {
|
|
11
44
|
const session = await getAuthSession()
|
|
12
45
|
if (!session) throw new Error('Unauthorized')
|
|
@@ -57,25 +90,7 @@ export const createTask = async ({
|
|
|
57
90
|
updatedById: session.user.id
|
|
58
91
|
}
|
|
59
92
|
})
|
|
60
|
-
await
|
|
61
|
-
where: {
|
|
62
|
-
taskId: id
|
|
63
|
-
},
|
|
64
|
-
data: {
|
|
65
|
-
taskId: null
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
if (imageIds.length)
|
|
69
|
-
await prisma.resource.updateMany({
|
|
70
|
-
where: {
|
|
71
|
-
id: {
|
|
72
|
-
in: imageIds
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
data: {
|
|
76
|
-
taskId: id
|
|
77
|
-
}
|
|
78
|
-
})
|
|
93
|
+
await handleImages(imageIds, id)
|
|
79
94
|
|
|
80
95
|
revalidatePath('/tasks')
|
|
81
96
|
revalidatePath(`/tasks/${id}`)
|
|
@@ -110,25 +125,7 @@ export const updateTask = async ({
|
|
|
110
125
|
updatedById: session.user.id
|
|
111
126
|
}
|
|
112
127
|
})
|
|
113
|
-
await
|
|
114
|
-
where: {
|
|
115
|
-
taskId: id
|
|
116
|
-
},
|
|
117
|
-
data: {
|
|
118
|
-
taskId: null
|
|
119
|
-
}
|
|
120
|
-
})
|
|
121
|
-
if (imageIds.length)
|
|
122
|
-
await prisma.resource.updateMany({
|
|
123
|
-
where: {
|
|
124
|
-
id: {
|
|
125
|
-
in: imageIds
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
data: {
|
|
129
|
-
taskId: id
|
|
130
|
-
}
|
|
131
|
-
})
|
|
128
|
+
await handleImages(imageIds, id)
|
|
132
129
|
|
|
133
130
|
revalidatePath('/tasks')
|
|
134
131
|
revalidatePath(`/tasks/${id}`)
|
|
@@ -138,6 +135,7 @@ export const deleteTask = async (id: string) => {
|
|
|
138
135
|
const session = await getAuthSession()
|
|
139
136
|
if (!session) throw new Error('Unauthorized')
|
|
140
137
|
|
|
138
|
+
await handleImages([], id)
|
|
141
139
|
await prisma.task.delete({
|
|
142
140
|
where: {
|
|
143
141
|
id
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
|
|
2
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
3
2
|
|
|
4
|
-
import { env } from '@/env.mjs'
|
|
5
3
|
import { getAuthSession } from '@/lib/auth'
|
|
6
4
|
import { prisma } from '@/lib/db'
|
|
7
|
-
|
|
8
|
-
const storageClient = GCPStorageClient({
|
|
9
|
-
projectId: env.FIREBASE_PROJECT_ID!,
|
|
10
|
-
privateKey: env.FIREBASE_PRIVATE_KEY!,
|
|
11
|
-
clientEmail: env.FIREBASE_CLIENT_EMAIL!,
|
|
12
|
-
bucket: env.FIREBASE_STORAGE_BUCKET!,
|
|
13
|
-
defaultMediaPublic: true
|
|
14
|
-
})
|
|
5
|
+
import { storageClient } from '@/lib/storageClient'
|
|
15
6
|
|
|
16
7
|
export async function POST(request: NextRequest) {
|
|
17
8
|
const session = await getAuthSession()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
|
|
2
|
+
|
|
3
|
+
import { env } from '@/env.mjs'
|
|
4
|
+
|
|
5
|
+
export const storageClient = GCPStorageClient({
|
|
6
|
+
projectId: env.FIREBASE_PROJECT_ID!,
|
|
7
|
+
privateKey: env.FIREBASE_PRIVATE_KEY!,
|
|
8
|
+
clientEmail: env.FIREBASE_CLIENT_EMAIL!,
|
|
9
|
+
bucket: env.FIREBASE_STORAGE_BUCKET!,
|
|
10
|
+
defaultMediaPublic: true
|
|
11
|
+
})
|