-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy pathindex.ts
217 lines (199 loc) · 7.82 KB
/
index.ts
1
2
3
4
5
6
7
8
9
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import { defineNuxtModule, createResolver, logger, addServerScanDir, installModule } from '@nuxt/kit'
import { join } from 'pathe'
import { defu } from 'defu'
import { mkdir, writeFile, readFile } from 'node:fs/promises'
import { findWorkspaceDir } from 'pkg-types'
import { readUser } from 'rc9'
import { $fetch } from 'ofetch'
import { joinURL } from 'ufo'
import { generateWrangler } from './utils'
import { version } from '../../package.json'
import { argv } from 'node:process'
const log = logger.withScope('nuxt:hub')
export interface ModuleOptions {
/**
* Set to `true` to use the remote storage.
* Only set to `true` in your nuxt.config on a projet you are not deploying to NuxtHub
* @default process.env.NUXT_HUB_REMOTE or --remote option when running `nuxt dev`
* @see https://hub.nuxt.com/docs/getting-started/installation#options
*/
remote?: boolean
/**
* The URL of the NuxtHub Console
* @default 'https://console.hub.nuxt.com'
*/
url?: string
/**
* The project's key on the NuxtHub platform, added with `nuxthub link`.
* @default process.env.NUXT_HUB_PROJECT_KEY
*/
projectKey?: string
/**
* The user token to access the NuxtHub platform, added with `nuxthub login`
* @default process.env.NUXT_HUB_USER_TOKEN
*/
userToken?: string
/**
* The URL of the deployed project, used to fetch the remote storage, a projectKey must be defined as well
* @default process.env.NUXT_HUB_PROJECT_URL
*/
projectUrl?: string
/**
* The secret key defined in the deployed project as env variable, used to fetch the remote storage from the projectUrl
* @default process.env.NUXT_HUB_PROJECT_SECRET_KEY
*/
projectSecretKey?: string
}
export default defineNuxtModule<ModuleOptions>({
meta: {
name: '@nuxthub/core',
configKey: 'hub',
version
},
defaults: {},
async setup (options, nuxt) {
const rootDir = nuxt.options.rootDir
const { resolve } = createResolver(import.meta.url)
// Waiting for https://github.com/unjs/c12/pull/139
// Then adding the c12 dependency to the project to 1.8.1
options = defu(options, {
...readUser('.nuxtrc').hub,
})
const runtimeConfig = nuxt.options.runtimeConfig
const hub = runtimeConfig.hub = defu(runtimeConfig.hub || {}, options, {
url: process.env.NUXT_HUB_URL || 'https://console.hub.nuxt.com',
projectKey: process.env.NUXT_HUB_PROJECT_KEY || '',
projectUrl: process.env.NUXT_HUB_PROJECT_URL || '',
projectSecretKey: process.env.NUXT_HUB_PROJECT_SECRET_KEY || '',
userToken: process.env.NUXT_HUB_USER_TOKEN || '',
remote: argv.includes('--remote') || process.env.NUXT_HUB_REMOTE === 'true',
version
})
// Add Server caching (Nitro)
nuxt.options.nitro = defu(nuxt.options.nitro, {
storage: {
cache: {
driver: 'cloudflare-kv-binding',
binding: 'CACHE',
base: 'cache'
}
},
devStorage: {
cache: {
driver: 'fs',
base: join(rootDir, '.data/cache')
}
}
})
// Bind `useDatabase()` to `hubDatabase()`
nuxt.options.nitro.experimental = defu(nuxt.options.nitro.experimental, {
database: true
})
// @ts-ignore
nuxt.options.nitro.database = defu(nuxt.options.nitro.database, {
default: {
connector: 'cloudflare-d1',
options: { bindingName: 'DB' }
}
})
// nuxt prepare, stop here
if (nuxt.options._prepare) {
return
}
if (hub.remote) {
// Can either use projectKey or projectUrl
if (hub.projectKey && hub.projectUrl) {
log.error('You cannot use both NUXT_HUB_PROJECT_KEY and NUXT_HUB_PROJECT_URL at the same time. Please use only one of them.')
process.exit(1)
}
// Check if the project is linked to a NuxtHub project
// it should have a projectKey and a userToken
// Then we fill the projectUrl
if (hub.projectKey) {
const project = await $fetch(`/api/projects/${hub.projectKey}`, {
baseURL: hub.url,
headers: {
authorization: `Bearer ${hub.userToken}`
}
}).catch(() => {
log.error('Failed to fetch NuxtHub linked project, make sure to run `nuxthub link` again.')
process.exit(1)
})
if (project) {
const adminUrl = joinURL(hub.url, project.teamSlug, project.slug)
log.info(`Linked to \`${adminUrl}\``)
hub.projectUrl = project.url
}
}
// Make sure we have a projectUrl when using the remote option
if (!hub.projectUrl) {
log.error('No project URL found, make sure to deploy the project using `nuxthub deploy` or link your project with `nuxthub link` or add the deployed URL as `NUXT_HUB_PROJECT_URL` environment variable (if self-hosted).')
process.exit(1)
}
// Make sure we have a secret when using the remote option
if (!hub.projectKey && !hub.projectSecretKey && !hub.userToken) {
log.error('No project secret key found, make sure to add the `NUXT_HUB_PROJECT_SECRET_KEY` environment variable.')
process.exit(1)
}
// If using the remote option with a projectUrl and a projectSecretKey
log.info(`Using remote features from \`${hub.projectUrl}\``)
const manifest = await $fetch('/api/_hub/manifest', {
baseURL: hub.projectUrl,
headers: {
authorization: `Bearer ${hub.projectSecretKey || hub.userToken}`
}
})
.catch(async (err) => {
let message = 'Project not found.\nMake sure to deploy the project using `nuxthub deploy` or add the deployed URL as `NUXT_HUB_PROJECT_URL` environment variable.'
if (err.status >= 500) {
message = 'Internal server error'
} else if (err.status === 401) {
message = 'Authorization failed.\nMake sure to provide a valid NUXT_HUB_PROJECT_SECRET_KEY or being logged in with `nuxthub login`'
}
log.error(`Failed to fetch remote storage: ${message}`)
process.exit(1)
})
if (manifest.version !== hub.version) {
log.warn(`\`${hub.projectUrl}\` is running \`@nuxthub/core@${manifest.version}\` while the local project is running \`@nuxthub/core@${hub.version}\`. Make sure to use the same version on both sides to avoid issues.`)
}
logger.info(`Remote storage available: ${Object.keys(manifest.storage).filter(k => manifest.storage[k]).map(k => `\`${k}\``).join(', ')} `)
return
}
// Add Proxy routes only if not remote
addServerScanDir(resolve('./runtime/server'))
// Local development without remote connection
if (nuxt.options.dev) {
log.info('Using local storage from `.data/hub`')
// Create the .data/hub/ directory
const hubDir = join(rootDir, './.data/hub')
try {
await mkdir(hubDir, { recursive: true })
} catch (e: any) {
if (e.errno === -17) {
// File already exists
} else {
throw e
}
}
const workspaceDir = await findWorkspaceDir(rootDir)
// Add it to .gitignore
const gitignorePath = join(workspaceDir , '.gitignore')
const gitignore = await readFile(gitignorePath, 'utf-8').catch(() => '')
if (!gitignore.includes('.data')) {
await writeFile(gitignorePath, `${gitignore ? gitignore + '\n' : gitignore}.data`, 'utf-8')
}
// Generate the wrangler.toml file
const wranglerPath = join(hubDir, './wrangler.toml')
await writeFile(wranglerPath, generateWrangler(), 'utf-8')
// @ts-ignore
nuxt.options.nitro.cloudflareDev = {
persistDir: hubDir,
configPath: wranglerPath,
silent: true
}
await installModule('nitro-cloudflare-dev')
nuxt.options.nitro.plugins = nuxt.options.nitro.plugins || []
nuxt.options.nitro.plugins.push(resolve('./runtime/ready.dev'))
}
}
})