add device flow

git4refactor
filip mertens 7 months ago
parent 777f635f75
commit c4dce2d804
  1. 25
      apps/remix-ide/src/app/files/dgitProvider.ts
  2. 150
      libs/remix-ui/git/src/components/github/devicecode.tsx
  3. 88
      libs/remix-ui/git/src/components/github/repositoryselect.tsx
  4. 4
      libs/remix-ui/git/src/components/gitui.tsx
  5. 21
      libs/remix-ui/git/src/components/panels/repositories.tsx

@ -273,7 +273,7 @@ class DGitProvider extends Plugin {
return status return status
} }
async compareBranches({branch, remote}:{branch: branch, remote: remote}) { async compareBranches({ branch, remote }: { branch: branch, remote: remote }) {
// Get current branch commits // Get current branch commits
const headCommits = await git.log({ const headCommits = await git.log({
...await this.addIsomorphicGitConfigFS(), ...await this.addIsomorphicGitConfigFS(),
@ -1028,7 +1028,7 @@ class DGitProvider extends Plugin {
} }
} }
async remotecommits(input: { owner: string, repo: string, token: string, branch: string, length: number,page: number }): Promise<pagedCommits[]> { async remotecommits(input: { owner: string, repo: string, token: string, branch: string, length: number, page: number }): Promise<pagedCommits[]> {
const octokit = new Octokit({ const octokit = new Octokit({
auth: input.token auth: input.token
}) })
@ -1091,20 +1091,19 @@ class DGitProvider extends Plugin {
} }
async repositories(input: { token: string }) { async repositories(input: { token: string }) {
console.log(input) // Set your access token
const octokit = new Octokit({ const accessToken = input.token;
auth: input.token
})
console.log('octokit', input.token) // Set headers for the request
const headers = {
'Authorization': `Bearer ${accessToken}`, // Include your GitHub access token
'Accept': 'application/vnd.github.v3+json', // GitHub API v3 media type
};
const data = await octokit.request('GET /user/repos', { // Make the GET request with Axios
per_page: 200, const response = await axios.get('https://api.github.com/user/repos?visibility=private,public', { headers })
page: 2
})
console.log(data.data) return response.data
return data.data
} }
} }

@ -1,30 +1,144 @@
import React, { useState } from 'react'; import React, { useEffect } from "react";
import { gitActionsContext, pluginActionsContext } from "../../state/context";
import { gitPluginContext } from "../gitui";
import axios from "axios";
import { CopyToClipboard } from "@remix-ui/clipboard";
import { Card } from "react-bootstrap";
const GetDeviceCode = () => {
const [userCode, setUserCode] = useState(null);
const requestDeviceCode = async () => { export const GetDeviceCode = () => {
const response = await fetch('http://localhost:3000/github.com/login/device/code', { const context = React.useContext(gitPluginContext)
method: 'POST', const actions = React.useContext(gitActionsContext)
const pluginActions = React.useContext(pluginActionsContext)
const [gitHubResponse, setGitHubResponse] = React.useState<any>(null)
const [authorized, setAuthorized] = React.useState<boolean>(false)
const getDeviceCodeFromGitHub = async () => {
setAuthorized(false)
// Send a POST request
const response = await axios({
method: 'post',
url: 'http://0.0.0.0:3000/github.com/login/device/code',
data: {
client_id: 'dccbc48453f7afa34fad',
scope: 'repo'
},
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json'
}, },
body: JSON.stringify({ });
// convert response to json
const githubrespone = await response.data;
console.log('json', githubrespone)
setGitHubResponse(githubrespone)
}
const connectApp = async () => {
// poll https://github.com/login/oauth/access_token
const accestokenresponse = await axios({
method: 'post',
url: 'http://0.0.0.0:3000/github.com/login/oauth/access_token',
data: {
client_id: 'dccbc48453f7afa34fad', client_id: 'dccbc48453f7afa34fad',
scope: 'repo', // or another appropriate scope device_code: gitHubResponse.device_code,
}), grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
},
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
}); });
const data = await response.json(); // convert response to json
setUserCode(data.user_code); // Store user code to show to the user const response = await accestokenresponse.data;
}; console.log('json2', response)
if (response.error) {
}
if (response.access_token) {
setAuthorized(true)
await pluginActions.saveToken(response.access_token)
await actions.getGitHubUser()
}
}
const disconnect = async () => {
setAuthorized(false)
setGitHubResponse(null)
}
const checkConnection = async () => {
//await actions.getGitHubUser()
}
useEffect(() => {
},[])
useEffect(() => {
console.log('context.rateLimit', context.rateLimit)
}, [context.rateLimit])
return ( return (
<div> <>
<button onClick={requestDeviceCode}>Get Device Code</button> {(context.gitHubUser && context.gitHubUser.login) ? null :
{userCode && <div>User Code: {userCode}</div>} <button className='btn btn-primary mt-1 w-100' onClick={async () => {
getDeviceCodeFromGitHub();
}}>Login in with github</button>
}
{gitHubResponse && !authorized &&
<div className="pt-2">
Step 1: Copy this code:
<div className="input-group text-secondary mb-0 h6">
<input disabled type="text" className="form-control" value={gitHubResponse.user_code} />
<div className="input-group-append">
<CopyToClipboard content={gitHubResponse.user_code} data-id='copyToClipboardCopyIcon' className='far fa-copy ml-1 p-2 mt-1' direction={"top"} />
</div> </div>
); </div>
}; <br></br>
Step 2: Authorize the app here
<br></br><a target="_blank" href={gitHubResponse.verification_uri}>{gitHubResponse.verification_uri}</a>
<br /><br></br>
Step 3: When you are done, click on the button below:
<button className='btn btn-primary mt-1 w-100' onClick={async () => {
connectApp()
}}>Connect</button>
</div>
}
{
(context.gitHubUser && context.gitHubUser.login) ?
<div className="pt-2">
<button className='btn btn-primary mt-1 w-100' onClick={async () => {
disconnect()
}}>Disconnect</button>
</div>: null
}
{
(context.gitHubUser && context.gitHubUser.login) ?
<div className="pt-2">
<Card>
<Card.Body>
<Card.Title>Connected as {context.gitHubUser.login}</Card.Title>
<Card.Text>
<img src={context.gitHubUser.avatar_url} className="w-100" />
<a target="_blank" href={context.gitHubUser.html_url}>{context.gitHubUser.html_url}</a>
</Card.Text>
</Card.Body>
</Card>
</div>: null
}
export default GetDeviceCode; </>)
}

@ -0,0 +1,88 @@
import { clearInstances } from 'libs/remix-ui/run-tab/src/lib/actions/actions';
import React, { useState, useCallback, useEffect } from 'react';
import { Button } from 'react-bootstrap';
import { OptionsOrGroups, GroupBase } from 'react-select';
import Select from 'react-select/async';
import { gitActionsContext } from '../../state/context';
import { selectStyles, selectTheme } from '../../types/styles';
import { gitPluginContext } from '../gitui';
import { TokenWarning } from '../panels/tokenWarning';
const RepositorySelect = () => {
const [page, setPage] = useState(1);
const [repoOtions, setRepoOptions] = useState<any>([]);
const context = React.useContext(gitPluginContext)
const actions = React.useContext(gitActionsContext)
const [loading, setLoading] = useState(false)
const [show, setShow] = useState(false)
useEffect(() => {
console.log('context', context.repositories)
if (context.repositories && context.repositories.length > 0) {
// map context.repositories to options
const options = context.repositories && context.repositories.length > 0 && context.repositories.map(repo => {
return { value: repo.id, label: repo.full_name }
})
console.log('options', options)
setRepoOptions(options)
} else {
setRepoOptions(null)
setShow(false)
}
setLoading(false)
}, [context.repositories])
useEffect(() => {
console.log('OTIONS', repoOtions)
},[repoOtions])
const selectRepo = async (value: number | string) => {
// find repo
console.log('setRepo', value, context.repositories)
const repo = context.repositories.find(repo => {
return repo.id.toString() === value.toString()
})
console.log('repo', repo)
if (repo) {
//setBranchOptions([])
//setBranch({ name: "" })
//setRepo(repo)
await actions.remoteBranches(repo.owner.login, repo.name)
}
}
const fetchRepositories = async () => {
try {
setShow(true)
setLoading(true)
//setRepoOptions([])
//setBranchOptions([])
console.log(await actions.repositories())
} catch (e) {
// do nothing
}
};
return (
<><TokenWarning /><Button onClick={fetchRepositories} className="w-100 mt-1">
<i className="fab fa-github mr-1"></i>Fetch Repositories from GitHub
</Button>
{
show ?
<Select
options={repoOtions}
className="mt-1"
onChange={(e: any) => e && selectRepo(e.value)}
theme={selectTheme}
styles={selectStyles}
isClearable={true}
placeholder="Type to search for a repository..."
isLoading={loading}
/> : null
}</>
);
};
export default RepositorySelect;

@ -29,7 +29,7 @@ import { GitHubCredentials } from './panels/githubcredentials'
import { loaderReducer } from '../state/loaderReducer' import { loaderReducer } from '../state/loaderReducer'
import { ApolloClient, ApolloProvider, NormalizedCacheObject } from '@apollo/client' import { ApolloClient, ApolloProvider, NormalizedCacheObject } from '@apollo/client'
import { client, getApolloLink } from '../state/apolloClient' import { client, getApolloLink } from '../state/apolloClient'
import GetDeviceCode from './github/devicecode' import { GetDeviceCode } from './github/devicecode'
export const gitPluginContext = React.createContext<gitState>(defaultGitState) export const gitPluginContext = React.createContext<gitState>(defaultGitState)
export const loaderContext = React.createContext<loaderState>(defaultLoaderState) export const loaderContext = React.createContext<loaderState>(defaultLoaderState)
@ -180,8 +180,6 @@ export const GitUI = (props: IGitUi) => {
<GitHubNavigation eventKey="7" activePanel={activePanel} callback={setActivePanel} /> <GitHubNavigation eventKey="7" activePanel={activePanel} callback={setActivePanel} />
<Accordion.Collapse className='bg-light' eventKey="7"> <Accordion.Collapse className='bg-light' eventKey="7">
<> <>
<GitHubAuth></GitHubAuth>
<GitHubCredentials></GitHubCredentials>
<GetDeviceCode></GetDeviceCode> <GetDeviceCode></GetDeviceCode>
</> </>
</Accordion.Collapse> </Accordion.Collapse>

@ -3,9 +3,10 @@ import { Alert, Button } from "react-bootstrap";
import { gitActionsContext } from "../../state/context"; import { gitActionsContext } from "../../state/context";
import { repository } from "../../types"; import { repository } from "../../types";
import { gitPluginContext } from "../gitui"; import { gitPluginContext } from "../gitui";
import Select from 'react-select' import AsyncSelect from 'react-select'
import { selectStyles, selectTheme } from "../../types/styles"; import { selectStyles, selectTheme } from "../../types/styles";
import { TokenWarning } from "./tokenWarning"; import { TokenWarning } from "./tokenWarning";
import RepositorySelect from "../github/repositoryselect";
interface RepositoriesProps { interface RepositoriesProps {
cloneDepth?: number cloneDepth?: number
@ -99,24 +100,10 @@ export const Repositories = (props: RepositoriesProps) => {
return ( return (
<> <>
<TokenWarning/> <RepositorySelect />
<Button onClick={fetchRepositories} className="w-100 mt-1">
<i className="fab fa-github mr-1"></i>Fetch Repositories from GitHub
</Button>
{show?
<Select
options={repoOtions}
className="mt-1"
onChange={(e: any) => e && selectRepo(e.value)}
theme={selectTheme}
styles={selectStyles}
isClearable={true}
placeholder="Type to search for a repository..."
isLoading={loading}
/>:null}
{branchOptions && branchOptions.length ? {branchOptions && branchOptions.length ?
<Select <AsyncSelect
options={branchOptions} options={branchOptions}
className="mt-1" className="mt-1"
onChange={(e: any) => e && selectRemoteBranch(e.value)} onChange={(e: any) => e && selectRemoteBranch(e.value)}

Loading…
Cancel
Save