From d7b2c64f669b4dfab405db29a4f455f3239d128d Mon Sep 17 00:00:00 2001 From: yu-zhen Date: Wed, 6 Nov 2024 17:33:44 +0700 Subject: [PATCH] feat: allow uploading file directly instead of collecting from the console --- packages/coordinator/.gitignore | 1 + .../coordinator/round-metadata-example.json | 9 ++ .../scripts/uploadRoundMetadata.ts | 106 +++++++++--------- 3 files changed, 64 insertions(+), 52 deletions(-) create mode 100644 packages/coordinator/round-metadata-example.json diff --git a/packages/coordinator/.gitignore b/packages/coordinator/.gitignore index 1e6853cb..6416d992 100644 --- a/packages/coordinator/.gitignore +++ b/packages/coordinator/.gitignore @@ -4,3 +4,4 @@ zkeys/ proofs/ tally.json session-keys.json +round-metadata.json diff --git a/packages/coordinator/round-metadata-example.json b/packages/coordinator/round-metadata-example.json new file mode 100644 index 00000000..b66b3119 --- /dev/null +++ b/packages/coordinator/round-metadata-example.json @@ -0,0 +1,9 @@ +{ + "roundId": "Round-ID", + "description": "This is the test round.", + "startsAt": "2024-11-05T17:00:00.000Z", + "registrationEndsAt": "2024-11-06T10:00:00.000Z", + "votingStartsAt": "2024-11-06T10:00:00.000Z", + "votingEndsAt": "2024-11-06T17:00:00.000Z", + "tallyFile": "[storage url]/[roundId].json" +} diff --git a/packages/coordinator/scripts/uploadRoundMetadata.ts b/packages/coordinator/scripts/uploadRoundMetadata.ts index 9862c674..102763b9 100644 --- a/packages/coordinator/scripts/uploadRoundMetadata.ts +++ b/packages/coordinator/scripts/uploadRoundMetadata.ts @@ -3,6 +3,7 @@ import { parse, isValid } from "date-fns"; import { enGB } from "date-fns/locale"; import dotenv from "dotenv"; +import fs from "fs"; import path from "path"; import * as readline from "readline"; @@ -21,13 +22,26 @@ interface IUploadMetadataProps { name: string; } +const METADATA_PATH = path.resolve("./round-metadata.json"); + dotenv.config({ path: path.resolve(import.meta.dirname, "../.env") }); +/** + * A function to check if the input string is a valid date + * @param formattedDateStr a date string to be checked + * @returns if the date string is valid or not + */ function isValidDate(formattedDateStr: string) { const parsed = parse(`${formattedDateStr}Z`, "yyyy/M/d H:m:sX", new Date(), { locale: enGB }); return isValid(parsed); } +/** + * This is a function for uploading metadata to the blob storage + * @param data an object containing metadata information about the round + * @param name the name of the round + * @returns url of the uploaded metadata + */ export async function uploadRoundMetadata({ data, name }: IUploadMetadataProps): Promise { // NOTICE! this is when you use vercel storage, if you're using another tool, please change this part. const blob = await put(name, JSON.stringify(data), { @@ -39,6 +53,10 @@ export async function uploadRoundMetadata({ data, name }: IUploadMetadataProps): return blob.url; } +/** + * This function collect round information from the console + * @returns an object containing information of the round + */ export async function collectMetadata(): Promise { const rl = readline.createInterface({ input: process.stdin, @@ -66,7 +84,7 @@ export async function collectMetadata(): Promise { const askStartTime = () => new Promise((resolve, reject) => { rl.question( - "When would you like to start this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ", + "When would you like to START this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in YOUR time) ", (answer) => { const valid = isValidDate(answer); @@ -82,65 +100,40 @@ export async function collectMetadata(): Promise { }); const askRegistrationEndTime = () => - new Promise((resolve, reject) => { - rl.question( - "When would you like to end the registration for applications? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ", - (answer) => { - const valid = isValidDate(answer); - - if (!valid) { - reject(new Error("Please answer in valid format.")); - } - - // eslint-disable-next-line no-console - console.log(`Your application registration end date for this round is: ${answer}`); - resolve(new Date(answer)); - }, - ); - }); + new Promise((resolve, reject) => { + rl.question("Please specify the duration of application registration in seconds: ", (answer) => { + const duration = Number(answer); - const askVotingStartTime = () => - new Promise((resolve, reject) => { - rl.question( - "When would you like to start the voting for this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ", - (answer) => { - const valid = isValidDate(answer); - - if (!valid) { - reject(new Error("Please answer in valid format.")); - } + if (!duration) { + reject(new Error("Please answer in number.")); + } - // eslint-disable-next-line no-console - console.log(`Your voting start date for this round is: ${answer}`); - resolve(new Date(answer)); - }, - ); + // eslint-disable-next-line no-console + console.log(`Your application registration duration for this round is: ${answer}`); + resolve(duration); + }); }); const askVotingEndTime = () => - new Promise((resolve, reject) => { - rl.question( - "When would you like to end the voting for this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ", - (answer) => { - const valid = isValidDate(answer); + new Promise((resolve, reject) => { + rl.question("Please specify the duration of voting in seconds: ", (answer) => { + const duration = Number(answer); - if (!valid) { - reject(new Error("Please answer in valid format.")); - } + if (!duration) { + reject(new Error("Please answer in number.")); + } - // eslint-disable-next-line no-console - console.log(`Your voting end date for this round is: ${answer}`); - resolve(new Date(answer)); - }, - ); + // eslint-disable-next-line no-console + console.log(`Your voting duration for this round is: ${answer}`); + resolve(duration); + }); }); const roundId = await askRoundId(); const description = await askDescription(); const startsAt = await askStartTime(); - const registrationEndsAt = await askRegistrationEndTime(); - const votingStartsAt = await askVotingStartTime(); - const votingEndsAt = await askVotingEndTime(); + const registrationEndsIn = await askRegistrationEndTime(); + const votingEndsIn = await askVotingEndTime(); rl.close(); @@ -151,15 +144,24 @@ export async function collectMetadata(): Promise { roundId, description, startsAt, - registrationEndsAt, - votingStartsAt, - votingEndsAt, + registrationEndsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000), + votingStartsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000), + votingEndsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000 + votingEndsIn * 1000), tallyFile: `${vercelStoragePrefix}/tally-${roundId}.json`, }; } async function main(): Promise { - const metadata = await collectMetadata(); + let metadata: RoundMetadata; + + // Try to read a metadata file first + if (fs.existsSync(METADATA_PATH)) { + metadata = JSON.parse(fs.readFileSync(METADATA_PATH, "utf-8")) as RoundMetadata; + } else { + // If there's no metadata file provided, collect metadata from console + metadata = await collectMetadata(); + } + const url = await uploadRoundMetadata({ data: metadata, name: `${metadata.roundId}.json` }); // eslint-disable-next-line no-console