Skip to content

Commit

Permalink
fix(apps): tunnel worker consistency fixes (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
azuradara authored Jan 3, 2025
1 parent 9c74338 commit a0142d0
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 65 deletions.
44 changes: 29 additions & 15 deletions packages/app/lib/cli/commands/app/dev.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Env, Http, Services, Session, System, Tasks, UI } from '@youcan/cli-kit';
import { Env, Http, Services, Path, Session, System, Tasks, UI, Filesystem } from '@youcan/cli-kit';
import type { Worker } from '@youcan/cli-kit';
import { bootAppWorker, bootExtensionWorker, bootTunnelWorker, bootWebWorker } from '@/cli/services/dev/workers';
import { AppCommand } from '@/util/app-command';
import { load } from '@/util/app-loader';
import { APP_CONFIG_FILENAME } from '@/constants';

interface Context {
cmd: Dev
Expand Down Expand Up @@ -56,31 +57,44 @@ class Dev extends AppCommand {
private async prepareNetworkOptions() {
const port = await System.getNextAvailablePort(3000);

// Start by `localhost` until a tunneled url is available
const appUrl = `http://localhost:${port}`;

this.app.networkConfig = { port, appUrl };
this.app.network_config = {
app_port: port,
app_url: `http://localhost:${port}`
}

const worker = await bootTunnelWorker(this, this.app, new Services.Cloudflared());

this.app.config = {
...this.app.config,
app_url: worker.getUrl(),
redirect_urls: this.app.config.redirect_urls?.length > 0
? this.app.config.redirect_urls.map(r => new URL(new URL(r).pathname, worker.getUrl()).toString())
: [new URL('/auth/callback', worker.getUrl()).toString()]
}

await Filesystem.writeJsonFile(
Path.join(this.app.root, APP_CONFIG_FILENAME),
this.app.config
)

return worker;
}

async reloadWorkers() {
this.controller = new AbortController();

// Preserve network config.
const networkConfig = this.app.networkConfig;
const networkConfig = this.app.network_config;
this.app = await load();
this.app.networkConfig = networkConfig;
this.app.network_config = networkConfig;

await this.syncAppConfig();

await this.runWorkers(await this.prepareDevProcesses());
}

private async runWorkers(workers: Worker.Interface[]): Promise<void> {
await Promise.all(workers.map(worker => worker.run())).catch((_) => {});
await Promise.all(workers.map(worker => worker.run())).catch((_) => { });
}

private async prepareDevProcesses(): Promise<Worker.Interface[]> {
Expand All @@ -103,19 +117,19 @@ class Dev extends AppCommand {
}

private buildEnvironmentVariables(): Record<string, string> {
if (!this.app.remoteConfig) {
if (!this.app.remote_config) {
throw new Error('remote app config not loaded');
}
if (!this.app.networkConfig) {
if (!this.app.network_config) {
throw new Error('app network config is not set');
}

return {
YOUCAN_API_KEY: this.app.remoteConfig.client_id,
YOUCAN_API_SECRET: this.app.remoteConfig.client_secret,
YOUCAN_API_SCOPES: this.app.remoteConfig.scopes.join(','),
APP_URL: this.app.networkConfig.appUrl,
PORT: this.app.networkConfig.port.toString(),
YOUCAN_API_KEY: this.app.remote_config.client_id,
YOUCAN_API_SECRET: this.app.remote_config.client_secret,
YOUCAN_API_SCOPES: this.app.remote_config.scopes.join(','),
APP_URL: this.app.network_config.app_url,
PORT: this.app.network_config.app_port.toString(),
};
}

Expand Down
8 changes: 4 additions & 4 deletions packages/app/lib/cli/commands/app/env/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class EnvShow extends AppCommand {
}

private async printEnvironmentVariables() {
if (!this.app.remoteConfig) {
if (!this.app.remote_config) {
throw new Error('remote app config not loaded');
}

this.log();
this.log(`${Color.yellow('YOUCAN_API_KEY')}=%s`, this.app.remoteConfig.client_id);
this.log(`${Color.yellow('YOUCAN_API_SECRET')}=%s`, this.app.remoteConfig.client_secret);
this.log(`${Color.yellow('YOUCAN_API_SCOPES')}=%s`, this.app.remoteConfig.scopes.join(','));
this.log(`${Color.yellow('YOUCAN_API_KEY')}=%s`, this.app.remote_config.client_id);
this.log(`${Color.yellow('YOUCAN_API_SECRET')}=%s`, this.app.remote_config.client_secret);
this.log(`${Color.yellow('YOUCAN_API_SCOPES')}=%s`, this.app.remote_config.scopes.join(','));
}
}

Expand Down
58 changes: 27 additions & 31 deletions packages/app/lib/cli/services/dev/workers/tunnel-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,54 +19,50 @@ export default class TunnelWorker extends Worker.Abstract {
}

public async boot(): Promise<void> {
if (!this.app.networkConfig) {
if (!this.app.network_config) {
throw new Error('app network config is not set');
}

this.logger.write('start tunneling the app');

await this.tunnelService.tunnel(this.app.networkConfig.port);
await this.tunnelService.tunnel(this.app.network_config.app_port);

// Stop the execution for while and see if the tunnel is available.
await System.sleep(5);
let attempts = 0;

const url = this.tunnelService.getUrl();
if (url) {
this.logger.write(`tunneled url obtained: \`${url}\``);
this.url = url;
this.app.networkConfig!.appUrl = this.url;
}
}

public async run(): Promise<void> {
const timeInterval = 500;
while (!this.url && attempts <= 28) {
const url = this.tunnelService.getUrl();

if (this.url) {
return;
}

setInterval(() => {
if (this.url !== null) {
this.checkForError();

return;
if (url) {
this.url = url;
this.app.network_config!.app_url = this.url;
this.logger.write(`tunneled url obtained: \`${url}\``);
}

await System.sleep(0.5)
}

this.url = this.tunnelService.getUrl();
if (this.url) {
this.logger.write(`tunneled url obtained: \`${this.url}\``);
this.app.networkConfig!.appUrl = this.url;
if (!this.url) {
this.logger.write('could not establish a tunnel, using localhost instead')
}
}

this.command.syncAppConfig();
}
}, timeInterval);
public async run(): Promise<void> {
setInterval(() => this.checkForError, 500);
}

private checkForError() {
const error = this.tunnelService.getError();

if (error) {
throw new Error(`Tunnel stopped: ${error}`);
throw new Error(`tunnel stopped: ${error}`);
}
}

public getUrl(): string {
if (!this.url) {
throw new Error('app url not set')
}

return this.url;
}
}
8 changes: 4 additions & 4 deletions packages/app/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ export interface App {
root: string
webs: Web[]
config: AppConfig
remoteConfig?: RemoteAppConfig
networkConfig?: {
appUrl: string
port: number
remote_config?: RemoteAppConfig
network_config?: {
app_url: string
app_port: number
}
extensions: Extension[]
}
Expand Down
14 changes: 3 additions & 11 deletions packages/app/lib/util/app-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,12 @@ export abstract class AppCommand extends Cli.Command {
? `${Env.apiHostname()}/apps/create`
: `${Env.apiHostname()}/apps/${this.app.config.id}/update`;

if (!this.app.networkConfig) {
throw new Error('app network config is not set');
}

const backUrl = new URL('/auth/callback', this.app.networkConfig.appUrl).toString();

const res = await Http.post<RemoteAppConfig>(endpoint, {
headers: { Authorization: `Bearer ${this.session.access_token}` },
body: JSON.stringify({
name: this.app.config.name,
app_url: this.app.networkConfig!.appUrl,
redirect_urls: [
backUrl,
],
app_url: this.app.config.app_url,
redirect_urls: this.app.config.redirect_urls
}),
});

Expand All @@ -45,7 +37,7 @@ export abstract class AppCommand extends Cli.Command {
this.app.config,
);

this.app.remoteConfig = res;
this.app.remote_config = res;

return this.app;
}
Expand Down

0 comments on commit a0142d0

Please sign in to comment.