novacloud22-cli 1.0.2 → 1.0.4

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 (2) hide show
  1. package/index.js +100 -100
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -13,6 +13,7 @@ const open = require('open')
13
13
  const os = require('os')
14
14
 
15
15
  const API_BASE = 'https://backend-vjzu.onrender.com'
16
+ const SITES_API = 'https://novacloud22-sites.onrender.com'
16
17
  const FRONTEND_URL = 'https://novacloud22.web.app'
17
18
  const CONFIG_DIR = path.join(os.homedir(), '.novacloud22')
18
19
  const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json')
@@ -86,114 +87,78 @@ program
86
87
  .description('Authenticate with your NovaCloud22 account')
87
88
  .action(async () => {
88
89
  banner()
89
- const spinner = ora('Starting authentication...').start()
90
90
 
91
- // Start local server to receive token
92
- const server = http.createServer(async (req, res) => {
93
- const url = new URL(req.url, 'http://localhost:9876')
94
- const token = url.searchParams.get('token')
91
+ // Generate unique state ID for polling
92
+ const state = require('crypto').randomBytes(16).toString('hex')
93
+ const loginUrl = `${FRONTEND_URL}/cli-auth?state=${state}`
95
94
 
96
- if (token) {
97
- // Verify token with backend
98
- try {
95
+ console.log(chalk.bold('\n NovaCloud22 CLI — Login\n'))
96
+ console.log(chalk.gray(' ' + '─'.repeat(50)))
97
+ console.log(chalk.cyan('\n Login URL:'))
98
+ console.log(chalk.blue.underline(` ${loginUrl}\n`))
99
+ console.log(chalk.gray(' ' + '─'.repeat(50)))
100
+
101
+ const { action } = await inquirer.prompt([{
102
+ type: 'list',
103
+ name: 'action',
104
+ message: 'What would you like to do?',
105
+ choices: [
106
+ { name: '🌐 Open in browser', value: 'open' },
107
+ { name: '📋 Copy link', value: 'copy' },
108
+ { name: '✕ Cancel', value: 'cancel' },
109
+ ]
110
+ }])
111
+
112
+ if (action === 'cancel') {
113
+ console.log(chalk.gray('\n Login cancelled.\n'))
114
+ process.exit(0)
115
+ }
116
+
117
+ if (action === 'open') {
118
+ await open(loginUrl)
119
+ console.log(chalk.gray('\n Browser opened. Complete sign in there.\n'))
120
+ } else if (action === 'copy') {
121
+ const { execSync } = require('child_process')
122
+ try {
123
+ const platform = process.platform
124
+ if (platform === 'darwin') execSync(`echo '${loginUrl}' | pbcopy`)
125
+ else if (platform === 'win32') execSync(`echo ${loginUrl} | clip`)
126
+ else execSync(`echo '${loginUrl}' | xclip -selection clipboard`)
127
+ console.log(chalk.green('\n ✓ Link copied! Paste it in your browser.\n'))
128
+ } catch {
129
+ console.log(chalk.yellow('\n Copy this link manually:'))
130
+ console.log(chalk.blue.underline(` ${loginUrl}\n`))
131
+ }
132
+ }
133
+
134
+ // Poll backend every 2s until token received (max 5 min)
135
+ const spinner = ora('Waiting for authentication...').start()
136
+ const maxWait = 150
137
+ let waited = 0
138
+
139
+ while (waited < maxWait) {
140
+ await new Promise(r => setTimeout(r, 2000))
141
+ waited++
142
+ try {
143
+ const res = await axios.get(`${API_BASE}/cli-auth/poll?state=${state}`)
144
+ if (res.data.ready && res.data.token) {
145
+ const token = res.data.token
99
146
  const verifyRes = await axios.get(`${API_BASE}/user/profile`, {
100
147
  headers: { Authorization: `Bearer ${token}` }
101
148
  })
102
149
  saveToken(token)
103
- res.writeHead(200, { 'Content-Type': 'text/html' })
104
- res.end(`
105
- <html><body style="font-family:sans-serif;text-align:center;padding:3rem;background:#0f0c29;color:white">
106
- <h2>✅ Logged in successfully!</h2>
107
- <p>You can close this tab and return to the terminal.</p>
108
- <p style="color:rgba(255,255,255,0.5);font-size:0.9rem">NovaCloud22 CLI</p>
109
- </body></html>
110
- `)
111
- server.close()
112
150
  spinner.succeed(chalk.green(`Logged in as ${verifyRes.data.email}`))
113
151
  console.log(chalk.gray('\n Run novacloud22 init to set up your project\n'))
114
- } catch {
115
- res.writeHead(400, { 'Content-Type': 'text/html' })
116
- res.end('<html><body>Authentication failed. Please try again.</body></html>')
117
- server.close()
118
- spinner.fail('Authentication failed')
119
- }
120
- } else {
121
- res.writeHead(400)
122
- res.end('No token received')
123
- }
124
- })
125
-
126
- server.listen(9876, async () => {
127
- spinner.stop()
128
- const loginUrl = `${FRONTEND_URL}/cli-auth?callback=http://localhost:9876`
129
-
130
- console.log(chalk.bold('\n NovaCloud22 CLI — Login\n'))
131
- console.log(chalk.gray(' ' + '─'.repeat(50)))
132
- console.log(chalk.cyan('\n Login URL:'))
133
- console.log(chalk.blue.underline(` ${loginUrl}\n`))
134
- console.log(chalk.gray(' ' + '─'.repeat(50)))
135
-
136
- const { action } = await inquirer.prompt([{
137
- type: 'list',
138
- name: 'action',
139
- message: 'What would you like to do?',
140
- choices: [
141
- { name: '🌐 Open in browser', value: 'open' },
142
- { name: '📋 Copy link', value: 'copy' },
143
- { name: '✕ Cancel', value: 'cancel' },
144
- ]
145
- }])
146
-
147
- if (action === 'open') {
148
- await open(loginUrl)
149
- console.log(chalk.gray('\n Browser opened. Complete sign in there.\n'))
150
- console.log(chalk.gray(' Waiting for authentication...'))
151
- } else if (action === 'copy') {
152
- // Copy to clipboard using pbcopy (mac) / clip (win) / xclip (linux)
153
- const { execSync } = require('child_process')
154
- try {
155
- const platform = process.platform
156
- if (platform === 'darwin') execSync(`echo '${loginUrl}' | pbcopy`)
157
- else if (platform === 'win32') execSync(`echo ${loginUrl} | clip`)
158
- else execSync(`echo '${loginUrl}' | xclip -selection clipboard`)
159
- console.log(chalk.green('\n ✓ Link copied to clipboard!'))
160
- console.log(chalk.gray(' Paste it in your browser to sign in.\n'))
161
- console.log(chalk.gray(' Waiting for authentication...'))
162
- } catch {
163
- console.log(chalk.yellow('\n Could not copy automatically. Copy this link manually:'))
164
- console.log(chalk.blue.underline(` ${loginUrl}\n`))
152
+ process.exit(0)
165
153
  }
166
- } else {
167
- console.log(chalk.gray('\n Login cancelled.\n'))
168
- server.close()
169
- process.exit(0)
154
+ } catch {
155
+ // keep polling
170
156
  }
171
- })
172
-
173
- server.on('error', () => {
174
- spinner.fail('Could not start local server on port 9876')
175
- console.log(chalk.yellow('\n Manual login: Go to ' + FRONTEND_URL + '/signin'))
176
- console.log(chalk.yellow(' Then run: novacloud22 login --token YOUR_TOKEN\n'))
177
- })
178
- })
179
-
180
- // LOGIN WITH TOKEN (manual fallback)
181
- program
182
- .command('login:token <token>')
183
- .description('Login with a token directly')
184
- .action(async (token) => {
185
- const spinner = ora('Verifying token...').start()
186
- try {
187
- const res = await axios.get(`${API_BASE}/user/profile`, {
188
- headers: { Authorization: `Bearer ${token}` }
189
- })
190
- saveToken(token)
191
- spinner.succeed(chalk.green(`Logged in as ${res.data.email}`))
192
- } catch {
193
- spinner.fail('Invalid token')
194
157
  }
195
- })
196
158
 
159
+ spinner.fail('Login timed out. Please try again.')
160
+ process.exit(1)
161
+ })
197
162
  // INIT
198
163
  program
199
164
  .command('init')
@@ -215,7 +180,7 @@ program
215
180
  const spinner = ora('Fetching your sites...').start()
216
181
  let sites = []
217
182
  try {
218
- const res = await axios.get(`${API_BASE}/p/my-sites`, { headers })
183
+ const res = await axios.get(`${SITES_API}/p/my-sites`, { headers })
219
184
  sites = res.data.sites || []
220
185
  spinner.stop()
221
186
  } catch {
@@ -257,6 +222,41 @@ program
257
222
  ])
258
223
 
259
224
  saveProjectConfig(answers)
225
+
226
+ // Auto-fix vite.config.js to use relative paths
227
+ const viteConfigs = ['vite.config.js', 'vite.config.ts']
228
+ let viteFixed = false
229
+ for (const configFile of viteConfigs) {
230
+ if (fs.existsSync(configFile)) {
231
+ const content = fs.readFileSync(configFile, 'utf8')
232
+ if (!content.includes("base:")) {
233
+ // Add base: './' to existing config
234
+ const updated = content.replace(
235
+ /defineConfig\s*\(\s*\{/,
236
+ `defineConfig({\n base: './',`
237
+ )
238
+ if (updated !== content) {
239
+ fs.writeFileSync(configFile, updated)
240
+ console.log(chalk.green(` ✓ Updated ${configFile} with base: './'`))
241
+ viteFixed = true
242
+ }
243
+ } else {
244
+ console.log(chalk.gray(` ✓ ${configFile} already has base config`))
245
+ viteFixed = true
246
+ }
247
+ break
248
+ }
249
+ }
250
+ // No vite config found — create one
251
+ if (!viteFixed && fs.existsSync('package.json')) {
252
+ const pkg = fs.readJsonSync('package.json')
253
+ const hasVite = pkg.devDependencies?.vite || pkg.dependencies?.vite
254
+ if (hasVite) {
255
+ fs.writeFileSync('vite.config.js', `import { defineConfig } from 'vite'\n\nexport default defineConfig({\n base: './',\n})\n`)
256
+ console.log(chalk.green(" \u2713 Created vite.config.js with base: './'"))
257
+ }
258
+ }
259
+
260
260
  console.log(chalk.green(`\n ✓ Created novacloud22.json`))
261
261
  console.log(chalk.gray(` Site: ${FRONTEND_URL}/p/${answers.slug}`))
262
262
  console.log(chalk.gray(` Run: novacloud22 deploy\n`))
@@ -305,7 +305,7 @@ program
305
305
  spinner.text = `Uploading ${relativePath}...`
306
306
  }
307
307
 
308
- const res = await axios.post(`${API_BASE}/p/deploy`, form, {
308
+ const res = await axios.post(`${SITES_API}/p/deploy`, form, {
309
309
  headers: { ...headers, ...form.getHeaders() },
310
310
  maxContentLength: Infinity,
311
311
  maxBodyLength: Infinity,
@@ -337,7 +337,7 @@ program
337
337
  const headers = authHeaders()
338
338
  const spinner = ora('Fetching your sites...').start()
339
339
  try {
340
- const res = await axios.get(`${API_BASE}/p/my-sites`, { headers })
340
+ const res = await axios.get(`${SITES_API}/p/my-sites`, { headers })
341
341
  const sites = res.data.sites || []
342
342
  spinner.stop()
343
343
 
@@ -434,7 +434,7 @@ program
434
434
 
435
435
  const spinner = ora('Fetching status...').start()
436
436
  try {
437
- const res = await axios.get(`${API_BASE}/p/info/${config.slug}`, { headers })
437
+ const res = await axios.get(`${SITES_API}/p/info/${config.slug}`, { headers })
438
438
  spinner.stop()
439
439
  const site = res.data
440
440
  console.log(chalk.bold(` Site: /${site.slug}`))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "novacloud22-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Deploy static websites to NovaCloud22",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -30,4 +30,4 @@
30
30
  "engines": {
31
31
  "node": ">=14.0.0"
32
32
  }
33
- }
33
+ }