From 2c31a9cbdf84ff2659313df799cbd79f9c167325 Mon Sep 17 00:00:00 2001 From: Kai Jellinghaus Date: Tue, 29 Nov 2022 16:01:53 +0100 Subject: [PATCH] feat(cli): Login via Frontend (#956) * CLI login page * Add Success Page * Change redirect on decline * Center & Size Loading Windmill Icon * Fix goto debug * fix spinner * Workspace from URL * Handle login --token & --workspace Co-authored-by: Ruben Fiszel --- cli/login.ts | 87 ++++++++++++++------- frontend/src/routes/user/cli-success.svelte | 18 +++++ frontend/src/routes/user/cli.svelte | 39 +++++++++ 3 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 frontend/src/routes/user/cli-success.svelte create mode 100644 frontend/src/routes/user/cli.svelte diff --git a/cli/login.ts b/cli/login.ts index 5ce45dfba9..63de40b594 100644 --- a/cli/login.ts +++ b/cli/login.ts @@ -1,44 +1,74 @@ import { Command } from "https://deno.land/x/cliffy@v0.25.4/command/mod.ts"; -import { - Input, - Secret, -} from "https://deno.land/x/cliffy@v0.25.4/prompt/mod.ts"; import { GlobalOptions } from "./types.ts"; -import { - setClient, - UserService, -} from "https://deno.land/x/windmill@v1.50.0/mod.ts"; import { colors } from "https://deno.land/x/cliffy@v0.25.4/ansi/colors.ts"; import { getStore } from "./store.ts"; -import { getDefaultRemote, getRemote } from "./remote.ts"; +import { getDefaultRemote } from "./remote.ts"; +import { getAvailablePort } from "https://deno.land/x/port@1.0.0/mod.ts"; export type Options = GlobalOptions; -async function login( - { remote, baseUrl }: Options, - email?: string, - password?: string -) { - if (remote) { - baseUrl = baseUrl ?? (await getRemote(remote))?.baseUrl; - } +async function login({ + baseUrl, + token: existingToken, + workspace: existingWorkspace, +}: Options) { baseUrl = baseUrl ?? (await getDefaultRemote())?.baseUrl; baseUrl = baseUrl ?? "https://app.windmill.dev"; - setClient("no-token", baseUrl); const urlStore = await getStore(baseUrl); - email = email ?? (await Input.prompt({ message: "Input your Email" })); - password = - password ?? (await Secret.prompt({ message: "Input your Password" })); - const token = await UserService.login({ - requestBody: { - email: email, - password: password, - }, - }); + if (existingToken) { + await Deno.writeTextFile(urlStore + "token", existingToken); + + if (existingWorkspace) { + await Deno.writeTextFile( + urlStore + "default_workspace_id", + existingWorkspace! + ); + console.log( + colors.bold.underline.green( + "Successfully logged in & switched to workspace " + existingWorkspace + ) + ); + } else { + console.log(colors.bold.underline.green("Successfully logged in")); + } + return; + } + + // Start listening on port 8080 of localhost. + const port = await getAvailablePort(); + if (port == undefined) { + console.log(colors.red.underline("failed to aquire port")); + return; + } + + const server = Deno.listen({ transport: "tcp", port }); + console.log(`Login by going to ${baseUrl}/user/cli?port=${port}`); + const firstConnection = await server.accept(); + const httpFirstConnection = Deno.serveHttp(firstConnection); + const firstRequest = (await httpFirstConnection.nextRequest())!; + const params = new URL(firstRequest.request.url!).searchParams; + const token = params.get("token"); + const workspace = params.get("workspace"); + await firstRequest?.respondWith( + Response.redirect(baseUrl + "/user/cli-success", 302) + ); + + if (token === undefined || token === null) { + console.log(colors.red.underline("Invalid Request. Failed to log in.")); + return; + } await Deno.writeTextFile(urlStore + "token", token); - console.log(colors.bold.underline.green("Successfully logged in!")); + await Deno.writeTextFile(urlStore + "default_workspace_id", workspace!); + console.log( + colors.bold.underline.green( + "Successfully logged in & switched to workspace " + workspace + ) + ); + + httpFirstConnection.close(); + server.close(); } export async function getToken(baseUrl: string): Promise { @@ -59,7 +89,6 @@ const command = new Command() .description( "Log into windmill. The credentials are not stored, but the token they are exchanged for will be." ) - .arguments("[email:string] [password:string]") .action(login as any); export default command; diff --git a/frontend/src/routes/user/cli-success.svelte b/frontend/src/routes/user/cli-success.svelte new file mode 100644 index 0000000000..a68d0c89ca --- /dev/null +++ b/frontend/src/routes/user/cli-success.svelte @@ -0,0 +1,18 @@ + + + +
+
+ +
+
+
diff --git a/frontend/src/routes/user/cli.svelte b/frontend/src/routes/user/cli.svelte new file mode 100644 index 0000000000..ae6ca13e59 --- /dev/null +++ b/frontend/src/routes/user/cli.svelte @@ -0,0 +1,39 @@ + + + +

+ Token will be posted to your local machine to port {port} +

+
+ + +
+