Skip to content

Commit

Permalink
Add New API key management module
Browse files Browse the repository at this point in the history
  • Loading branch information
anudit committed Sep 26, 2021
1 parent 7fe9936 commit a1e15a5
Show file tree
Hide file tree
Showing 16 changed files with 668 additions and 50 deletions.
2 changes: 1 addition & 1 deletion components/CustomAvatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const CustomAvatar = (props) => {

useEffect(() => {
if (isAddress(address)){
fetcher(`/api/identity?address=${address}&apikey=CONVO`, "GET", {}).then((res)=>{
fetcher(`/api/identity?address=${address}&apikey=CSCpPwHnkB3niBJiUjy92YGP6xVkVZbWfK8xriDO`, "GET", {}).then((res)=>{
if (Boolean(res?.success) === true) {
setVerified(res.score);
}
Expand Down
6 changes: 3 additions & 3 deletions contexts/Web3Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const Web3ContextProvider = ({children}) => {
// there was a previous session try and validate that first.
if (Boolean(cookies.get('CONVO_SESSION')) === true) {
let tokenRes = await fetcher(
'/api/validateAuth?apikey=CONVO', "POST", {
'/api/validateAuth?apikey=CSCpPwHnkB3niBJiUjy92YGP6xVkVZbWfK8xriDO', "POST", {
signerAddress: tempaddress,
token: cookies.get('CONVO_SESSION')
}
Expand Down Expand Up @@ -169,7 +169,7 @@ export const Web3ContextProvider = ({children}) => {
async function getAuthToken(manualAddress = undefined) {

let authAdd = Boolean(manualAddress) === true ? manualAddress : signerAddress;
let tokenRes = await fetcher('/api/validateAuth?apikey=CONVO', "POST", {
let tokenRes = await fetcher('/api/validateAuth?apikey=CSCpPwHnkB3niBJiUjy92YGP6xVkVZbWfK8xriDO', "POST", {
signerAddress: authAdd,
token: cookies.get('CONVO_SESSION')
});
Expand Down Expand Up @@ -223,7 +223,7 @@ export const Web3ContextProvider = ({children}) => {
[ ethers.utils.hexlify(ethers.utils.toUtf8Bytes(data)), signerAddress.toLowerCase() ]
);

let res = await fetcher(`/api/auth?apikey=CONVO`, "POST", {
let res = await fetcher(`/api/auth?apikey=CSCpPwHnkB3niBJiUjy92YGP6xVkVZbWfK8xriDO`, "POST", {
signerAddress,
signature,
timestamp
Expand Down
2 changes: 1 addition & 1 deletion hooks/Ably.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import Ably from "ably/promises";
import { useEffect } from 'react'

const ably = new Ably.Realtime.Promise({ authUrl: process.env.NEXT_PUBLIC_API_SITE_URL + '/api/getAblyAuth?apikey=CONVO' });
const ably = new Ably.Realtime.Promise({ authUrl: process.env.NEXT_PUBLIC_API_SITE_URL + '/api/getAblyAuth?apikey=CSCpPwHnkB3niBJiUjy92YGP6xVkVZbWfK8xriDO' });

export function useChannel(channelName, callbackOnMessage) {
const channel = ably.channels.get(channelName);
Expand Down
139 changes: 139 additions & 0 deletions lib/apikeys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
const { randomId } = require("@/utils/stringUtils");
const Redis = require("ioredis");

export async function createApikey(address){

let client = new Redis(process.env.REDIS_CONNECTION);

let promise = new Promise((res) => {

let newKey = "CS"+randomId(38);
// let newKey="CONVO";
let dt = new Date();
let date = String(dt.getMonth()+1).padStart('2','0') + String(dt.getFullYear()).slice(2);

client.get(`${address}-keys`).then(prev=>{

prev = JSON.parse(prev);
if (prev === null){
let newData = {
"activeKey":newKey,
"pastKeys":[],
"whitelist":[]
};
// No keys made yet.
client
.multi()
.set(`${address}-keys`, JSON.stringify(newData))
.set(newKey,true)
.set(`${newKey}-usage-${date}`, 0)
.exec((err)=>{
if (err) {
return res(err);
}
else {
return res(newData);
}
});
}
else {
if ( prev['pastKeys'].length >= 5){
return res({
success: false,
error: "Limit of 10 Keys/Account exceeded. Please contact [email protected] to increase limit."
});
}
else{
// Regenerate key.
let newPastkeys = prev['pastKeys'].concat([prev['activeKey']])
let newKey = "CS"+randomId(38);
let newData = {
"activeKey":newKey,
"pastKeys":newPastkeys,
"whitelist":[]
};
client
.multi()
.set(`${address}-keys`, JSON.stringify(newData))
.del(prev['activeKey'])
.set(newKey, true)
.set(`${newKey}-usage-${date}`, 0)
.exec((err, resp)=>{
console.log(resp);
if (err) {
return res(err);
}
else {
return res(newData);
}
});
}
}
});

});
let result = await promise;
return result;

}

export async function getApikeyData(address){

let client = new Redis(process.env.REDIS_CONNECTION);
let promise = new Promise((res) => {

client.get(`${address}-keys`).then(data=>{
if (data === null){
res({
'activeKey':null
})
}
else {
let jsonData = JSON.parse(data);
let recObj = client.multi().keys(`${jsonData['activeKey']}-usage-*`);
for (let index = 0; index < jsonData['pastKeys'].length; index++) {
recObj = recObj.keys(`${jsonData['pastKeys'][index]}-usage-*`)
}
recObj.exec((err, results) => {
if (err){
return res(err)
}
else {

let qk = [];

let recObj2 = client.multi();
for (let i = 0; i < results.length; i++) {
for (let j = 0; j < results[i][1].length; j++) {
qk.push(results[i][1][j]);
recObj2 = recObj2.get(results[i][1][j]);
}
}
recObj2.exec((err2, results2) => {
if (err){
return res(err2);
}
else {
let resDb = {}
for (let i = 0; i < results2.length; i++) {
resDb[qk[i]] = results2[i][1];
}
return res({
...jsonData,
data:resDb
});
}

});

}
})
}
});
});

let result = await promise;
client.quit();
return result;

}
72 changes: 62 additions & 10 deletions middlewares/withApikey.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,69 @@
const Redis = require("ioredis");

const withApikey = (next) => async (req, res) => {

if (req.method === 'OPTIONS') {
return res.status(200).end();
}
try {

if (req.method === 'OPTIONS') {
return res.status(200).end();
}

if (Object.keys(req.query).includes('apikey') === false){
return res.status(401).json({
'success':false,
'error': 'No API key, please refer to the integration docs at https://docs.theconvo.space/ to see how to get and use a new API key.'
});
}

if (req.query.apikey == 'CONVO' ){
return await next(req, res);
}

let client = new Redis(process.env.REDIS_CONNECTION);

const CAP = 1000000;
let dt = new Date();
let date = String(dt.getMonth()+1).padStart('2','0') + String(parseInt(dt.getFullYear())).slice(2);

return client.multi()
.get(req.query.apikey)
.get(`${req.query.apikey}-usage-${date}`)
.exec(async (err, data)=>{

if (Object.keys(req.query).includes('apikey') === false || req.query.apikey !== 'CONVO' ){
return res.status(401).json({
'success':false,
'error': 'Invalid API key, please refer to the integration docs at https://docs.theconvo.space/ to see how to get and use a new API key.'
if (data[0][1] == 'true') { // Valid API key

if(parseInt(data[1][1]) >= CAP){ // Over the limit
res.setHeader('X-Rate-Limit-Limit', CAP)
res.setHeader('X-Rate-Limit-Remaining', 0 )
client.quit();
return res.status(429).end("Rate limit exceeded")
}

else {
client.incr(`${req.query.apikey}-usage-${date}`)
res.setHeader('X-Rate-Limit-Limit', CAP)
res.setHeader('X-Rate-Limit-Remaining', CAP - parseInt(data[1][1]) -1 )
client.quit();
return await next(req, res);
}

}
else {
client.quit();
return res.status(401).json({
'success': false,
'error': 'Invalid API key, please refer to the integration docs at https://docs.theconvo.space/ to see how to get and use a new API key.'
});
}

});

} catch (error) {
return res.status(500).json({
'success': false,
'error': error
});
}
else {
return await next(req, res);

}

}
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@
"prop-types": "^15.7.2",
"qs": "^6.10.1",
"react": "^17.0.2",
"react-data-table-component": "^7.3.0",
"react-data-table-component": "^7.3.1",
"react-dom": "^17.0.2",
"react-hotkeys-hook": "^3.4.1",
"react-hotkeys-hook": "^3.4.2",
"react-linkify": "^1.0.0-alpha",
"react-qr-code": "^2.0.2",
"recharts": "^2.1.4",
"styled-components": "^5.3.1",
"swr": "^1.0.1",
"telegraf": "^4.4.2",
Expand Down
59 changes: 59 additions & 0 deletions pages/api/apikey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import validateAuth from "@/lib/validateAuth";
import { createApikey, getApikeyData } from "@/lib/apikeys";

const handler = async(req, res) => {

try {

if (req.method === "GET") {
if (validateAuth(req.query?.token, req.query?.signerAddress) === true) {

// return all apikey data.
let resp = await getApikeyData(req.query.signerAddress);
return res.status(200).json({
success: true,
...resp
});

}
else {
return res.status(401).json({
success: false,
'error':'Invalid Auth'
});
}

}
else if (req.method === "POST" ) {

if (validateAuth(req.body.token, req.body.signerAddress) === true) {

// create/regenerate api key.
let data = await createApikey(req.body.signerAddress);
return res.status(200).json({
success: true,
...data
});

}
else {
return res.status(401).json({
success: false,
'error':'Invalid Auth'
});
}

}
else {
return res.status(404).json({
success: false,
'error':'Invalid request method.'
});
}

} catch (error) {
return res.status(500).json({ success: false,'error':error.toString() });
}
}

export default handler;
2 changes: 1 addition & 1 deletion pages/dashboard/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const DataTokenView = () => {
}
else {

let data = await fetcher(`/api/comments?author=${signerAddress}&apikey=CONVO`, "GET");
let data = await fetcher(`/api/comments?author=${signerAddress}&apikey=CSCpPwHnkB3niBJiUjy92YGP6xVkVZbWfK8xriDO`, "GET");
const content = new Blob([JSON.stringify(data)]);
const client = new NFTStorage({ token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJnaXRodWJ8MTIwMTU1NTMiLCJpc3MiOiJuZnQtc3RvcmFnZSIsImlhdCI6MTYxNjMwMjY3NzYyNCwibmFtZSI6ImRlZmF1bHQifQ.nf5d4LV9CZSGrAwus6Cb3q9amggU278rPEJSlNujLPY" });
let cid = await client.storeBlob(content);
Expand Down
Loading

1 comment on commit a1e15a5

@vercel
Copy link

@vercel vercel bot commented on a1e15a5 Sep 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.