mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-02-09 21:45:53 -05:00
3box profiles (#9)
* Fix auth * Added 3box profile info * Added player names in list * Added hasura action to fetch verified accounts on 3Box * Added usernames * fix router
This commit is contained in:
1
packages/app-react/@types/3box.d.ts
vendored
Normal file
1
packages/app-react/@types/3box.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module '3box';
|
||||
1
packages/app-react/@types/react-router-dom.d.ts
vendored
Normal file
1
packages/app-react/@types/react-router-dom.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module 'react-router-dom';
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"3box": "^1.18.1",
|
||||
"@apollo/client": "^3.0.0-beta.43",
|
||||
"@apollo/react-hooks": "^3.1.5",
|
||||
"@material-ui/core": "^4.9.10",
|
||||
@@ -20,6 +21,7 @@
|
||||
"graphql-codegen-hasura-core": "^4.8.4",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.4.1",
|
||||
"typescript": "~3.7.2",
|
||||
"uuid": "^7.0.3",
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import React from 'react';
|
||||
import { ApolloProvider } from '@apollo/react-hooks';
|
||||
|
||||
import { CssBaseline } from '@material-ui/core';
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
import { createApolloClient } from './apollo';
|
||||
|
||||
import { Home } from './containers/Home';
|
||||
import Web3ContextProvider from './contexts/Web3';
|
||||
|
||||
import Header from './components/Header';
|
||||
import Routes from './Routes';
|
||||
|
||||
const apolloClient = createApolloClient();
|
||||
|
||||
function App() {
|
||||
@@ -14,7 +17,10 @@ function App() {
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<Web3ContextProvider>
|
||||
<CssBaseline/>
|
||||
<Home/>
|
||||
<Router>
|
||||
<Header/>
|
||||
<Routes/>
|
||||
</Router>
|
||||
</Web3ContextProvider>
|
||||
</ApolloProvider>
|
||||
);
|
||||
|
||||
19
packages/app-react/src/Routes.tsx
Normal file
19
packages/app-react/src/Routes.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Switch, Route, Redirect } from "react-router-dom";
|
||||
|
||||
import { Home } from './containers/Home';
|
||||
import { Player } from './containers/Player';
|
||||
|
||||
export default function Routes() {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<Home />
|
||||
</Route>
|
||||
<Route path="/player/:playerId">
|
||||
<Player />
|
||||
</Route>
|
||||
<Redirect to="/"/>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
@@ -24,7 +24,11 @@ export function loginLoading(client, loading = true) {
|
||||
|
||||
export async function login(client, token, ethAddress) {
|
||||
loginLoading(client);
|
||||
|
||||
client.writeData({
|
||||
data: {
|
||||
authToken: token,
|
||||
},
|
||||
});
|
||||
return client.query({
|
||||
query: queries.get_MyAccount,
|
||||
variables: { eth_address: ethAddress }
|
||||
@@ -36,14 +40,13 @@ export async function login(client, token, ethAddress) {
|
||||
client.writeData({
|
||||
data: {
|
||||
authState: 'logged',
|
||||
authToken: token,
|
||||
playerId: res.data.Account[0].Player.id,
|
||||
},
|
||||
});
|
||||
setTokenInStore(token);
|
||||
})
|
||||
.catch(async error => {
|
||||
logout();
|
||||
logout(client);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
15
packages/app-react/src/components/Header.tsx
Normal file
15
packages/app-react/src/components/Header.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
import {Login} from "../containers/Login";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<Box>
|
||||
<Link to={`/`}><button>Home</button></Link>
|
||||
<Login/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
export default function Player({ player }: { player: any }) {
|
||||
return (
|
||||
<Box>
|
||||
{player.id}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
90
packages/app-react/src/components/PlayerDetails.tsx
Normal file
90
packages/app-react/src/components/PlayerDetails.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import {useMutation} from "@apollo/react-hooks";
|
||||
|
||||
import ThreeBox from '3box';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
import {useMyPlayer} from "../graphql/hooks";
|
||||
import {getPlayerETHAddress} from "../utils/players";
|
||||
import mutations from '../graphql/mutations';
|
||||
|
||||
function getProfilePicture(boxProfile: any) {
|
||||
const imageHash = boxProfile && boxProfile.image && boxProfile.image[0] && boxProfile.image[0].contentUrl && boxProfile.image[0].contentUrl['/'];
|
||||
if(imageHash) {
|
||||
return `https://ipfs.infura.io/ipfs/${imageHash}`;
|
||||
} else {
|
||||
return 'https://i.imgur.com/RXJO8FD.png';
|
||||
}
|
||||
}
|
||||
|
||||
export default function PlayerDetails({ player }: { player: any }) {
|
||||
const myPlayer = useMyPlayer();
|
||||
const isMyPlayer = myPlayer && myPlayer.id === player.id;
|
||||
const [boxProfile, setBoxProfile] = useState<any>();
|
||||
const [usernameInput, setUsernameInput] = useState<string>(player.username);
|
||||
const [updateBoxProfiles] = useMutation(mutations.UpdateBoxProfiles);
|
||||
const [updateUsername] = useMutation(mutations.UpdateUsername);
|
||||
|
||||
const ethAddress = getPlayerETHAddress(player);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
|
||||
const boxProfile = await ThreeBox.getProfile(ethAddress);
|
||||
setBoxProfile(boxProfile);
|
||||
|
||||
})();
|
||||
}, [ethAddress]);
|
||||
|
||||
const goToEditBoxProfile = useCallback(() => {
|
||||
window.open(`https://3box.io/${ethAddress}/edit`)
|
||||
}, []);
|
||||
|
||||
const editUserName = useCallback(() => {
|
||||
// TODO Apollo does not updates caches as it expects that the mutation returns an object with id, but hasura returns { returning: [{id}] }
|
||||
updateUsername({
|
||||
variables: {
|
||||
username: usernameInput
|
||||
}
|
||||
}).then(res =>
|
||||
console.log('updated username', res.data)
|
||||
);
|
||||
}, [usernameInput]);
|
||||
|
||||
const updateAccounts = useCallback(() => {
|
||||
updateBoxProfiles().then(res =>
|
||||
console.log('updated verified profiles', res.data.updateBoxProfile.updatedProfiles)
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<h3>{player.username}</h3>
|
||||
<h4>{player.id}</h4>
|
||||
{isMyPlayer && <button onClick={goToEditBoxProfile}>Edit profile</button>}
|
||||
{isMyPlayer && <div>
|
||||
<input value={usernameInput} onChange={e => setUsernameInput(e.target.value)} />
|
||||
<button onClick={editUserName}>Change username</button>
|
||||
</div>}
|
||||
{boxProfile ?
|
||||
<div>
|
||||
<p><b>Name:</b> {boxProfile.name}</p>
|
||||
<p><b>Description:</b> {boxProfile.description}</p>
|
||||
<img src={getProfilePicture(boxProfile)} width={100} alt="profile-image"/>
|
||||
</div>
|
||||
:
|
||||
<p>Loading box profile</p>
|
||||
}
|
||||
<div>
|
||||
<h4>External accounts</h4>
|
||||
<ul>
|
||||
{player.Accounts.map((account: any) =>
|
||||
<li key={account.type}><b>{account.type}</b>: {account.identifier}</li>
|
||||
)}
|
||||
</ul>
|
||||
{isMyPlayer && <button onClick={updateAccounts}>Update accounts</button>}
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
13
packages/app-react/src/components/PlayerListItem.tsx
Normal file
13
packages/app-react/src/components/PlayerListItem.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
export default function PlayerListItem({ player }: { player: any }) {
|
||||
return (
|
||||
<Box>
|
||||
{player.username}
|
||||
<Link to={`/player/${player.id}`}><button>View player</button></Link>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
|
||||
import PlayerList from './PlayerList';
|
||||
import { Login } from './Login';
|
||||
import { MyPlayer } from './MyPlayer';
|
||||
import {localQueries} from "../apollo";
|
||||
|
||||
export const Home: React.FC = () => {
|
||||
const { data, loading } = useQuery(localQueries.get_authState);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<PlayerList/>
|
||||
<Login/>
|
||||
{!loading && data?.authState === 'logged' && <MyPlayer/>}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import React, {useContext} from 'react';
|
||||
import React, {useContext, useCallback} from 'react';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
import { Web3Context } from '../contexts/Web3';
|
||||
import {localQueries} from "../apollo";
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import {Link} from "react-router-dom";
|
||||
|
||||
export const Login: React.FC = () => {
|
||||
const { data, loading } = useQuery(localQueries.get_authState);
|
||||
|
||||
const { connectWeb3 } = useContext(Web3Context);
|
||||
const { connectWeb3, disconnect } = useContext(Web3Context);
|
||||
|
||||
const connect = useCallback(() => {
|
||||
connectWeb3().catch(console.error);
|
||||
}, [connectWeb3]);
|
||||
|
||||
if(loading || data?.authState === 'loading') {
|
||||
return (
|
||||
@@ -18,13 +23,18 @@ export const Login: React.FC = () => {
|
||||
</Box>
|
||||
);
|
||||
} else if(data?.authState === 'logged') {
|
||||
const { playerId } = data;
|
||||
return (
|
||||
<Box>Connected</Box>
|
||||
<Box>
|
||||
Connected
|
||||
<Link to={`/player/${playerId}`}><button>View my player</button></Link>
|
||||
<button onClick={disconnect}>Logout</button>
|
||||
</Box>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Box>
|
||||
Unknown state
|
||||
<button onClick={connect}>Connect</button>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
import Player from '../components/Player';
|
||||
import { useMyPlayer } from '../graphql/hooks';
|
||||
|
||||
export const MyPlayer: React.FC = () => {
|
||||
const { data, called, loading, error } = useMyPlayer();
|
||||
|
||||
if(error) {
|
||||
return <div>error</div>
|
||||
}
|
||||
if(loading || !called) {
|
||||
return <div>loading</div>
|
||||
}
|
||||
|
||||
const myPlayer = data.Player[0];
|
||||
return (
|
||||
<Box>
|
||||
<h4>My player</h4>
|
||||
<Player player={myPlayer} />
|
||||
</Box>
|
||||
)
|
||||
};
|
||||
|
||||
33
packages/app-react/src/containers/Player.tsx
Normal file
33
packages/app-react/src/containers/Player.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import {useQuery} from "@apollo/react-hooks";
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Box } from '@material-ui/core';
|
||||
|
||||
import PlayerDetails from '../components/PlayerDetails';
|
||||
|
||||
import queries from "../graphql/queries";
|
||||
|
||||
export const Player: React.FC = ({}) => {
|
||||
const { playerId } = useParams();
|
||||
const { data, loading, error } = useQuery(queries.get_Player, {
|
||||
variables: {
|
||||
player_id: playerId,
|
||||
}
|
||||
});
|
||||
|
||||
if(error) {
|
||||
return <div>error</div>
|
||||
}
|
||||
if(loading) {
|
||||
return <div>loading</div>
|
||||
}
|
||||
|
||||
const myPlayer = data.Player[0];
|
||||
return (
|
||||
<Box>
|
||||
<h4>Player</h4>
|
||||
<PlayerDetails player={myPlayer} />
|
||||
</Box>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -6,19 +6,23 @@ import {useQuery} from '@apollo/react-hooks';
|
||||
|
||||
import queries from '../graphql/queries';
|
||||
|
||||
import Player from '../components/Player';
|
||||
import PlayerListItem from '../components/PlayerListItem';
|
||||
|
||||
export default function PlayerList() {
|
||||
const { data, loading } = useQuery(queries.get_Player);
|
||||
const { data, loading, error } = useQuery(queries.get_Player);
|
||||
|
||||
if(error) {
|
||||
return <div>error: {error.message}</div>
|
||||
}
|
||||
if(loading) {
|
||||
return <div>loading</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<h4>Player list</h4>
|
||||
{data.Player.map((player: any) =>
|
||||
<Player key={player.id} player={player} />
|
||||
<PlayerListItem key={player.id} player={player} />
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -8,16 +8,18 @@ import {useApolloClient} from '@apollo/react-hooks';
|
||||
|
||||
import config from '../config';
|
||||
import { did } from '@the-game/utils';
|
||||
import {loginLoading, login, getTokenFromStore} from '../apollo/auth';
|
||||
import {loginLoading, login, logout, getTokenFromStore} from '../apollo/auth';
|
||||
|
||||
type Web3ContextType = {
|
||||
ethersProvider: Web3Provider | null,
|
||||
connectWeb3: () => void;
|
||||
connectWeb3: () => Promise<void>;
|
||||
disconnect: () => void,
|
||||
}
|
||||
|
||||
export const Web3Context = createContext<Web3ContextType>({
|
||||
ethersProvider: null,
|
||||
connectWeb3: () => {},
|
||||
connectWeb3: async () => {},
|
||||
disconnect: () => undefined,
|
||||
});
|
||||
|
||||
const providerOptions = {
|
||||
@@ -75,6 +77,11 @@ const Web3ContextProvider: React.FC = props => {
|
||||
}
|
||||
}, [apolloClient, connectDID]);
|
||||
|
||||
const disconnect = useCallback(async () => {
|
||||
web3Modal.clearCachedProvider();
|
||||
logout(apolloClient);
|
||||
}, [apolloClient]);
|
||||
|
||||
useEffect(() => {
|
||||
if(web3Modal.cachedProvider) {
|
||||
connectWeb3().catch(console.error);
|
||||
@@ -82,7 +89,7 @@ const Web3ContextProvider: React.FC = props => {
|
||||
}, [connectWeb3]);
|
||||
|
||||
return (
|
||||
<Web3Context.Provider value={{ ethersProvider, connectWeb3 }}>
|
||||
<Web3Context.Provider value={{ ethersProvider, connectWeb3, disconnect }}>
|
||||
{props.children}
|
||||
</Web3Context.Provider>
|
||||
);
|
||||
|
||||
@@ -3,11 +3,13 @@ const fragments: any = {};
|
||||
fragments.PlayerFragment = `
|
||||
fragment PlayerFragment on Player {
|
||||
id
|
||||
username
|
||||
}
|
||||
`;
|
||||
fragments.AccountFragment = `
|
||||
fragment AccountFragment on Account {
|
||||
identifier
|
||||
type
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import queries from './queries';
|
||||
|
||||
export function useMyPlayer() {
|
||||
const authStateQuery = useQuery(localQueries.get_authState);
|
||||
const [getMyPlayer, myPlayerQuery] = useLazyQuery(queries.get_MyPlayer);
|
||||
const [getMyPlayer, myPlayerQuery] = useLazyQuery(queries.get_Player);
|
||||
|
||||
const playerId = authStateQuery.data?.playerId;
|
||||
|
||||
@@ -21,5 +21,5 @@ export function useMyPlayer() {
|
||||
}
|
||||
}, [getMyPlayer, playerId]);
|
||||
|
||||
return myPlayerQuery;
|
||||
return myPlayerQuery.data?.Player[0];
|
||||
}
|
||||
|
||||
31
packages/app-react/src/graphql/mutations.ts
Normal file
31
packages/app-react/src/graphql/mutations.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const mutations: any = {};
|
||||
|
||||
mutations.UpdateBoxProfiles = gql`
|
||||
mutation UpdateBoxProfiles {
|
||||
updateBoxProfile {
|
||||
success
|
||||
updatedProfiles
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
mutations.UpdateUsername = gql`
|
||||
mutation UpdateUsername($username: String!) {
|
||||
update_Player(
|
||||
where: {},
|
||||
_set: {
|
||||
username: $username
|
||||
}
|
||||
) {
|
||||
affected_rows
|
||||
returning {
|
||||
id
|
||||
username
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default mutations;
|
||||
@@ -5,23 +5,18 @@ import fragments from './fragments';
|
||||
const queries: any = {};
|
||||
|
||||
queries.get_Player = gql`
|
||||
query GetPlayer {
|
||||
Player {
|
||||
...PlayerFragment
|
||||
}
|
||||
}
|
||||
${fragments.PlayerFragment}
|
||||
`;
|
||||
|
||||
queries.get_MyPlayer = gql`
|
||||
query GetPlayer($player_id: uuid) {
|
||||
Player(
|
||||
where: { id: { _eq: $player_id } }
|
||||
) {
|
||||
...PlayerFragment
|
||||
Accounts {
|
||||
...AccountFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.PlayerFragment}
|
||||
${fragments.AccountFragment}
|
||||
`;
|
||||
|
||||
queries.get_MyAccount = gql`
|
||||
|
||||
3
packages/app-react/src/utils/players.ts
Normal file
3
packages/app-react/src/utils/players.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function getPlayerETHAddress(player: any) {
|
||||
return player.Accounts.find((a: any) => a.type === "ETHEREUM").identifier;
|
||||
}
|
||||
1
packages/backend/@types/3box.d.ts
vendored
Normal file
1
packages/backend/@types/3box.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module '3box';
|
||||
@@ -13,6 +13,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"3box": "^1.18.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"ethers": "^4.0.46",
|
||||
"express": "^4.17.1",
|
||||
|
||||
11
packages/backend/src/handlers/actions/routes.ts
Normal file
11
packages/backend/src/handlers/actions/routes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import express from 'express';
|
||||
|
||||
import { asyncHandlerWrapper } from '../../lib/apiHelpers';
|
||||
|
||||
import updateBoxProfileHandler from './updateBoxProfile/handler';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/updateBoxProfile', asyncHandlerWrapper(updateBoxProfileHandler));
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Request, Response } from 'express';
|
||||
import {hasuraQuery} from "../../../lib/hasuraHelpers";
|
||||
import {getPlayerETHAddress} from "../../../lib/playerHelpers";
|
||||
import Box from '3box';
|
||||
|
||||
const getPlayerQuery = `
|
||||
query GetPlayer ($playerId: uuid!) {
|
||||
Player(
|
||||
where: {
|
||||
id: { _eq: $playerId }
|
||||
}
|
||||
) {
|
||||
id
|
||||
Accounts {
|
||||
identifier
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const upsertAccount = `
|
||||
mutation upsert_Account($objects: [Account_insert_input!]!) {
|
||||
insert_Account (
|
||||
objects: $objects,
|
||||
on_conflict: {
|
||||
constraint: Account_identifier_type_player_key,
|
||||
update_columns: [identifier]
|
||||
}
|
||||
) {
|
||||
affected_rows
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const handler = async (req: Request, res: Response) => {
|
||||
|
||||
const { session_variables } = req.body;
|
||||
const role = session_variables['x-hasura-role'];
|
||||
const playerId = session_variables['x-hasura-user-id'];
|
||||
|
||||
if(role !== 'player') {
|
||||
throw new Error('expected role player');
|
||||
}
|
||||
|
||||
const result = await hasuraQuery(getPlayerQuery, {
|
||||
playerId,
|
||||
});
|
||||
|
||||
const player = result.Player[0];
|
||||
if(!player) {
|
||||
throw new Error('unknown-player');
|
||||
}
|
||||
|
||||
const ethAddress = getPlayerETHAddress(player);
|
||||
const boxProfile = await Box.getProfile(ethAddress);
|
||||
const verifiedProfile = await Box.getVerifiedAccounts(boxProfile);
|
||||
const updatedProfiles = await updateVerifiedProfiles(playerId, verifiedProfile);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
updatedProfiles,
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
async function updateVerifiedProfiles(playerId: string, verifiedProfiles: any) {
|
||||
const updatedProfiles: string[] = [];
|
||||
|
||||
if(verifiedProfiles.github) {
|
||||
const result = await hasuraQuery(upsertAccount, {
|
||||
objects: [
|
||||
{
|
||||
player_id: playerId,
|
||||
type: 'GITHUB',
|
||||
identifier: verifiedProfiles.github.username,
|
||||
linkToProof: verifiedProfiles.github.proof,
|
||||
}
|
||||
],
|
||||
});
|
||||
if(result.affected_rows === 0) {
|
||||
throw new Error('Error while upserting github profile');
|
||||
}
|
||||
updatedProfiles.push('github');
|
||||
}
|
||||
|
||||
if(verifiedProfiles.twitter) {
|
||||
const result = await hasuraQuery(upsertAccount, {
|
||||
objects: [
|
||||
{
|
||||
player_id: playerId,
|
||||
type: 'TWITTER',
|
||||
identifier: verifiedProfiles.twitter.username,
|
||||
linkToProof: verifiedProfiles.twitter.proof,
|
||||
}
|
||||
],
|
||||
});
|
||||
if(result.affected_rows === 0) {
|
||||
throw new Error('Error while upserting github profile');
|
||||
}
|
||||
updatedProfiles.push('twitter');
|
||||
}
|
||||
|
||||
return updatedProfiles;
|
||||
}
|
||||
|
||||
export default handler;
|
||||
@@ -1,6 +1,4 @@
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
import config from '../../config';
|
||||
import { hasuraQuery } from '../../lib/hasuraHelpers';
|
||||
|
||||
const getPlayerQuery = `
|
||||
query GetPlayerFromETH ($eth_address: String) {
|
||||
@@ -18,40 +16,27 @@ query GetPlayerFromETH ($eth_address: String) {
|
||||
`;
|
||||
|
||||
const createProfileMutation = `
|
||||
mutation CreateAccountFromETH ($eth_address: String) {
|
||||
mutation CreateAccountFromETH ($eth_address: String!, $username: String!) {
|
||||
insert_Account(
|
||||
objects: {
|
||||
type: "ETHEREUM",
|
||||
identifier: $eth_address
|
||||
Player: {
|
||||
data: {}
|
||||
data: {
|
||||
username: $username
|
||||
}
|
||||
}
|
||||
}) {
|
||||
returning {
|
||||
identifier
|
||||
}
|
||||
affected_rows
|
||||
returning {
|
||||
identifier
|
||||
Player {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
async function hasuraQuery(query: string, qv: any = {}) {
|
||||
const result = await fetch(config.graphqlURL, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query: query, variables: qv }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-hasura-access-key': config.adminKey,
|
||||
},
|
||||
});
|
||||
|
||||
const { errors, data } = await result.json();
|
||||
|
||||
if(errors) {
|
||||
throw new Error(JSON.stringify(errors));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
interface IPlayer {
|
||||
id: string
|
||||
}
|
||||
@@ -59,7 +44,11 @@ interface IPlayer {
|
||||
export async function createPlayer(ethAddress: string): Promise<IPlayer> {
|
||||
const resProfile = await hasuraQuery(createProfileMutation, {
|
||||
eth_address: ethAddress,
|
||||
username: ethAddress,
|
||||
});
|
||||
if(resProfile.insert_Account.affected_rows !== 2) {
|
||||
throw new Error('error while creating profile');
|
||||
}
|
||||
return resProfile.insert_Account.returning[0].Player;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import express from 'express';
|
||||
|
||||
import { asyncHandlerWrapper } from '../lib/apiHelpers';
|
||||
|
||||
import actionsRoutes from './actions/routes';
|
||||
|
||||
import authHandler from './auth-webhook/handler';
|
||||
|
||||
const router = express.Router();
|
||||
@@ -11,5 +13,6 @@ router.get('/', function (req, res) {
|
||||
});
|
||||
|
||||
router.get('/auth-webhook', asyncHandlerWrapper(authHandler));
|
||||
router.use('/actions', actionsRoutes);
|
||||
|
||||
export default router;
|
||||
|
||||
20
packages/backend/src/lib/hasuraHelpers.ts
Normal file
20
packages/backend/src/lib/hasuraHelpers.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import fetch from 'node-fetch';
|
||||
import config from '../config';
|
||||
|
||||
export async function hasuraQuery(query: string, qv: any = {}) {
|
||||
const result = await fetch(config.graphqlURL, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query: query, variables: qv }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-hasura-access-key': config.adminKey,
|
||||
},
|
||||
});
|
||||
|
||||
const { errors, data } = await result.json();
|
||||
|
||||
if(errors) {
|
||||
throw new Error(JSON.stringify(errors));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
3
packages/backend/src/lib/playerHelpers.ts
Normal file
3
packages/backend/src/lib/playerHelpers.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function getPlayerETHAddress(player: any) {
|
||||
return player.Accounts.find((a: any) => a.type === "ETHEREUM").identifier;
|
||||
}
|
||||
@@ -6,6 +6,11 @@
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
}
|
||||
"esModuleInterop": true,
|
||||
"typeRoots": ["./@types"]
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"./@types"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user