├── server
├── .gitignore
├── requirements.txt
├── database.py
├── openapi.json
└── webserver.py
├── client
├── .eslintrc.json
├── app
│ ├── globals.css
│ ├── favicon.ico
│ ├── oauth
│ │ └── [slug]
│ │ │ ├── .page.js.swp
│ │ │ └── page.js
│ ├── layout.js
│ ├── login
│ │ └── page.js
│ └── dashboard
│ │ └── page.js
├── jsconfig.json
├── postcss.config.js
├── next.config.js
├── tailwind.config.js
├── package.json
├── .gitignore
└── public
│ ├── google-logo.svg
│ ├── logo.svg
│ └── copy.svg
└── README.md
/server/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/*
2 |
--------------------------------------------------------------------------------
/client/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/client/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 |
--------------------------------------------------------------------------------
/client/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Anil-matcha/GPT-Actions/main/client/app/favicon.ico
--------------------------------------------------------------------------------
/client/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/client/app/oauth/[slug]/.page.js.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Anil-matcha/GPT-Actions/main/client/app/oauth/[slug]/.page.js.swp
--------------------------------------------------------------------------------
/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/client/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: false,
4 | rewrites: async () => {
5 | return [
6 | {
7 | source: '/api/:path*',
8 | destination:'http://127.0.0.1:5000/:path*',
9 | },
10 | ]
11 | },
12 | }
13 |
14 | module.exports = nextConfig
15 |
--------------------------------------------------------------------------------
/client/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Inter } from 'next/font/google'
2 | import './globals.css'
3 |
4 | const inter = Inter({ subsets: ['latin'] })
5 |
6 | export const metadata = {
7 | title: 'GPTAuth',
8 | description: 'Authenticate with your GPT',
9 | }
10 |
11 | export default function RootLayout({ children }) {
12 | return (
13 |
14 |
{children}
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/client/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './pages/**/*.{js,ts,jsx,tsx,mdx}',
5 | './components/**/*.{js,ts,jsx,tsx,mdx}',
6 | './app/**/*.{js,ts,jsx,tsx,mdx}',
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
12 | 'gradient-conic':
13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
14 | },
15 | },
16 | },
17 | plugins: [],
18 | }
19 |
--------------------------------------------------------------------------------
/server/requirements.txt:
--------------------------------------------------------------------------------
1 | alembic==1.13.0
2 | blinker==1.7.0
3 | certifi==2023.11.17
4 | charset-normalizer==3.3.2
5 | click==8.1.7
6 | Flask==3.0.0
7 | Flask-Login==0.6.3
8 | Flask-Migrate==4.0.5
9 | Flask-SQLAlchemy==3.1.1
10 | idna==3.6
11 | importlib-metadata==7.0.0
12 | importlib-resources==6.1.1
13 | itsdangerous==2.1.2
14 | Jinja2==3.1.2
15 | Mako==1.3.0
16 | MarkupSafe==2.1.3
17 | oauthlib==3.2.2
18 | requests==2.31.0
19 | requests-oauthlib==1.3.1
20 | SQLAlchemy==2.0.23
21 | typing_extensions==4.9.0
22 | urllib3==2.1.0
23 | Werkzeug==3.0.1
24 | zipp==3.17.0
25 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "axios": "^1.6.2",
13 | "next": "14.0.3",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "autoprefixer": "^10.0.1",
19 | "eslint": "^8",
20 | "eslint-config-next": "14.0.3",
21 | "postcss": "^8",
22 | "tailwindcss": "^3.3.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
38 | package-lock.json
39 |
--------------------------------------------------------------------------------
/client/public/google-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/client/app/login/page.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import Image from 'next/image'
3 | import { useState,useEffect } from "react"
4 | import axios from "axios"
5 |
6 | function Login() {
7 | useEffect(()=>{
8 | axios.get("/api/login",{params:{id:localStorage.getItem('authID'), url:window.location.href}}).then((res)=>{
9 | window.location.href = localStorage.getItem('redirectTo')
10 | }).catch((err)=>{
11 | console.log(err)
12 | })
13 | },[])
14 | return (
15 |
16 |
17 |
18 |
Logging you in ...
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
26 | export default Login;
27 |
--------------------------------------------------------------------------------
/client/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/public/copy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | GPT Auth
3 |
4 | Auth for your Custom GPT
5 |
6 |
7 |
8 |
9 | **GPT Auth** provides a user-friendly solution to quickly setup oauth for your custom GPT and self-host it.
10 |
11 | ### Tutorial -> https://youtu.be/naJCyASboTk
12 |
13 | ## Key Features 🎯
14 |
15 | - **Robust Security**: Tailored for Custom GPTs, ensuring protection against unauthorized access.
16 | - **Access Control**: Effective monitoring and management of user access by GPT owners.
17 | - **Easy Integration**: User-friendly setup, comprehensive guide, and intuitive dashboard.
18 | - **Community & Support**: Access to a supportive community and dedicated developer support.
19 | - **Interactive Demo & Documentation**: Hands-on demo and extensive documentation available.
20 |
21 | ### Stack
22 |
23 | - Next.js
24 | - OpenAI
25 | - Tailwind
26 | - Flask
27 | - Sqlalchemy
28 |
29 | ### Run the project locally
30 |
31 | Minimum requirements to run the projects locally
32 |
33 | - Node.js v18
34 | - OpenAI API Key
35 | - Python3
36 |
37 | ```shell
38 |
39 | ## Client
40 |
41 | cd client
42 |
43 | npm install
44 |
45 | npm run build
46 |
47 | npm start
48 |
49 | ## Server
50 |
51 | cd ../server
52 |
53 | pip install -r requirements.txt
54 |
55 | python webserver.py
56 | ```
57 |
58 | Use ngrok to test on a https endpoint
59 |
60 | ### Hosted version of GPT Auth
61 |
62 | If you don't want to setup locally and wish to use a hosted version, you can start from https://gpt-auth.thesamur.ai/
63 |
64 | ### Demo
65 |
66 | Here is a demo chatgpt plugin built with GPT Auth to test the entire flow of the app https://chat.openai.com/g/g-xx7FJpizE-gpt-auth
67 |
--------------------------------------------------------------------------------
/client/app/oauth/[slug]/page.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import Image from 'next/image'
3 | import { useState,useEffect } from "react"
4 | import axios from "axios"
5 | import { useSearchParams } from "next/navigation";
6 |
7 | export default function Home({params:{slug}}) {
8 | const [authUrl,setAuthUrl] = useState("")
9 | const [user,setUser] = useState({email:'',password:''})
10 | const searchParams = useSearchParams()
11 | useEffect(()=>{
12 | localStorage.setItem('authID',slug)
13 | if(searchParams.get("myRedirect")!=null){
14 | localStorage.setItem('redirectTo',searchParams.get("myRedirect"))
15 | }else{
16 | var redirect_uri = searchParams.get('redirect_uri')
17 | redirect_uri += "?state="+searchParams.get('state')+"&code="+searchParams.get('client_id')
18 | localStorage.setItem('redirectTo',redirect_uri)
19 | }
20 | axios.get("/api/get_login_url",{params:{id:slug}}).then((res)=>{
21 | setAuthUrl(res.data)
22 | }).catch((err)=>{
23 | console.log(err)
24 | })
25 | },[])
26 | return (
27 |
28 |
29 |
30 |
Welcome to GPTAuth
31 | Login to continue
32 |
33 |
34 |
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/server/database.py:
--------------------------------------------------------------------------------
1 | from flask_sqlalchemy import SQLAlchemy
2 | from sqlalchemy import DateTime, extract, ForeignKey, MetaData, cast, Table,create_engine
3 | from flask import Flask
4 | from flask_migrate import Migrate
5 | from flask_login import UserMixin, LoginManager
6 |
7 | convention = {
8 | "ix": 'ix_%(column_0_label)s',
9 | "uq": "uq_%(table_name)s_%(column_0_name)s",
10 | "ck": "ck_%(table_name)s_%(constraint_name)s",
11 | "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
12 | "pk": "pk_%(table_name)s"
13 | }
14 | metadata = MetaData(naming_convention=convention)
15 | db = SQLAlchemy(metadata=metadata)
16 | app = Flask(__name__)
17 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///./test.db'
18 |
19 | login_manager = LoginManager(app)
20 |
21 | @login_manager.user_loader
22 | def load_user(user_id):
23 | getAdmin = EmbedAI_Admin.query.filter_by(id=user_id).first()
24 | return getAdmin
25 |
26 | db.init_app(app)
27 | db.app = app
28 | app.secret_key = "gptAuth"
29 | migrate = Migrate(app, db, compare_type=True,
30 | render_as_batch=True)
31 |
32 |
33 |
34 | class User(UserMixin,db.Model):
35 | __tablename__ = "user"
36 | id = db.Column(db.Integer, primary_key=True, autoincrement=True)
37 | token = db.Column(db.String(100))
38 | name = db.Column(db.String(100))
39 | email = db.Column(db.String(100), unique=True, index=True)
40 | google_id = db.Column(db.String(100))
41 | profile_image = db.Column(db.String(100000))
42 | admin_id = db.Column(db.String(100), ForeignKey("admin.id"), index=True)
43 |
44 | def get_id(self):
45 | return (self.id)
46 |
47 | class Admin(db.Model):
48 | __tablename__ = "admin"
49 | id = db.Column(db.String(100), primary_key=True)
50 | client_id = db.Column(db.String(100))
51 | client_secret = db.Column(db.String(100))
52 | google_client_id = db.Column(db.String(200))
53 | google_client_secret = db.Column(db.String(200))
54 | google_login_url = db.Column(db.String)
55 | users = db.relationship('User', backref='admin',
56 | cascade="all,delete", lazy='dynamic')
57 |
--------------------------------------------------------------------------------
/server/openapi.json:
--------------------------------------------------------------------------------
1 | {
2 | "openapi": "3.1.0",
3 | "info": {
4 | "title": "Greeting API Integration",
5 | "version": "1.0.0",
6 | "description": "API integration for fetching random greetings."
7 | },
8 | "servers": [
9 | {
10 | "url": "http://localhost:5000"
11 | }
12 | ],
13 | "paths": {
14 | "/get_greeting": {
15 | "get": {
16 | "operationId": "getRandomGreeting",
17 | "summary": "Get a random greeting",
18 | "description": "Fetches a random greeting from the Greeting API.",
19 | "responses": {
20 | "200": {
21 | "description": "Successful response with a random greeting",
22 | "content": {
23 | "application/json": {
24 | "schema": {
25 | "type": "object",
26 | "properties": {
27 | "greeting": {
28 | "type": "string",
29 | "description": "The random greeting."
30 | }
31 | },
32 | "required": ["greeting"]
33 | }
34 | }
35 | }
36 | },
37 | "400":{
38 | "description": "Error with Authentication",
39 | "content": {
40 | "application/json": {
41 | "schema": {
42 | "type": "object",
43 | "properties": {
44 | "error": {
45 | "type": "string",
46 | "description": "The error occurred."
47 | }
48 | },
49 | "required": ["error"]
50 | }
51 | }
52 | }
53 | },
54 | "default": {
55 | "description": "Unexpected error",
56 | "content": {
57 | "application/json": {
58 | "schema": {
59 | "$ref": "#/components/schemas/Error"
60 | }
61 | }
62 | }
63 | }
64 | }
65 | }
66 | }
67 | },
68 | "components": {
69 | "schemas": {
70 | "Error": {
71 | "type": "object",
72 | "properties": {
73 | "code": {
74 | "type": "integer",
75 | "format": "int32"
76 | },
77 | "message": {
78 | "type": "string"
79 | }
80 | }
81 | }
82 | },
83 | "securitySchemes": {
84 | "basicAuth": {
85 | "type": "http",
86 | "scheme": "basic"
87 | }
88 | }
89 | },
90 | "security": [
91 | {
92 | "basicAuth": []
93 | }
94 | ]
95 | }
--------------------------------------------------------------------------------
/server/webserver.py:
--------------------------------------------------------------------------------
1 | from flask import Flask,jsonify,request,session,render_template,redirect,url_for,make_response,send_from_directory,send_file
2 | from flask_login import login_required, login_user, current_user, logout_user
3 | from database import *
4 | import string, secrets
5 | import random
6 | from requests_oauthlib import OAuth2Session
7 | import urllib.parse
8 | import os
9 | from urllib.parse import urlparse, parse_qs
10 | os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
11 |
12 | GOOGLE_AUTH_BASE_URL = "https://accounts.google.com/o/oauth2/v2/auth"
13 |
14 | GOOGLE_SCOPE = [
15 | "https://www.googleapis.com/auth/userinfo.email",
16 | "https://www.googleapis.com/auth/userinfo.profile",
17 | "openid"
18 | ]
19 |
20 | DOMAIN_URL = "https://9979-122-171-21-142.ngrok-free.app"
21 |
22 | @app.route('/verify_token/', methods=['POST'])
23 | def verify_token(slug):
24 | alphabet = string.ascii_letters + string.digits
25 | token = ''.join(secrets.choice(alphabet) for i in range(8))
26 | getUser = User.query.filter_by(admin_id=slug).order_by(User.id.desc()).first()
27 | getUser.token = token
28 | db.session.commit()
29 | return jsonify(access_token=token)
30 |
31 | @app.route('/get_greeting', methods=['GET'])
32 | def get_greeting():
33 | token = request.headers['Authorization'].split(" ")[1]
34 | if User.query.filter_by(token=token).first()!=None:
35 | hello_list = ['Good morning','Good afternoon','How do you do?','What’s new?','Well, look who it is!','How have you been?','What’s up buttercup?','What’s up doc?','How are you?','What’s up','How are you going?','Yo, what’s up?','Where’ve you been?','Whassup?','Hey man','Hey dude','How are you guys?','Oi!','Mate!','Hey, what’s up','Cheers','You right?','How ya goin?','How are ya?','Howdy','What’s going on?','What do you know?','How’s this weather?']
36 | return random.choice(hello_list),200
37 | else:
38 | return "Invalid credentials",400
39 |
40 | @app.route('/get_credentials', methods=['POST'])
41 | def get_credentials():
42 | data = request.get_json()
43 | if "id" in data:
44 | id = data['id']
45 | getAdmin = Admin.query.filter_by(id=id).first()
46 | redirect_uri = DOMAIN_URL + "/login"
47 | google = OAuth2Session(
48 | getAdmin.google_client_id, scope=GOOGLE_SCOPE, redirect_uri=redirect_uri)
49 | login_url, state = google.authorization_url(GOOGLE_AUTH_BASE_URL)
50 | getAdmin.google_login_url = login_url
51 | db.session.commit()
52 | else:
53 | adminId = secrets.token_urlsafe(12)
54 | alphabet = string.ascii_letters + string.digits
55 | client_id = ''.join(secrets.choice(alphabet) for i in range(8))
56 | client_secret = ''.join(secrets.choice(alphabet) for i in range(8))
57 | google_client_id = data["googleId"]
58 | google_client_secret = data["googleSecret"]
59 | url_host = urllib.parse.urlsplit(request.url).hostname
60 | redirect_uri = DOMAIN_URL + "/login"
61 | google = OAuth2Session(
62 | google_client_id, scope=GOOGLE_SCOPE, redirect_uri=redirect_uri)
63 | login_url, state = google.authorization_url(GOOGLE_AUTH_BASE_URL)
64 | getAdmin = Admin(id=adminId,client_id=client_id,client_secret=client_secret,google_login_url=login_url,google_client_id=google_client_id,google_client_secret=google_client_secret)
65 | db.session.add(getAdmin)
66 | db.session.commit()
67 | return jsonify(id=getAdmin.client_id,secret=getAdmin.client_secret,adminId=getAdmin.id)
68 |
69 | @app.route('/get_login_url', methods=['GET'])
70 | def get_login_url():
71 | id = request.args.get('id')
72 | getAdmin = Admin.query.filter_by(id=id).first()
73 | return jsonify(getAdmin.google_login_url)
74 |
75 | @app.route('/oauth/', methods=['GET'])
76 | def oauth_slug(slug):
77 | redirect_uri = request.args.get('redirect_uri')
78 | redirect_uri += "?state="+request.args.get('state')+"&code="+request.args.get('client_id')
79 | url = DOMAIN_URL + "/oauth/"+slug+"?myRedirect="+redirect_uri
80 | return redirect(url)
81 |
82 | @app.route("/login", methods=['GET'])
83 | def login():
84 | adminId = request.args.get("id")
85 | if request.args.get("url") != None:
86 | request_url = request.args.get("url")
87 | parsed_url = urlparse(request_url)
88 | query_params = parse_qs(parsed_url.query)
89 | code = query_params.get('code', [None])[0]
90 | else:
91 | request_url = request.url
92 | code = None
93 | url_host = urllib.parse.urlsplit(request.url).hostname
94 | redirect_uri = "https://"+url_host+"/google_callback"
95 | getAdmin = Admin.query.filter_by(id=adminId).first()
96 | redirect_uri = DOMAIN_URL + "/login"
97 | google = OAuth2Session(
98 | getAdmin.google_client_id, scope=GOOGLE_SCOPE, redirect_uri=redirect_uri)
99 | token_url = "https://www.googleapis.com/oauth2/v4/token"
100 | welcome = False
101 | google.fetch_token(token_url, client_secret=getAdmin.google_client_secret,
102 | code=code)
103 | response = google.get(
104 | 'https://www.googleapis.com/oauth2/v1/userinfo').json()
105 | email = response["email"].lower()
106 | googleId = str(response["id"])
107 | name = response["name"]
108 |
109 | getUser = User.query.filter_by(email=email).first()
110 | if getUser == None:
111 | getUser = User(email=email,google_id=googleId, name=name, admin_id=getAdmin.id)
112 | db.session.add(getUser)
113 | db.session.commit()
114 | else:
115 | getUser.google_id = googleId
116 | db.session.commit()
117 | login_user(getUser, remember=True)
118 | return "Success",200
119 |
120 | if __name__ =='__main__':
121 | app.run(host="0.0.0.0", debug = True, port=5000)
122 |
--------------------------------------------------------------------------------
/client/app/dashboard/page.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import Image from 'next/image'
3 | import { useState,useEffect } from "react"
4 | import axios from "axios"
5 |
6 | function Dashboard() {
7 | const [created,setCreated] = useState(false)
8 | const [credentials,setCredentials] = useState({adminId:'',id:'',secret:''})
9 | const [URLs,setURLs] = useState({auth:'',token:''})
10 | const [googleCreds,setGoogleCreds] = useState({id:'',secret:''})
11 | const [fullUrl, setFullUrl] = useState('');
12 | const createCredentials = () => {
13 | if(googleCreds.id==""||googleCreds.secret==""){
14 | alert('Enter Google Credentials!')
15 | }else{
16 | axios.post("/api/get_credentials",{googleId:googleCreds.id,googleSecret:googleCreds.secret}).then((res)=>{
17 | setCredentials({adminId:res.data.adminId,id:res.data.id,secret:res.data.secret})
18 | setCreated(true)
19 | localStorage.setItem('authID',res.data.adminId)
20 | }).catch((err)=>{
21 | console.log(err)
22 | })
23 | }
24 | }
25 | const copyCredential = (cred) => {
26 | navigator.clipboard.writeText(cred)
27 | }
28 |
29 | useEffect(() => {
30 | // This code runs on the client-side
31 | const url = window.location.protocol + "//" + window.location.host + "/login";
32 | setFullUrl(url);
33 | }, []);
34 |
35 | const handleButtonClick = () => {
36 | // Ensure this function also safely accesses `window`
37 | if (typeof window !== 'undefined') {
38 | copyCredential(window.location.host + "/login");
39 | }
40 | };
41 |
42 | useEffect(()=>{
43 | if(credentials.adminId!=""){
44 | setURLs({auth:window.location.protocol + "//" + window.location.host + "/oauth/"+credentials.adminId,token:window.location.protocol + "//" + window.location.host + "/api/verify_token/"+credentials.adminId})
45 | }
46 | },[credentials])
47 | useEffect(()=>{
48 | if(localStorage.getItem('authID')!=null){
49 | axios.post("/api/get_credentials",{id:localStorage.getItem('authID')}).then((res)=>{
50 | setCredentials({adminId:res.data.adminId,id:res.data.id,secret:res.data.secret})
51 | setCreated(true)
52 | }).catch((err)=>{
53 | console.log(err)
54 | })
55 | }
56 | },[])
57 | return (
58 |
59 |
60 |
61 |
Welcome to GPTAuth
62 |
Create your unique credentials to use in your GPT, using your Google Oauth app details
63 |
64 |
65 |
66 |
67 |
73 |
79 |
80 |
81 |
82 | {created==false?
83 | <>
84 |
85 |
86 |
87 | setGoogleCreds({id:e.target.value,secret:googleCreds.secret})}/>
88 |
89 |
90 |
91 |
92 |
93 | setGoogleCreds({secret:e.target.value,id:googleCreds.id})}/>
94 |
95 |
96 |
100 | >
101 | :
102 | <>
103 |
104 |
105 |
106 |
107 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
137 |
138 |
139 | >
140 | }
141 |
142 | );
143 | }
144 |
145 | export default Dashboard;
146 |
--------------------------------------------------------------------------------