Skip to content

Commit

Permalink
enhance(client): support login with browser (#2916)
Browse files Browse the repository at this point in the history
  • Loading branch information
jialeicui authored Oct 30, 2023
1 parent e37cf94 commit af283ad
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 3 deletions.
6 changes: 3 additions & 3 deletions client/starwhale/core/instance/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
import typing as t

import click
Expand Down Expand Up @@ -42,8 +41,9 @@ def _login(instance: str, username: str, password: str, token: str, alias: str)
* INSTANCE: Instance URI, if ignore it, swcli will login current selected instance.
"""
if not bool(password and username) ^ bool(token):
click.echo("token or password+username, only choose one type")
sys.exit(1)
click.echo("credential or token not provided, will open browser to login")
InstanceTermView().login_with_browser(instance, alias)
return

if token:
kw = {"token": token}
Expand Down
67 changes: 67 additions & 0 deletions client/starwhale/core/instance/view.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import sys
import time
import typing as t
import threading
import contextlib
from urllib.parse import urlparse

from rich import box
from rich.panel import Panel
Expand Down Expand Up @@ -44,6 +48,69 @@ def login(self, instance: str, alias: str, **kw: str) -> None:
)
sys.exit(1)

def login_with_browser(self, instance: str, alias: str) -> None:
p = urlparse(instance)
instance = f"{p.scheme}://{p.netloc}"

import uvicorn
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

login_done = False

app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

@app.on_event("startup")
async def on_startup() -> None:
nonlocal instance
url = f"{instance}/auth/client"
import webbrowser

webbrowser.open(url)

@app.get("/login")
async def _login(token: str) -> dict:
login(instance, alias, token=token)
nonlocal login_done
login_done = True
return {"message": "success"}

# https://github.com/encode/uvicorn/issues/742
class Server(uvicorn.Server):
def install_signal_handlers(self) -> None:
pass

@contextlib.contextmanager
def run_in_thread(self) -> t.Generator:
thread = threading.Thread(target=self.run)
thread.start()
try:
while not self.started:
time.sleep(1e-3)
yield
finally:
self.should_exit = True
thread.join()

config = uvicorn.Config(
app, host="127.0.0.1", port=8007, log_level="error", loop="asyncio"
)
server = Server(config=config)
with server.run_in_thread():
while not login_done:
time.sleep(1e-3)

console.print(
f":man_cook: login {instance} as [blue]{alias}[/blue] successfully!"
)

def logout(self, instance: str) -> None:
if instance == STANDALONE_INSTANCE:
console.print(f":pinching_hand: skip {instance} instance logout")
Expand Down
8 changes: 8 additions & 0 deletions console/src/i18n/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,14 @@ const locales0 = {
en: 'Wechat',
zh: '微信',
},
'Client Login Success': {
en: 'Client Login Success',
zh: '客户端登录成功',
},
'Client Login Failed': {
en: 'Client Login Failed',
zh: '客户端登录失败',
},
'copyright': {
en: 'Copyright © 2023 Starwhale, Inc. All rights reserved. ',
zh: '版权所有 © 2023 Starwhale, Inc. 保留所有权利。',
Expand Down
36 changes: 36 additions & 0 deletions console/src/pages/Auth/ClientLogin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useEffect, useState } from 'react'
import axios from 'axios'
import { cliMateServer } from '@/consts'
import { useAuth } from '@/api/Auth'
import useTranslation from '@/hooks/useTranslation'
// eslint-disable-next-line
import { Spinner } from 'baseui/spinner'

export default function ClientLogin() {
const [t] = useTranslation()
const { token } = useAuth()
const [success, setSuccess] = useState(false)
const [loading, setLoading] = useState(false)
const [errMsg, setErrMsg] = useState('')
useEffect(() => {
if (!token) {
return
}
setLoading(true)
axios
.get(`${cliMateServer}/login`, { params: { token } })
.then(({ data: { message } }) => {
if (message !== 'success') {
setErrMsg(message)
} else {
setSuccess(true)
}
setLoading(false)
})
.catch(() => {
setErrMsg(t('Client Login Failed'))
setLoading(false)
})
}, [t, token])
return <div>{loading ? <Spinner /> : <div>{success ? t('Client Login Success') : errMsg}</div>}</div>
}
8 changes: 8 additions & 0 deletions console/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import ReportListCard from '@/pages/Report/ReportListCard'
import ReportEdit from '@/pages/Report/ReportEdit'
import JobOverview from './pages/Job/JobOverview'
import EvaluationOverview from './pages/Evaluation/EvaluationOverview'
import ClientLogin from '@/pages/Auth/ClientLogin'

const useStyles = createUseStyles({
root: ({ theme }: IThemedStyleProps) => ({
Expand Down Expand Up @@ -133,6 +134,13 @@ const Routes = () => {
</Switch>
</SettingsOverviewLayout>
</Route>
<Route exact path='/auth/:path?'>
<CenterLayout>
<Switch>
<Route exact path='/auth/client' component={ClientLogin} />
</Switch>
</CenterLayout>
</Route>
{/* project */}
<Route exact path='/projects/:projectId/members'>
<CenterLayout>
Expand Down

0 comments on commit af283ad

Please sign in to comment.