web3.db-fileconnector
Version:
GraphQL System Built on web3 technologies. Web3.DB is an open database built on top of web3 technologies. It is a decentralized, open-source database that allows users to store and query data in a secure and efficient manner. The system is designed to be
245 lines (216 loc) • 7.35 kB
JavaScript
import logger from "../../logger/index.js";
import { getValueByPath } from "../../utils/helpers.js";
export default class GitcoinPassportPlugin {
/**
* This will initialize all of the hooks used by this plugin.
* A plugin can register multiple hooks, each hook being linked to a function that will be executed when the hook is triggered
*/
async init() {
let HOOKS = {};
switch (this.action) {
case "validate":
HOOKS.validate = (stream) => this.isValid(stream);
break;
case "add_metadata":
HOOKS.add_metadata = (stream) => this.getScore(stream);
break;
}
return {
HOOKS: HOOKS,
};
}
/** Will check if user has a sufficient Gitcoin Passport score to use this app */
async isValid(stream) {
// Get address and network from did
let field;
/** Will convert the field to the actual value and make an exception for controller to use the address instead of the full did */
if (this.field == "controller") {
let { address } = getAddressFromDid(stream.controller);
field = address;
} else {
field = getValueByPath(stream, this.field);
}
logger.debug("address to use:", field);
if (field) {
let score = await this.computeScore(field);
/** Make sure the user's score is above the minimum score required by the developer */
if (score > this.min_score) {
return true;
} else {
return false;
}
} else {
logger.debug(
"Stream " + stream.stream_id + " rejected by Gitcoin Passport."
);
return false;
}
}
/** Returns a simple hello:world key value pair which will be added to the plugins_data field */
async getScore(stream) {
let score;
// Get address and network from did
let field;
/** Will convert the field to the actual value and make an exception for controller to use the address instead of the full did */
if (this.field == "controller") {
let { address } = getAddressFromDid(stream.controller);
field = address;
} else {
field = getValueByPath(stream, this.field);
}
if (field) {
score = await this.computeScore(field);
}
return {
score: score,
};
}
async computeScore(address) {
let score;
try {
/** Will submit passport to make sure we retrieve the latest score */
let res = await this.submitPassport(address);
logger.debug("res:", res);
// Check if passport was already submitted recently
let isRecent = isTimestampRecent(res.last_score_timestamp);
if (isRecent && res.status == "DONE") {
// Passport was already submitted recently so we use the latest score
score = Number(res.score);
} else {
/** Wait for Passport to process the submission and then retrieve the passport score */
await sleep(3000);
/** Retrieve passport score */
let passportScore = await this.getPassportScore(address);
score = Number(passportScore.score);
}
} catch (e) {
score = undefined;
}
logger.debug("score:", score);
return score;
}
/** Submit passport to API to make sure we are using the latest score */
async submitPassport(address) {
let passport_details;
/** Submit passport to generate a score */
try {
let res = await fetch(
"https://api.scorer.gitcoin.co/registry/submit-passport",
{
method: "POST",
body: JSON.stringify({
address: address,
scorer_id: this.scorer_id,
}),
headers: {
"Content-Type": "application/json",
"X-API-Key": this.api_key,
},
}
);
passport_details = await res.json();
} catch (e) {
logger.error("Error submitting passport for this scorer: ", e);
}
return passport_details;
}
/** Retrieve passport score */
async getPassportScore(address) {
let res_score_details;
try {
let res_score = await fetch(
"https://api.scorer.gitcoin.co/registry/score/" +
this.scorer_id +
"/" +
address,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-Key": this.api_key,
},
}
);
res_score_details = await res_score.json();
} catch (e) {
logger.error("Error retrieving passport score for this scorer: ", e);
}
return res_score_details;
}
}
/** HELPERS **/
/** Will check if the passport was submitted recently or not */
function isTimestampRecent(timestampStr) {
try {
// Extracting the timestamp from the response and removing the timezone part.
// This creates a consistent ISO-8601 format that JavaScript can understand.
const dateTimePart = timestampStr.split("+")[0]; // '2023-10-20T08:18:56.915704'
// Adding 'Z' to indicate UTC time zone, ensuring it's treated as UTC regardless of the system's local time zone.
const utcTimestamp = `${dateTimePart}Z`;
// Parsing the adjusted timestamp string and converting it to a Date object.
const timestamp = new Date(utcTimestamp);
// Getting the current time.
const currentTime = new Date();
// Calculating the time difference in milliseconds.
const timeDifference = currentTime.getTime() - timestamp.getTime();
logger.debug("timeDifference:", timeDifference);
// Checking if the time difference is less than 1 hour (3600000 milliseconds).
const isRecent = timeDifference < 3600000;
return isRecent;
} catch (error) {
logger.error("An error occurred while processing the timestamp:", error);
return false; // You can handle the error as appropriate for your case.
}
}
/** Returns a JSON object with the address and network based on the did */
export function getAddressFromDid(did) {
if (did) {
let didParts = did.split(":");
if (did.substring(0, 7) == "did:pkh") {
/** Explode address to retrieve did */
if (didParts.length >= 4) {
let address = didParts[4];
let network = didParts[2];
let chain = didParts[2] + ":" + didParts[3];
/** Return result */
return {
address: address,
network: network,
chain: chain,
};
} else {
/** Return null object */
return {
address: null,
network: null,
chain: null,
};
}
} else if (did.substring(0, 7) == "did:key") {
/** Return did object */
return {
address: didParts[3],
network: "key",
chain: "key",
};
} else {
/** Return null object */
return {
address: null,
network: null,
chain: null,
};
}
} else {
/** Return null object */
return {
address: null,
network: null,
chain: null,
};
}
}
/** Wait for x ms in an async function */
export const sleep = (milliseconds) => {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
};