Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] added cloudflare static tunnels support #282

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
9 changes: 8 additions & 1 deletion packages/create-robo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,20 @@ new Command('create-robo <projectName>')
await robo.plugins()
}

// Ask the user for their Discord credentials (token and client ID) and store them for later use
// Ask the user for their Discord credentials (Token and Client ID) and store them for later use
// Skip this step if the user is creating a plugin
if (!robo.isPlugin) {
logger.debug(`Asking for Discord credentials...`)
await robo.askForDiscordCredentials()
}

// Ask the user for their CloudFlare credentials (API key, Account ID and Domain) and store them for later use
// Skip this step if the user is not creating an app
if (robo.isApp) {
logger.debug(`Asking for CloudFlare setup...`)
await robo.askForCloudFlareSetup()
}

const packageManager = getPackageManager()
logger.log(Indent.repeat(15))
logger.log(Indent, '🚀', chalk.bold.green('Your Robo is ready!'))
Expand Down
70 changes: 68 additions & 2 deletions packages/create-robo/src/robo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ export default class Robo {
return this._isPlugin
}

public get isApp(): boolean {
return this._isApp
}

public get missingEnv(): boolean {
return this._missingEnv
}
Expand Down Expand Up @@ -864,7 +868,7 @@ export default class Robo {
})
discordToken = await input({
message: this._isApp
? 'Enter your Discord Client Secret (press enter to skip)'
? 'Enter your Discord Client Secret (press enter to skip):'
: 'Enter your Discord Token (press Enter to skip):'
})
}
Expand Down Expand Up @@ -912,6 +916,68 @@ export default class Robo {
envContent = updateOrAddVariable(envContent, 'PORT', '3000')
}

await fs.writeFile(envFilePath, envContent, 'utf-8')
await this.createEnvTsFile()
this._spinner.stop()
// logger.log(Indent, ' Manage your credentials in the', chalk.bold.cyan('.env'), 'file.')
}

async askForCloudFlareSetup(): Promise<void> {
const cloudflareDashboard = 'Dashboard:'
const cloudflareDashboardUrl = chalk.bold.blue('https://dash.cloudflare.com')
const cloudflareOfficialGuide = 'Guide:'
const cloudflareOfficialGuideUrl = chalk.bold.blue('https://developers.cloudflare.com/fundamentals/api/get-started/create-token')

let cloudflareAPIKey = ''
let cloudflareAccountID = ''
let cloudflareDomain = ''
logger.log('')
logger.log(Indent, chalk.bold('💈 Setting up CloudFlare Tunnel'))
logger.log(Indent, ' Get your credentials from the CloudFlare Dashboard.\n')
logger.log(Indent, ` ${cloudflareDashboard} ${cloudflareDashboardUrl}`)
logger.log(Indent, ` ${cloudflareOfficialGuide} ${cloudflareOfficialGuideUrl}\n`)

if (this._cliOptions.creds) {
cloudflareAccountID = await input({
message: 'Enter your CloudFlare Account ID (press enter to skip):'
})
cloudflareAPIKey = await input({
message: 'Enter your CloudFlare API Key (press enter to skip):'
})
cloudflareDomain = await input({
message: 'Enter your CloudFlare Domain (press enter to skip):'
})
}

if (!cloudflareAPIKey || !cloudflareAccountID || !cloudflareDomain) {
this._missingEnv = true
}

if (this._cliOptions.verbose) {
logger.log('')
} else {
logger.log('\x1B[1A\x1B[K\x1B[1A\x1B[K')
}
this._spinner.setText(Indent + ' {{spinner}} Applying credentials...\n')
this._spinner.start()

const envFilePath = path.join(this._workingDir, '.env')
let envContent = ''

try {
envContent = await fs.readFile(envFilePath, 'utf8')
} catch (error) {
if (hasProperties(error, ['code']) && error.code !== 'ENOENT') {
throw error
}
}

envContent = updateOrAddVariable(envContent, 'CLOUDFLARE_API_KEY', cloudflareAPIKey ?? '')
envContent = updateOrAddVariable(envContent, 'CLOUDFLARE_ACCOUNT_ID', cloudflareAccountID ?? '')
envContent = updateOrAddVariable(envContent, 'CLOUDFLARE_DOMAIN', cloudflareDomain ?? '')
envContent = updateOrAddVariable(envContent, 'CLOUDFLARE_TUNNEL_ID', '')
envContent = updateOrAddVariable(envContent, 'CLOUDFLARE_TUNNEL_TOKEN', '')

await fs.writeFile(envFilePath, envContent, 'utf-8')
await this.createEnvTsFile()
this._spinner.stop()
Expand Down Expand Up @@ -950,7 +1016,7 @@ export default class Robo {
private async createEnvTsFile() {
if (this._useTypeScript) {
const autoCompletionEnvVar = `export {}\ndeclare global {\n namespace NodeJS {\n interface ProcessEnv {\n DISCORD_CLIENT_ID: string\n ${
this._isApp ? 'DISCORD_CLIENT_SECRET: string' : 'DISCORD_TOKEN: string'
this._isApp ? 'DISCORD_CLIENT_SECRET: string\nCLOUDFLARE_API_KEY: string\nCLOUDFLARE_ACCOUNT_ID: string\nCLOUDFLARE_DOMAIN: string\nCLOUDFLARE_TUNNEL_ID: string\nCLOUDFLARE_TUNNEL_TOKEN: string\n' : 'DISCORD_TOKEN: string'
}\n }\n } \n}`

const tsconfigPath = path.join(this._workingDir, 'tsconfig.json')
Expand Down
9 changes: 8 additions & 1 deletion packages/robo/src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChildProcess, spawn } from 'child_process'
import { logger } from '../../core/logger.js'
import { DEFAULT_CONFIG, FLASHCORE_KEYS, Indent, cloudflareLogger } from '../../core/constants.js'
import { getConfigPaths, loadConfig, loadConfigPath } from '../../core/config.js'
import { installCloudflared, isCloudflaredInstalled, startCloudflared, stopCloudflared } from '../utils/cloudflared.js'
import { installCloudflared, isCloudflaredInstalled, initializeCloudflareTunnel, startCloudflared, stopCloudflared } from '../utils/cloudflared.js'
import { IS_WINDOWS, filterExistingPaths, getWatchedPlugins, packageJson, timeout } from '../utils/utils.js'
import path from 'node:path'
import Watcher, { Change } from '../utils/watcher.js'
Expand Down Expand Up @@ -99,6 +99,13 @@ async function devAction(_args: string[], options: DevCommandOptions) {
cloudflareLogger.info(`Cloudflared installed successfully!`)
}

// Initialize Cloudflare tunnel
if (options.tunnel && isCloudflaredInstalled()) {
cloudflareLogger.event(`Initializing Cloudflare tunnel...`)
const initialized = await initializeCloudflareTunnel()
cloudflareLogger.info(initialized ? `Managed Cloudflare Tunnel initialized successfully!` : `Failed to initialize Managed Cloudflare Tunnel.`)
}

if (options.tunnel && !process.env.PORT) {
cloudflareLogger.error(`Cannot start tunnel without a PORT environment variable.`)
process.exit(1)
Expand Down
Loading
Loading