create-ej-app 12.0.0 → 15.0.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 +5 -4
- package/cli.ts +9 -4
- package/package.json +3 -3
- package/template/base/package.json +23 -29
- package/template/base/src/lib/auth.ts +15 -6
- package/template/base/terraform/artifact-registry.tf +5 -0
- package/template/base/terraform/backend-service.tf +13 -0
- package/template/base/terraform/cleanup.sh +9 -0
- package/template/base/terraform/cloudbuild.tf +70 -0
- package/template/base/terraform/cloudrun.tf +68 -0
- package/template/base/terraform/database.tf +60 -0
- package/template/base/terraform/forwarding-rule.tf +43 -0
- package/template/base/terraform/ip-address.tf +11 -0
- package/template/base/terraform/network-endpoint-group.tf +11 -0
- package/template/base/terraform/network.tf +21 -0
- package/template/base/terraform/pre.sh +3 -0
- package/template/base/terraform/providers.tf +18 -0
- package/template/base/terraform/secret.txt +11 -0
- package/template/base/terraform/setup.sh +25 -0
- package/template/base/terraform/ssl-certs.tf +32 -0
- package/template/base/terraform/target-https-proxy copy.tf +14 -0
- package/template/base/terraform/target-https-proxy.tf +14 -0
- package/template/base/terraform/terraform.tfvars +4 -0
- package/template/base/terraform/url-map.tf +23 -0
- package/template/base/terraform/variables.tf +21 -0
- package/template/old/.env.example +0 -17
- package/template/old/.eslintrc.cjs +0 -26
- package/template/old/components.json +0 -17
- package/template/old/drizzle.config.ts +0 -11
- package/template/old/next.config.mjs +0 -4
- package/template/old/package.json +0 -87
- package/template/old/postcss.config.mjs +0 -8
- package/template/old/prettier.config.mjs +0 -11
- package/template/old/public/next.svg +0 -1
- package/template/old/public/vercel.svg +0 -1
- package/template/old/src/app/(app)/dashboard/layout.tsx +0 -14
- package/template/old/src/app/(app)/dashboard/page.tsx +0 -12
- package/template/old/src/app/(app)/layout.tsx +0 -125
- package/template/old/src/app/(app)/roles/[id]/actions.ts +0 -59
- package/template/old/src/app/(app)/roles/[id]/page.tsx +0 -46
- package/template/old/src/app/(app)/roles/[id]/render.tsx +0 -264
- package/template/old/src/app/(app)/roles/[id]/validation.ts +0 -22
- package/template/old/src/app/(app)/roles/actions.ts +0 -33
- package/template/old/src/app/(app)/roles/page.tsx +0 -52
- package/template/old/src/app/(app)/roles/render.tsx +0 -80
- package/template/old/src/app/(app)/settings/actions.ts +0 -17
- package/template/old/src/app/(app)/settings/layout.tsx +0 -14
- package/template/old/src/app/(app)/settings/loading.tsx +0 -28
- package/template/old/src/app/(app)/settings/page.tsx +0 -19
- package/template/old/src/app/(app)/settings/render.tsx +0 -107
- package/template/old/src/app/(app)/settings/validation.ts +0 -5
- package/template/old/src/app/(app)/tasks/[id]/actions.ts +0 -92
- package/template/old/src/app/(app)/tasks/[id]/page.tsx +0 -59
- package/template/old/src/app/(app)/tasks/[id]/render.tsx +0 -482
- package/template/old/src/app/(app)/tasks/[id]/validation.ts +0 -18
- package/template/old/src/app/(app)/tasks/actions.ts +0 -39
- package/template/old/src/app/(app)/tasks/page.tsx +0 -59
- package/template/old/src/app/(app)/tasks/render.tsx +0 -129
- package/template/old/src/app/(app)/tasks/statusEnum.ts +0 -3
- package/template/old/src/app/(app)/users/[id]/actions.ts +0 -68
- package/template/old/src/app/(app)/users/[id]/page.tsx +0 -39
- package/template/old/src/app/(app)/users/[id]/render.tsx +0 -325
- package/template/old/src/app/(app)/users/[id]/validation.ts +0 -18
- package/template/old/src/app/(app)/users/actions.ts +0 -34
- package/template/old/src/app/(app)/users/page.tsx +0 -62
- package/template/old/src/app/(app)/users/render.tsx +0 -124
- package/template/old/src/app/(auth)/layout.tsx +0 -27
- package/template/old/src/app/(auth)/login/page.tsx +0 -49
- package/template/old/src/app/(auth)/login/render.tsx +0 -146
- package/template/old/src/app/(marketing)/layout.tsx +0 -42
- package/template/old/src/app/(marketing)/page.tsx +0 -97
- package/template/old/src/app/api/auth/[...nextauth]/route.ts +0 -7
- package/template/old/src/app/favicon.ico +0 -0
- package/template/old/src/app/globals.css +0 -76
- package/template/old/src/app/layout.tsx +0 -31
- package/template/old/src/components/app-nav-command.tsx +0 -178
- package/template/old/src/components/dark-mode-toggle.tsx +0 -43
- package/template/old/src/components/data-table/data-table-column-header.tsx +0 -91
- package/template/old/src/components/data-table/data-table-faceted-filter.tsx +0 -149
- package/template/old/src/components/data-table/data-table-loading.tsx +0 -77
- package/template/old/src/components/data-table/data-table-pagination.tsx +0 -94
- package/template/old/src/components/data-table/data-table-toolbar.tsx +0 -78
- package/template/old/src/components/data-table/data-table-types.ts +0 -17
- package/template/old/src/components/data-table/data-table-view-options.tsx +0 -59
- package/template/old/src/components/data-table/data-table.tsx +0 -301
- package/template/old/src/components/data-table/simple-data-table.tsx +0 -109
- package/template/old/src/components/empty-placeholder.tsx +0 -78
- package/template/old/src/components/heading.tsx +0 -17
- package/template/old/src/components/icons.tsx +0 -101
- package/template/old/src/components/main-nav.tsx +0 -64
- package/template/old/src/components/mobile-nav.tsx +0 -50
- package/template/old/src/components/shell.tsx +0 -13
- package/template/old/src/components/side-nav.tsx +0 -51
- package/template/old/src/components/site-footer.tsx +0 -31
- package/template/old/src/components/system-info.tsx +0 -36
- package/template/old/src/components/ui/alert-dialog.tsx +0 -141
- package/template/old/src/components/ui/avatar.tsx +0 -50
- package/template/old/src/components/ui/badge.tsx +0 -36
- package/template/old/src/components/ui/button.tsx +0 -56
- package/template/old/src/components/ui/card.tsx +0 -79
- package/template/old/src/components/ui/checkbox.tsx +0 -30
- package/template/old/src/components/ui/command.tsx +0 -155
- package/template/old/src/components/ui/dialog.tsx +0 -122
- package/template/old/src/components/ui/dropdown-menu.tsx +0 -200
- package/template/old/src/components/ui/form.tsx +0 -176
- package/template/old/src/components/ui/input.tsx +0 -25
- package/template/old/src/components/ui/label.tsx +0 -26
- package/template/old/src/components/ui/popover.tsx +0 -31
- package/template/old/src/components/ui/select.tsx +0 -160
- package/template/old/src/components/ui/separator.tsx +0 -31
- package/template/old/src/components/ui/skeleton.tsx +0 -15
- package/template/old/src/components/ui/sonner.tsx +0 -31
- package/template/old/src/components/ui/table.tsx +0 -117
- package/template/old/src/components/user-account-nav.tsx +0 -60
- package/template/old/src/components/user-avatar.tsx +0 -27
- package/template/old/src/data-access/resource.ts +0 -85
- package/template/old/src/data-access/role-permission.ts +0 -45
- package/template/old/src/data-access/role.ts +0 -105
- package/template/old/src/data-access/task.ts +0 -141
- package/template/old/src/data-access/user.ts +0 -141
- package/template/old/src/db/index.ts +0 -14
- package/template/old/src/db/schema.ts +0 -287
- package/template/old/src/env.mjs +0 -58
- package/template/old/src/hooks/use-debounce.tsx +0 -17
- package/template/old/src/hooks/use-lock-body.tsx +0 -13
- package/template/old/src/lib/auth.ts +0 -89
- package/template/old/src/lib/cn.ts +0 -4
- package/template/old/src/lib/currentUser.ts +0 -8
- package/template/old/src/lib/delay.ts +0 -2
- package/template/old/src/lib/formatDate.ts +0 -11
- package/template/old/src/lib/generateLabel.ts +0 -5
- package/template/old/src/lib/s3Client.ts +0 -156
- package/template/old/src/lib/safe-action.ts +0 -19
- package/template/old/src/lib/siteConfig.ts +0 -4
- package/template/old/src/lib/sortOrderEnum.ts +0 -2
- package/template/old/src/use-case/role-permission.ts +0 -19
- package/template/old/src/use-case/role.ts +0 -110
- package/template/old/src/use-case/task.ts +0 -166
- package/template/old/src/use-case/user.ts +0 -121
- package/template/old/tailwind.config.ts +0 -81
- package/template/old/tsconfig.json +0 -47
package/cli.js
CHANGED
|
@@ -78,10 +78,6 @@ commander_1.program.action(() => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
78
78
|
{
|
|
79
79
|
name: 'base',
|
|
80
80
|
message: 'Base setup with Next 16, Better Auth, shadcn and Drizzle'
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: 'old',
|
|
84
|
-
message: 'old setup with ts, shadcn, auth, drizzle'
|
|
85
81
|
}
|
|
86
82
|
]
|
|
87
83
|
},
|
|
@@ -121,6 +117,11 @@ build
|
|
|
121
117
|
next-env.d.ts
|
|
122
118
|
.DS_Store
|
|
123
119
|
.env
|
|
120
|
+
`);
|
|
121
|
+
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'terraform', '.gitignore'), `.terraform
|
|
122
|
+
*.tfstate
|
|
123
|
+
*.tfstate.*
|
|
124
|
+
.terraform.lock.hcl
|
|
124
125
|
`);
|
|
125
126
|
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'README.md'), `# ${projectName}
|
|
126
127
|
|
package/cli.ts
CHANGED
|
@@ -75,10 +75,6 @@ program.action(async () => {
|
|
|
75
75
|
{
|
|
76
76
|
name: 'base',
|
|
77
77
|
message: 'Base setup with Next 16, Better Auth, shadcn and Drizzle'
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: 'old',
|
|
81
|
-
message: 'old setup with ts, shadcn, auth, drizzle'
|
|
82
78
|
}
|
|
83
79
|
]
|
|
84
80
|
},
|
|
@@ -132,6 +128,15 @@ next-env.d.ts
|
|
|
132
128
|
`
|
|
133
129
|
)
|
|
134
130
|
|
|
131
|
+
fs.writeFileSync(
|
|
132
|
+
path.join(projectDir, 'terraform', '.gitignore'),
|
|
133
|
+
`.terraform
|
|
134
|
+
*.tfstate
|
|
135
|
+
*.tfstate.*
|
|
136
|
+
.terraform.lock.hcl
|
|
137
|
+
`
|
|
138
|
+
)
|
|
139
|
+
|
|
135
140
|
fs.writeFileSync(
|
|
136
141
|
path.join(projectDir, 'README.md'),
|
|
137
142
|
`# ${projectName}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ej-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.0.0",
|
|
4
4
|
"description": "Bootstrap a next.js app with my preferred setup",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/ejekanshjain/create-ej-app#readme",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"commander": "^14.0.
|
|
33
|
+
"commander": "^14.0.3",
|
|
34
34
|
"copy-dir": "^1.3.0",
|
|
35
35
|
"enquirer": "^2.4.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^25.
|
|
38
|
+
"@types/node": "^25.2.3",
|
|
39
39
|
"typescript": "^5.9.3"
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -10,63 +10,57 @@
|
|
|
10
10
|
"analyze": "ANALYZE=true bun --bun next build --webpack",
|
|
11
11
|
"start": "bun --bun next start",
|
|
12
12
|
"lint": "eslint",
|
|
13
|
+
"type-check": "tsc --noEmit --incremental false",
|
|
13
14
|
"format": "prettier --write \"./src/**/*.{js,cjs,mjs,jsx,ts,tsx}\"",
|
|
14
15
|
"db:push": "drizzle-kit push",
|
|
15
16
|
"db:studio": "drizzle-kit studio",
|
|
16
17
|
"gen:auth": "bunx @better-auth/cli generate"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
20
|
+
"@ejekanshjain/simple-email-validator": "1.0.2",
|
|
19
21
|
"@hookform/resolvers": "5.2.2",
|
|
20
|
-
"@next/third-parties": "16.1.
|
|
21
|
-
"@paralleldrive/cuid2": "3.0
|
|
22
|
+
"@next/third-parties": "16.1.6",
|
|
23
|
+
"@paralleldrive/cuid2": "3.3.0",
|
|
22
24
|
"@radix-ui/react-label": "2.1.8",
|
|
23
25
|
"@radix-ui/react-separator": "1.1.8",
|
|
24
26
|
"@radix-ui/react-slot": "1.2.4",
|
|
25
27
|
"@t3-oss/env-nextjs": "0.13.10",
|
|
26
|
-
"@tanstack/react-query": "5.90.
|
|
27
|
-
"better-auth": "1.4.
|
|
28
|
+
"@tanstack/react-query": "5.90.21",
|
|
29
|
+
"better-auth": "1.4.18",
|
|
28
30
|
"class-variance-authority": "0.7.1",
|
|
29
31
|
"clsx": "2.1.1",
|
|
30
32
|
"drizzle-orm": "0.45.1",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"next": "16.1.1",
|
|
33
|
+
"lucide-react": "0.563.0",
|
|
34
|
+
"next": "16.1.6",
|
|
34
35
|
"next-safe-action": "8.0.11",
|
|
35
36
|
"next-themes": "0.4.6",
|
|
36
37
|
"nextjs-toploader": "3.9.17",
|
|
37
|
-
"nodemailer": "
|
|
38
|
-
"postgres": "3.4.
|
|
39
|
-
"react": "19.2.
|
|
40
|
-
"react-dom": "19.2.
|
|
41
|
-
"react-hook-form": "7.
|
|
38
|
+
"nodemailer": "8.0.1",
|
|
39
|
+
"postgres": "3.4.8",
|
|
40
|
+
"react": "19.2.4",
|
|
41
|
+
"react-dom": "19.2.4",
|
|
42
|
+
"react-hook-form": "7.71.1",
|
|
42
43
|
"sonner": "2.0.7",
|
|
43
44
|
"tailwind-merge": "3.4.0",
|
|
44
45
|
"tw-animate-css": "1.4.0",
|
|
45
|
-
"workflow": "4.0
|
|
46
|
-
"zod": "4.
|
|
46
|
+
"workflow": "4.1.0-beta.55",
|
|
47
|
+
"zod": "4.3.6"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
|
-
"@next/bundle-analyzer": "16.1.
|
|
50
|
+
"@next/bundle-analyzer": "16.1.6",
|
|
50
51
|
"@tailwindcss/postcss": "4.1.18",
|
|
51
|
-
"@types/bun": "1.3.
|
|
52
|
-
"@types/nodemailer": "7.0.
|
|
53
|
-
"@types/react": "19.2.
|
|
52
|
+
"@types/bun": "1.3.9",
|
|
53
|
+
"@types/nodemailer": "7.0.9",
|
|
54
|
+
"@types/react": "19.2.14",
|
|
54
55
|
"@types/react-dom": "19.2.3",
|
|
55
56
|
"babel-plugin-react-compiler": "1.0.0",
|
|
56
|
-
"drizzle-kit": "0.31.
|
|
57
|
+
"drizzle-kit": "0.31.9",
|
|
57
58
|
"eslint": "9.39.2",
|
|
58
|
-
"eslint-config-next": "16.1.
|
|
59
|
-
"prettier": "3.
|
|
59
|
+
"eslint-config-next": "16.1.6",
|
|
60
|
+
"prettier": "3.8.1",
|
|
60
61
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
61
62
|
"prettier-plugin-tailwindcss": "0.7.2",
|
|
62
63
|
"tailwindcss": "4.1.18",
|
|
63
64
|
"typescript": "5.9.3"
|
|
64
|
-
}
|
|
65
|
-
"trustedDependencies": [
|
|
66
|
-
"@swc/core",
|
|
67
|
-
"@tailwindcss/oxide",
|
|
68
|
-
"esbuild",
|
|
69
|
-
"sharp",
|
|
70
|
-
"unrs-resolver"
|
|
71
|
-
]
|
|
65
|
+
}
|
|
72
66
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateEmail } from '@ejekanshjain/simple-email-validator'
|
|
1
2
|
import { createId } from '@paralleldrive/cuid2'
|
|
2
3
|
import { APIError, betterAuth } from 'better-auth'
|
|
3
4
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
lastLoginMethod,
|
|
8
9
|
magicLink
|
|
9
10
|
} from 'better-auth/plugins'
|
|
10
|
-
import { isFakeEmail } from 'fakefilter'
|
|
11
11
|
import { headers } from 'next/headers'
|
|
12
12
|
import { cache } from 'react'
|
|
13
13
|
import { db } from '~/db'
|
|
@@ -68,12 +68,21 @@ export const auth = betterAuth({
|
|
|
68
68
|
before: createAuthMiddleware(async ctx => {
|
|
69
69
|
if (
|
|
70
70
|
ctx.path === '/sign-in/magic-link' &&
|
|
71
|
-
ctx.body?.email
|
|
72
|
-
isFakeEmail(ctx.body.email)
|
|
71
|
+
ctx.body?.email
|
|
73
72
|
) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
try {
|
|
74
|
+
const validation = await validateEmail({
|
|
75
|
+
email: ctx.body.email
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if (validation.isValid === false) {
|
|
79
|
+
throw new APIError('BAD_REQUEST', {
|
|
80
|
+
message: 'Please use a valid email address.'
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error('Email validation failed', err)
|
|
85
|
+
}
|
|
77
86
|
}
|
|
78
87
|
})
|
|
79
88
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
resource "google_compute_backend_service" "my_app_prod_service" {
|
|
2
|
+
name = "my-app-prod-service"
|
|
3
|
+
|
|
4
|
+
backend {
|
|
5
|
+
group = google_compute_region_network_endpoint_group.my_app_prod_neg.id
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
protocol = "HTTPS"
|
|
9
|
+
locality_lb_policy = "ROUND_ROBIN"
|
|
10
|
+
load_balancing_scheme = "EXTERNAL_MANAGED"
|
|
11
|
+
|
|
12
|
+
depends_on = [google_compute_region_network_endpoint_group.my_app_prod_neg]
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
gcloud run revisions list \
|
|
2
|
+
--region=us-central1 \
|
|
3
|
+
--format=json | \
|
|
4
|
+
jq -r '.[] | select(.status.conditions[] | select(.type == "Active" and .status != "True")) | .metadata.name' | \
|
|
5
|
+
sort -u | \
|
|
6
|
+
xargs -I {} gcloud run revisions delete {} \
|
|
7
|
+
--region=us-central1 --quiet
|
|
8
|
+
|
|
9
|
+
gcloud artifacts docker images list us-docker.pkg.dev/project-id/github/my-app --format=json --include-tags | jq -r '.[] | select((.tags // []) as $tags | ($tags | index("main") // -1) == -1 and ($tags | index("dev") // -1) == -1) | .version' | while read -r digest; do echo "Deleting image with digest: $digest"; gcloud artifacts docker images delete us-docker.pkg.dev/project-id/github/my-app@$digest --delete-tags --quiet || true; done
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
resource "google_cloudbuild_trigger" "my_app_main_build_trigger" {
|
|
2
|
+
name = "my-app-main-build-trigger"
|
|
3
|
+
description = "Build trigger for My App"
|
|
4
|
+
location = "global"
|
|
5
|
+
include_build_logs = "INCLUDE_BUILD_LOGS_WITH_STATUS"
|
|
6
|
+
disabled = false
|
|
7
|
+
|
|
8
|
+
github {
|
|
9
|
+
owner = "github-username"
|
|
10
|
+
name = "my-app"
|
|
11
|
+
push {
|
|
12
|
+
branch = "^main$"
|
|
13
|
+
invert_regex = false
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
substitutions = {
|
|
18
|
+
_IMAGE_REGION = "us-docker.pkg.dev"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
service_account = "projects/${var.project}/serviceAccounts/${var.compute_service_account}"
|
|
22
|
+
|
|
23
|
+
build {
|
|
24
|
+
timeout = "600s"
|
|
25
|
+
step {
|
|
26
|
+
name = "gcr.io/cloud-builders/docker"
|
|
27
|
+
args = [
|
|
28
|
+
"build",
|
|
29
|
+
"-t",
|
|
30
|
+
"$${_IMAGE_REGION}/$${PROJECT_ID}/github/my-app:$${COMMIT_SHA}",
|
|
31
|
+
"-t",
|
|
32
|
+
"$${_IMAGE_REGION}/$${PROJECT_ID}/github/my-app:main",
|
|
33
|
+
"."
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
step {
|
|
38
|
+
name = "gcr.io/cloud-builders/docker"
|
|
39
|
+
args = [
|
|
40
|
+
"push",
|
|
41
|
+
"$${_IMAGE_REGION}/$${PROJECT_ID}/github/my-app:$${COMMIT_SHA}"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
step {
|
|
46
|
+
name = "gcr.io/cloud-builders/docker"
|
|
47
|
+
args = [
|
|
48
|
+
"push",
|
|
49
|
+
"$${_IMAGE_REGION}/$${PROJECT_ID}/github/my-app:main"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
step {
|
|
54
|
+
name = "gcr.io/cloud-builders/gcloud"
|
|
55
|
+
args = [
|
|
56
|
+
"run",
|
|
57
|
+
"deploy",
|
|
58
|
+
"my-app-prod",
|
|
59
|
+
"--image",
|
|
60
|
+
"$${_IMAGE_REGION}/$${PROJECT_ID}/github/my-app:$${COMMIT_SHA}",
|
|
61
|
+
"--region",
|
|
62
|
+
var.region
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
options {
|
|
67
|
+
logging = "CLOUD_LOGGING_ONLY"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
resource "google_cloud_run_v2_service" "my_app_prod" {
|
|
2
|
+
name = "my-app-prod"
|
|
3
|
+
location = var.region
|
|
4
|
+
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
|
|
5
|
+
|
|
6
|
+
scaling {
|
|
7
|
+
max_instance_count = 1
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
template {
|
|
11
|
+
execution_environment = "EXECUTION_ENVIRONMENT_GEN2"
|
|
12
|
+
service_account = var.compute_service_account
|
|
13
|
+
max_instance_request_concurrency = 80
|
|
14
|
+
|
|
15
|
+
vpc_access {
|
|
16
|
+
network_interfaces {
|
|
17
|
+
network = google_compute_network.vpc_network.name
|
|
18
|
+
subnetwork = google_compute_network.vpc_network.name
|
|
19
|
+
}
|
|
20
|
+
egress = "PRIVATE_RANGES_ONLY"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
containers {
|
|
24
|
+
name = "my-app-prod-1"
|
|
25
|
+
image = "us-docker.pkg.dev/${var.project}/github/my-app:main"
|
|
26
|
+
|
|
27
|
+
ports {
|
|
28
|
+
container_port = 3000
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
resources {
|
|
32
|
+
limits = {
|
|
33
|
+
cpu = "1000m"
|
|
34
|
+
memory = "1Gi"
|
|
35
|
+
}
|
|
36
|
+
cpu_idle = true
|
|
37
|
+
startup_cpu_boost = true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
env {
|
|
41
|
+
name = "APP_ENV"
|
|
42
|
+
value = "production"
|
|
43
|
+
}
|
|
44
|
+
env {
|
|
45
|
+
name = "DATABASE_URL"
|
|
46
|
+
value_source {
|
|
47
|
+
secret_key_ref {
|
|
48
|
+
secret = "PROD_DATABASE_URL"
|
|
49
|
+
version = "latest"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Add rest of env variables as needed
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
depends_on = [google_sql_database.prod_db]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
resource "google_cloud_run_service_iam_member" "my_app_prod_unauthenticated" {
|
|
62
|
+
location = google_cloud_run_v2_service.my_app_prod.location
|
|
63
|
+
service = google_cloud_run_v2_service.my_app_prod.name
|
|
64
|
+
role = "roles/run.invoker"
|
|
65
|
+
member = "allUsers"
|
|
66
|
+
|
|
67
|
+
depends_on = [google_cloud_run_v2_service.my_app_prod]
|
|
68
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
resource "google_sql_database_instance" "prod_postgres_instance" {
|
|
2
|
+
name = "prod-db"
|
|
3
|
+
database_version = "POSTGRES_17"
|
|
4
|
+
|
|
5
|
+
settings {
|
|
6
|
+
location_preference {
|
|
7
|
+
zone = var.zone
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
edition = "ENTERPRISE"
|
|
11
|
+
tier = "db-f1-micro"
|
|
12
|
+
deletion_protection_enabled = true
|
|
13
|
+
|
|
14
|
+
disk_size = 10
|
|
15
|
+
disk_autoresize = true
|
|
16
|
+
retain_backups_on_delete = true
|
|
17
|
+
|
|
18
|
+
backup_configuration {
|
|
19
|
+
enabled = true
|
|
20
|
+
start_time = "00:00"
|
|
21
|
+
point_in_time_recovery_enabled = true
|
|
22
|
+
backup_retention_settings {
|
|
23
|
+
retained_backups = 30
|
|
24
|
+
}
|
|
25
|
+
transaction_log_retention_days = 7
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ip_configuration {
|
|
29
|
+
private_network = google_compute_network.vpc_network.self_link
|
|
30
|
+
ipv4_enabled = true
|
|
31
|
+
authorized_networks {
|
|
32
|
+
value = "103.53.75.58"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
insights_config {
|
|
37
|
+
query_insights_enabled = true
|
|
38
|
+
record_client_address = true
|
|
39
|
+
record_application_tags = true
|
|
40
|
+
query_string_length = 4096
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
lifecycle {
|
|
45
|
+
prevent_destroy = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
depends_on = [google_service_networking_connection.private_connection]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
resource "google_sql_database" "prod_db" {
|
|
52
|
+
name = "prod_database"
|
|
53
|
+
instance = google_sql_database_instance.prod_postgres_instance.name
|
|
54
|
+
|
|
55
|
+
lifecycle {
|
|
56
|
+
prevent_destroy = true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
depends_on = [google_sql_database_instance.prod_postgres_instance]
|
|
60
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
resource "google_compute_global_forwarding_rule" "main_lb_https_ipv4_frontend" {
|
|
2
|
+
name = "main-lb-https-ipv4-frontend"
|
|
3
|
+
ip_protocol = "TCP"
|
|
4
|
+
ip_address = google_compute_global_address.main_lb_ipv4.address
|
|
5
|
+
load_balancing_scheme = "EXTERNAL_MANAGED"
|
|
6
|
+
port_range = "443"
|
|
7
|
+
target = google_compute_target_https_proxy.main_lb_target_proxy.id
|
|
8
|
+
|
|
9
|
+
depends_on = [google_compute_global_address.main_lb_ipv4, google_compute_target_https_proxy.main_lb_target_proxy]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
resource "google_compute_global_forwarding_rule" "main_lb_https_ipv6_frontend" {
|
|
13
|
+
name = "main-lb-https-ipv6-frontend"
|
|
14
|
+
ip_protocol = "TCP"
|
|
15
|
+
ip_address = google_compute_global_address.main_lb_ipv6.address
|
|
16
|
+
load_balancing_scheme = "EXTERNAL_MANAGED"
|
|
17
|
+
port_range = "443"
|
|
18
|
+
target = google_compute_target_https_proxy.main_lb_target_proxy.id
|
|
19
|
+
|
|
20
|
+
depends_on = [google_compute_global_address.main_lb_ipv6, google_compute_target_https_proxy.main_lb_target_proxy]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resource "google_compute_global_forwarding_rule" "main_lb_ipv4_http_forwarding_rule" {
|
|
24
|
+
name = "main-lb-ipv4-http-forwarding-rule"
|
|
25
|
+
ip_protocol = "TCP"
|
|
26
|
+
ip_address = google_compute_global_address.main_lb_ipv4.address
|
|
27
|
+
load_balancing_scheme = "EXTERNAL_MANAGED"
|
|
28
|
+
port_range = "80"
|
|
29
|
+
target = google_compute_target_http_proxy.main_lb_http_redirect_proxy.id
|
|
30
|
+
|
|
31
|
+
depends_on = [google_compute_global_forwarding_rule.main_lb_https_ipv4_frontend]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
resource "google_compute_global_forwarding_rule" "main_lb_ipv6_http_forwarding_rule" {
|
|
35
|
+
name = "main-lb-ipv6-http-forwarding-rule"
|
|
36
|
+
ip_protocol = "TCP"
|
|
37
|
+
ip_address = google_compute_global_address.main_lb_ipv6.address
|
|
38
|
+
load_balancing_scheme = "EXTERNAL_MANAGED"
|
|
39
|
+
port_range = "80"
|
|
40
|
+
target = google_compute_target_http_proxy.main_lb_http_redirect_proxy.id
|
|
41
|
+
|
|
42
|
+
depends_on = [google_compute_global_forwarding_rule.main_lb_https_ipv6_frontend]
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
resource "google_compute_global_address" "main_lb_ipv4" {
|
|
2
|
+
name = "main-lb-ipv4"
|
|
3
|
+
ip_version = "IPV4"
|
|
4
|
+
address_type = "EXTERNAL"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
resource "google_compute_global_address" "main_lb_ipv6" {
|
|
8
|
+
name = "main-lb-ipv6"
|
|
9
|
+
ip_version = "IPV6"
|
|
10
|
+
address_type = "EXTERNAL"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
resource "google_compute_region_network_endpoint_group" "my_app_prod_neg" {
|
|
2
|
+
name = "my-app-prod-neg"
|
|
3
|
+
network_endpoint_type = "SERVERLESS"
|
|
4
|
+
region = var.region
|
|
5
|
+
|
|
6
|
+
cloud_run {
|
|
7
|
+
service = google_cloud_run_v2_service.my_app_prod.name
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
depends_on = [google_cloud_run_v2_service.my_app_prod]
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
resource "google_compute_network" "vpc_network" {
|
|
2
|
+
name = "terraform-network"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
resource "google_compute_global_address" "private_ip_range" {
|
|
6
|
+
name = "private-ip-range"
|
|
7
|
+
prefix_length = 16
|
|
8
|
+
address_type = "INTERNAL"
|
|
9
|
+
purpose = "VPC_PEERING"
|
|
10
|
+
network = google_compute_network.vpc_network.self_link
|
|
11
|
+
|
|
12
|
+
depends_on = [google_compute_network.vpc_network]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resource "google_service_networking_connection" "private_connection" {
|
|
16
|
+
network = google_compute_network.vpc_network.id
|
|
17
|
+
service = "servicenetworking.googleapis.com"
|
|
18
|
+
reserved_peering_ranges = [google_compute_global_address.private_ip_range.name]
|
|
19
|
+
|
|
20
|
+
depends_on = [google_compute_global_address.private_ip_range]
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
google = {
|
|
4
|
+
source = "hashicorp/google"
|
|
5
|
+
version = "7.8.0"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
backend "gcs" {
|
|
10
|
+
bucket = "project-id-tf-state"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
provider "google" {
|
|
15
|
+
project = var.project
|
|
16
|
+
region = var.region
|
|
17
|
+
zone = var.zone
|
|
18
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
echo "Starting pre.sh script..."
|
|
2
|
+
|
|
3
|
+
gcloud config set project project-id
|
|
4
|
+
|
|
5
|
+
gcloud auth application-default set-quota-project project-id
|
|
6
|
+
|
|
7
|
+
echo "Setting up Google Cloud SDK and enabling required APIs..."
|
|
8
|
+
|
|
9
|
+
gcloud services enable logging.googleapis.com monitoring.googleapis.com compute.googleapis.com run.googleapis.com artifactregistry.googleapis.com vpcaccess.googleapis.com cloudbuild.googleapis.com servicenetworking.googleapis.com networkconnectivity.googleapis.com certificatemanager.googleapis.com sql-component.googleapis.com storage-component.googleapis.com storage.googleapis.com cloudapis.googleapis.com secretmanager.googleapis.com
|
|
10
|
+
|
|
11
|
+
echo "Enabling APIs completed."
|
|
12
|
+
|
|
13
|
+
echo "Please manually create a db secret in Secret Manager with names in file secret.txt"
|
|
14
|
+
|
|
15
|
+
echo "Creating storage bucket for Terraform state..."
|
|
16
|
+
|
|
17
|
+
gcloud storage buckets create gs://project-id-tf-state --location=us
|
|
18
|
+
|
|
19
|
+
gcloud storage buckets update gs://project-id-tf-state --versioning
|
|
20
|
+
|
|
21
|
+
echo "Creating storage bucket for Terraform state completed."
|
|
22
|
+
|
|
23
|
+
echo "Please manually connect github repositories on cloud build repositories from here https://console.cloud.google.com/cloud-build/repositories/1st-gen"
|
|
24
|
+
|
|
25
|
+
echo "And then copy the service account (eg:- 123456-compute@developer.gserviceaccount.com) to vars terraform.tfvars file from this link https://console.cloud.google.com/iam-admin/iam"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
resource "google_certificate_manager_certificate" "my_app_cert" {
|
|
2
|
+
name = "my-app-cert"
|
|
3
|
+
|
|
4
|
+
managed {
|
|
5
|
+
domains = [
|
|
6
|
+
"myapp.com"
|
|
7
|
+
]
|
|
8
|
+
dns_authorizations = [
|
|
9
|
+
google_certificate_manager_dns_authorization.my_app_prod_auth.id,
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
depends_on = [
|
|
14
|
+
google_certificate_manager_dns_authorization.my_app_prod_auth,
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
resource "google_certificate_manager_dns_authorization" "my_app_prod_auth" {
|
|
19
|
+
name = "my-app-prod-auth"
|
|
20
|
+
domain = "myapp.com"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resource "google_certificate_manager_certificate_map" "default" {
|
|
24
|
+
name = "default-cert-map"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
resource "google_certificate_manager_certificate_map_entry" "my_app_prod_entry" {
|
|
28
|
+
name = "my-app-prod-entry"
|
|
29
|
+
map = google_certificate_manager_certificate_map.default.name
|
|
30
|
+
hostname = "myapp.com"
|
|
31
|
+
certificates = [google_certificate_manager_certificate.my_app_cert.id]
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
resource "google_compute_target_https_proxy" "main_lb_target_proxy" {
|
|
2
|
+
name = "main-lb-target-proxy"
|
|
3
|
+
url_map = google_compute_url_map.main_lb.id
|
|
4
|
+
certificate_map = "//certificatemanager.googleapis.com/projects/${var.project}/locations/global/certificateMaps/${google_certificate_manager_certificate_map.default.name}"
|
|
5
|
+
|
|
6
|
+
depends_on = [google_compute_url_map.main_lb]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
resource "google_compute_target_http_proxy" "main_lb_http_redirect_proxy" {
|
|
10
|
+
name = "main-lb-http-redirect-proxy"
|
|
11
|
+
url_map = google_compute_url_map.main_lb_http_redirect.id
|
|
12
|
+
|
|
13
|
+
depends_on = [google_compute_url_map.main_lb_http_redirect]
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
resource "google_compute_target_https_proxy" "main_lb_target_proxy" {
|
|
2
|
+
name = "main-lb-target-proxy"
|
|
3
|
+
url_map = google_compute_url_map.main_lb.id
|
|
4
|
+
certificate_map = "//certificatemanager.googleapis.com/projects/${var.project}/locations/global/certificateMaps/${google_certificate_manager_certificate_map.default.name}"
|
|
5
|
+
|
|
6
|
+
depends_on = [google_compute_url_map.main_lb]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
resource "google_compute_target_http_proxy" "main_lb_http_redirect_proxy" {
|
|
10
|
+
name = "main-lb-http-redirect-proxy"
|
|
11
|
+
url_map = google_compute_url_map.main_lb_http_redirect.id
|
|
12
|
+
|
|
13
|
+
depends_on = [google_compute_url_map.main_lb_http_redirect]
|
|
14
|
+
}
|