Skip to content

Commit 351974c

Browse files
author
Caleb Barnes
committed
move urls to constants with env overrides / cleanup logging / remove unused function / add --yes option to use defaults and --overwrite to overwrite files
1 parent 7503cb0 commit 351974c

File tree

4 files changed

+248
-179
lines changed

4 files changed

+248
-179
lines changed

src/commands/database/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const NEON_DATABASE_EXTENSION_SLUG = process.env.NEON_DATABASE_EXTENSION_SLUG ?? '7jjmnqyo-netlify-neon'
2+
export const JIGSAW_URL = process.env.JIGSAW_URL ?? 'https://api.netlifysdk.com'
3+
export const NETLIFY_WEB_UI = process.env.NETLIFY_WEB_UI ?? 'https://app.netlify.com'

src/commands/database/database.ts

Lines changed: 148 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,242 @@
11
import { OptionValues } from 'commander'
22
import inquirer from 'inquirer'
33
import BaseCommand from '../base-command.js'
4-
import { getExtension, getExtensionInstallations, getSiteConfiguration, installExtension } from './utils.js'
4+
import { getAccount, getExtension, getSiteConfiguration, installExtension } from './utils.js'
55
import { initDrizzle } from './drizzle.js'
6+
import { NEON_DATABASE_EXTENSION_SLUG } from './constants.js'
7+
import prettyjson from 'prettyjson'
8+
import { chalk, log } from '../../utils/command-helpers.js'
69

7-
const NETLIFY_DATABASE_EXTENSION_SLUG = '7jjmnqyo-netlify-neon'
10+
type SiteInfo = {
11+
id: string
12+
name: string
13+
account_id: string
14+
admin_url: string
15+
url: string
16+
ssl_url: string
17+
}
818

919
const init = async (_options: OptionValues, command: BaseCommand) => {
20+
const siteInfo = command.netlify.siteInfo as SiteInfo
1021
if (!command.siteId) {
1122
console.error(`The project must be linked with netlify link before initializing a database.`)
1223
return
1324
}
1425

1526
const initialOpts = command.opts()
1627

17-
if (initialOpts.drizzle !== false && initialOpts.drizzle !== true) {
18-
const answers = await inquirer.prompt(
19-
[
20-
{
21-
type: 'confirm',
22-
name: 'drizzle',
23-
message: 'Use Drizzle?',
24-
},
25-
].filter((q) => !initialOpts[q.name]),
26-
)
27-
28-
if (!initialOpts.drizzle) {
29-
command.setOptionValue('drizzle', answers.drizzle)
28+
/**
29+
* Only prompt for drizzle if the user did not pass in the `--drizzle` or `--no-drizzle` option
30+
*/
31+
if (initialOpts.drizzle !== false && initialOpts.drizzle !== true && !initialOpts.yes) {
32+
type Answers = {
33+
drizzle: boolean
3034
}
35+
const answers = await inquirer.prompt<Answers>([
36+
{
37+
type: 'confirm',
38+
name: 'drizzle',
39+
message: 'Use Drizzle?',
40+
},
41+
])
42+
command.setOptionValue('drizzle', answers.drizzle)
3143
}
32-
const opts = command.opts()
33-
if (opts.drizzle && command.project.root) {
44+
45+
const opts = command.opts<{
46+
drizzle?: boolean | undefined
47+
/**
48+
* Skip prompts and use default values (answer yes to all prompts)
49+
*/
50+
yes?: true | undefined
51+
}>()
52+
53+
if (opts.drizzle || (opts.yes && opts.drizzle !== false)) {
3454
await initDrizzle(command)
3555
}
3656

3757
if (!command.netlify.api.accessToken) {
38-
throw new Error(`No access token found, please login with netlify login`)
58+
throw new Error(`Please login with netlify login before running this command`)
3959
}
4060

41-
let site: Awaited<ReturnType<typeof command.netlify.api.getSite>>
42-
try {
43-
// @ts-expect-error -- feature_flags is not in the types
44-
site = await command.netlify.api.getSite({ siteId: command.siteId, feature_flags: 'cli' })
45-
} catch (e) {
46-
throw new Error(`Error getting site, make sure you are logged in with netlify login`, {
47-
cause: e,
48-
})
49-
}
50-
// console.log('site', site)
51-
if (!site.account_id) {
52-
throw new Error(`No account id found for site ${command.siteId}`)
61+
// let site: Awaited<ReturnType<typeof command.netlify.api.getSite>>
62+
// try {
63+
// site = await command.netlify.api.getSite({
64+
// siteId: command.siteId,
65+
// // @ts-expect-error -- feature_flags is not in the types
66+
// feature_flags: 'cli',
67+
// })
68+
// } catch (e) {
69+
// throw new Error(`Error getting site, make sure you are logged in with netlify login`, {
70+
// cause: e,
71+
// })
72+
// }
73+
if (!siteInfo.account_id || !siteInfo.name) {
74+
throw new Error(`Error getting site, make sure you are logged in with netlify login`)
5375
}
5476

55-
console.log(`Initializing a new database for site: ${command.siteId} on account ${site.account_id}
56-
Please wait...`)
77+
const account = await getAccount(command, { accountId: siteInfo.account_id })
78+
79+
log(`Initializing a new database...`)
5780

5881
const netlifyToken = command.netlify.api.accessToken.replace('Bearer ', '')
5982
const extension = await getExtension({
60-
accountId: site.account_id,
83+
accountId: siteInfo.account_id,
6184
token: netlifyToken,
62-
slug: NETLIFY_DATABASE_EXTENSION_SLUG,
85+
slug: NEON_DATABASE_EXTENSION_SLUG,
6386
})
64-
6587
if (!extension?.hostSiteUrl) {
6688
throw new Error(`Failed to get extension host site url when installing extension`)
6789
}
6890

69-
if (!extension.installedOnTeam) {
70-
const answers = await inquirer.prompt([
91+
const installNeonExtension = async () => {
92+
if (!siteInfo.account_id || !account.name || !extension.name || !extension.hostSiteUrl) {
93+
throw new Error(`Failed to install extension "${extension.name}"`)
94+
}
95+
const installed = await installExtension({
96+
accountId: siteInfo.account_id,
97+
token: netlifyToken,
98+
slug: NEON_DATABASE_EXTENSION_SLUG,
99+
hostSiteUrl: extension.hostSiteUrl,
100+
})
101+
if (!installed) {
102+
throw new Error(`Failed to install extension on team "${account.name}": "${extension.name}"`)
103+
}
104+
log(`Extension "${extension.name}" successfully installed on team "${account.name}"`)
105+
}
106+
107+
if (!extension.installedOnTeam && !opts.yes) {
108+
type Answers = {
109+
installExtension: boolean
110+
}
111+
const answers = await inquirer.prompt<Answers>([
71112
{
72113
type: 'confirm',
73114
name: 'installExtension',
74-
message: `Netlify Database extension is not installed on team ${site.account_id}, would you like to install it now?`,
115+
message: `The required extension "${extension.name}" is not installed on team "${account.name}", would you like to install it now?`,
75116
},
76117
])
77118
if (answers.installExtension) {
78-
const installed = await installExtension({
79-
accountId: site.account_id,
80-
token: netlifyToken,
81-
slug: NETLIFY_DATABASE_EXTENSION_SLUG,
82-
hostSiteUrl: extension.hostSiteUrl ?? '',
83-
})
84-
if (!installed) {
85-
throw new Error(`Failed to install extension on team ${site.account_id}: ${NETLIFY_DATABASE_EXTENSION_SLUG}`)
86-
}
87-
console.log(`Netlify Database extension installed on team ${site.account_id}`)
119+
await installNeonExtension()
88120
} else {
89121
return
90122
}
91123
}
124+
if (!extension.installedOnTeam && opts.yes) {
125+
await installNeonExtension()
126+
}
92127

93128
try {
94129
const siteEnv = await command.netlify.api.getEnvVar({
95-
accountId: site.account_id,
130+
accountId: siteInfo.account_id,
96131
siteId: command.siteId,
97132
key: 'NETLIFY_DATABASE_URL',
98133
})
99134

100135
if (siteEnv.key === 'NETLIFY_DATABASE_URL') {
101-
console.error(`Database already initialized for site: ${command.siteId}`)
136+
log(`Environment variable "NETLIFY_DATABASE_URL" already exists on site: ${siteInfo.name}`)
137+
log(`You can run "netlify db status" to check the status for this site`)
102138
return
103139
}
104140
} catch {
105141
// no op, env var does not exist, so we just continue
106142
}
107143

108-
const extensionSiteUrl = process.env.UNSTABLE_NETLIFY_DATABASE_EXTENSION_HOST_SITE_URL ?? extension.hostSiteUrl
144+
const hostSiteUrl = process.env.NEON_DATABASE_EXTENSION_HOST_SITE_URL ?? extension.hostSiteUrl
145+
const initEndpoint = new URL('/cli-db-init', hostSiteUrl).toString()
109146

110-
const initEndpoint = new URL('/cli-db-init', extensionSiteUrl).toString()
111-
console.log('initEndpoint', initEndpoint)
112147
const req = await fetch(initEndpoint, {
113148
method: 'POST',
114149
headers: {
115150
'Content-Type': 'application/json',
116151
'nf-db-token': netlifyToken,
117152
'nf-db-site-id': command.siteId,
118-
'nf-db-account-id': site.account_id,
153+
'nf-db-account-id': siteInfo.account_id,
119154
},
120155
})
121156

122157
if (!req.ok) {
123158
throw new Error(`Failed to initialize DB: ${await req.text()}`)
124159
}
125160

126-
const res = await req.json()
127-
console.log(res)
161+
const res = (await req.json()) as {
162+
code?: string
163+
message?: string
164+
}
165+
if (res.code === 'DATABASE_INITIALIZED') {
166+
if (res.message) {
167+
log(res.message)
168+
}
169+
170+
log(
171+
prettyjson.render({
172+
'Current team': account.name,
173+
'Current site': siteInfo.name,
174+
[`${extension.name} extension`]: 'installed',
175+
Database: 'connected',
176+
'Site environment variable': 'NETLIFY_DATABASE_URL',
177+
}),
178+
)
179+
}
128180
return
129181
}
130182

131183
const status = async (_options: OptionValues, command: BaseCommand) => {
184+
const siteInfo = command.netlify.siteInfo as SiteInfo
132185
if (!command.siteId) {
133-
console.error(`The project must be linked with netlify link before initializing a database.`)
134-
return
186+
throw new Error(`The project must be linked with netlify link before initializing a database.`)
135187
}
136-
// check if this site has a db initialized
137-
const site = await command.netlify.api.getSite({ siteId: command.siteId })
138-
if (!site.account_id) {
188+
if (!siteInfo.account_id) {
139189
throw new Error(`No account id found for site ${command.siteId}`)
140190
}
141191
if (!command.netlify.api.accessToken) {
142192
throw new Error(`You must be logged in with netlify login to check the status of the database`)
143193
}
144194
const netlifyToken = command.netlify.api.accessToken.replace('Bearer ', '')
145-
const extensionInstallation = await getExtensionInstallations({
146-
accountId: site.account_id,
147-
siteId: command.siteId,
148-
token: netlifyToken,
149-
})
150195

151-
if (!extensionInstallation) {
152-
console.log(`Netlify Database extension not installed on team ${site.account_id}`)
153-
return
196+
const account = await getAccount(command, { accountId: siteInfo.account_id })
197+
198+
let siteEnv: Awaited<ReturnType<typeof command.netlify.api.getEnvVar>> | undefined
199+
try {
200+
siteEnv = await command.netlify.api.getEnvVar({
201+
accountId: siteInfo.account_id,
202+
siteId: command.siteId,
203+
key: 'NETLIFY_DATABASE_URL',
204+
})
205+
} catch {
206+
// no-op, env var does not exist, so we just continue
154207
}
155208

156-
const siteConfig = await getSiteConfiguration({
157-
siteId: command.siteId,
158-
accountId: site.account_id,
159-
slug: NETLIFY_DATABASE_EXTENSION_SLUG,
209+
const extension = await getExtension({
210+
accountId: account.id,
160211
token: netlifyToken,
212+
slug: NEON_DATABASE_EXTENSION_SLUG,
161213
})
162-
163-
if (!siteConfig) {
164-
throw new Error(`Failed to get site configuration for site ${command.siteId}`)
165-
}
214+
let siteConfig
166215
try {
167-
const siteEnv = await command.netlify.api.getEnvVar({
168-
accountId: site.account_id,
216+
siteConfig = await getSiteConfiguration({
169217
siteId: command.siteId,
170-
key: 'NETLIFY_DATABASE_URL',
218+
accountId: siteInfo.account_id,
219+
slug: NEON_DATABASE_EXTENSION_SLUG,
220+
token: netlifyToken,
171221
})
172-
173-
if (siteEnv.key === 'NETLIFY_DATABASE_URL') {
174-
console.log(`Database initialized for site: ${command.siteId}`)
175-
return
176-
}
177222
} catch {
178-
throw new Error(
179-
`Database not initialized for site: ${command.siteId}.
180-
Run 'netlify db init' to initialize a database`,
181-
)
223+
// no-op, site config does not exist or extension not installed
182224
}
225+
226+
log(
227+
prettyjson.render({
228+
'Current team': account.name,
229+
'Current site': siteInfo.name,
230+
[extension?.name ? `${extension.name} extension` : 'Database extension']: extension?.installedOnTeam
231+
? 'installed'
232+
: chalk.red('not installed'),
233+
// @ts-expect-error -- siteConfig is not typed
234+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
235+
Database: siteConfig?.config?.neonProjectId ? 'connected' : chalk.red('not connected'),
236+
'Site environment variable':
237+
siteEnv?.key === 'NETLIFY_DATABASE_URL' ? 'NETLIFY_DATABASE_URL' : chalk.red('NETLIFY_DATABASE_URL not set'),
238+
}),
239+
)
183240
}
184241

185242
export const createDatabaseCommand = (program: BaseCommand) => {
@@ -190,6 +247,8 @@ export const createDatabaseCommand = (program: BaseCommand) => {
190247
.description('Initialize a new database')
191248
.option('--drizzle', 'Sets up drizzle-kit and drizzle-orm in your project')
192249
.option('--no-drizzle', 'Skips drizzle')
250+
.option('-y, --yes', 'Skip prompts and use default values')
251+
.option('-o, --overwrite', 'Overwrites existing files that would be created when setting up drizzle')
193252
.action(init)
194253

195254
dbCommand.command('status').description('Check the status of the database').action(status)

0 commit comments

Comments
 (0)