From ac77abc20e82555ce7f5b2b0effdd3111742d1a6 Mon Sep 17 00:00:00 2001 From: jupiter Date: Sat, 12 Oct 2024 17:33:17 +0000 Subject: [PATCH 1/6] fix:sentry issues --- docker/named_pipe/nohup.out | 169 ++++++++++++++++++++++++++++++++++++ src/backend/dependencies.ts | 2 +- src/backend/server.ts | 3 - 3 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 docker/named_pipe/nohup.out diff --git a/docker/named_pipe/nohup.out b/docker/named_pipe/nohup.out new file mode 100644 index 0000000..c717cfa --- /dev/null +++ b/docker/named_pipe/nohup.out @@ -0,0 +1,169 @@ +1000 +Deleting... news.domains.mdgspace.org +rm: cannot remove '/etc/nginx/sites-available/news.domains.mdgspace.org.conf': No such file or directory +rm: cannot remove '/etc/nginx/sites-enabled/news.domains.mdgspace.org.conf': No such file or directory +Error response from daemon: No such container: news.domains.mdgspace.org +Error response from daemon: No such container: news.domains.mdgspace.org +Error response from daemon: No such image: news.domains.mdgspace.org:latest +1000 +Creating subdomain news.domains.mdgspace.org which redirects to https://news.ycombinator.com/ +Generating url.conf +url: https://news.ycombinator.com/ +1000 +Creating subdomain github.domains.mdgspace.org which redirects to https://github.com/ +Generating url.conf +url: https://github.com/ +1000 +Deleting... github.domains.mdgspace.org +Error response from daemon: No such container: github.domains.mdgspace.org +Error response from daemon: No such container: github.domains.mdgspace.org +Error response from daemon: No such image: github.domains.mdgspace.org:latest +1000 +Deleting... nik-55.domains.mdgspace.org +Error response from daemon: No such container: nik-55.domains.mdgspace.org +Error response from daemon: No such container: nik-55.domains.mdgspace.org +Error response from daemon: No such image: nik-55.domains.mdgspace.org:latest +1000 +Creating subdomain kituuu.mdgspace.org.domains.mdgspace.org which redirects to https://your-personal-gallery.vercel.app/ +Generating url.conf +url: https://your-personal-gallery.vercel.app/ +1000 +Deleting... kituuu.mdgspace.org.domains.mdgspace.org +Error response from daemon: No such container: kituuu.mdgspace.org.domains.mdgspace.org +Error response from daemon: No such container: kituuu.mdgspace.org.domains.mdgspace.org +Error response from daemon: No such image: kituuu.mdgspace.org.domains.mdgspace.org:latest +1000 +Creating subdomain kituuu.domains.mdgspace.org which redirects to https://your-personal-gallery.vercel.app/ +Generating url.conf +url: https://your-personal-gallery.vercel.app/ +1000 +Deleting... kituuu.domains.mdgspace.org +Error response from daemon: No such container: kituuu.domains.mdgspace.org +Error response from daemon: No such container: kituuu.domains.mdgspace.org +Error response from daemon: No such image: kituuu.domains.mdgspace.org:latest +1000 +Creating subdomain google.domains.mdgspace.org which redirects to https://www.google.com/ +Generating url.conf +url: https://www.google.com/ +1000 +Creating subdomain nano-test.domains.mdgspace.org which redirects to https://nanonish.github.io/ +Generating url.conf +url: https://nanonish.github.io/ +1000 +Creating subdomain wiki.domains.mdgspace.org which redirects to https://www.wikipedia.org/ +Generating url.conf +url: https://www.wikipedia.org/ +1000 +Creating subdomain wiki.domains.mdgspace.org which redirects to https://www.wikipedia.org/ +Generating url.conf +url: https://www.wikipedia.org/ +1000 +Deleting... todo.domains.mdgspace.org +Error response from daemon: No such container: todo.domains.mdgspace.org +Error response from daemon: No such container: todo.domains.mdgspace.org +Error response from daemon: No such image: todo.domains.mdgspace.org:latest +1000 +Deleting... hey.domains.mdgspace.org +Error response from daemon: No such container: hey.domains.mdgspace.org +Error response from daemon: No such container: hey.domains.mdgspace.org +Error response from daemon: No such image: hey.domains.mdgspace.org:latest +Available ports: 8067 +Creating subdomain hey.domains.mdgspace.org +Cloning into 'hey.domains.mdgspace.org'... +#0 building with "default" instance using docker driver + +#1 [internal] load build definition from Dockerfile +#1 transferring dockerfile: 225B done +#1 DONE 0.0s + +#2 [internal] load metadata for docker.io/library/python:latest +#2 DONE 2.2s + +#3 [internal] load .dockerignore +#3 transferring context: 2B done +#3 DONE 0.0s + +#4 [1/5] FROM docker.io/library/python:latest@sha256:7859853e7607927aa1d1b1a5a2f9e580ac90c2b66feeb1b77da97fed03b1ccbe +#4 resolve docker.io/library/python:latest@sha256:7859853e7607927aa1d1b1a5a2f9e580ac90c2b66feeb1b77da97fed03b1ccbe 0.0s done +#4 sha256:2e66a70da0bec13fb3d492fcdef60fd8a5ef0a1a65c4e8a4909e26742852f0f2 0B / 64.15MB 0.1s +#4 sha256:7859853e7607927aa1d1b1a5a2f9e580ac90c2b66feeb1b77da97fed03b1ccbe 9.72kB / 9.72kB done +#4 sha256:ea2ebd905ab246ece277be25520ca0cfe82758b3d2b369e2fd69b374c1d6c7fa 5.86kB / 5.86kB done +#4 sha256:2e6afa3f266c11e8960349e7866203a9df478a50362bb5488c45fe39d99b2707 0B / 24.05MB 0.1s +#4 sha256:f6b1cea9aa1bba7fdf508248d1ab303a4cb11274997f39c9a0d6646f221379da 2.32kB / 2.32kB done +#4 sha256:8cd46d290033f265db57fd808ac81c444ec5a5b3f189c3d6d85043b647336913 0B / 49.56MB 0.1s +#4 CANCELED + +#5 [internal] load build context +#5 transferring context: 16.77MB 0.2s done +#5 DONE 0.3s + +#6 [2/5] WORKDIR /app +#6 CACHED + +#7 [3/5] COPY requirements.txt . +#7 ERROR: failed to calculate checksum of ref 44b139ae-3131-4796-9538-e5f44b9c0408::pe7dzgi8kpgbwg9fr8gsw1js8: "/requirements.txt": not found +------ + > [3/5] COPY requirements.txt .: +------ +Dockerfile:3 +-------------------- + 1 | FROM python:latest + 2 | WORKDIR /app + 3 | >>> COPY requirements.txt . + 4 | RUN pip install --no-cache-dir -r requirements.txt + 5 | COPY . . +-------------------- +ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref 44b139ae-3131-4796-9538-e5f44b9c0408::pe7dzgi8kpgbwg9fr8gsw1js8: "/requirements.txt": not found +Unable to find image 'hey.domains.mdgspace.org:latest' locally +docker: Error response from daemon: pull access denied for hey.domains.mdgspace.org, repository does not exist or may require 'docker login': denied: requested access to the resource is denied. +See 'docker run --help'. +Available ports: 8067 +Creating subdomain hy.domains.mdgspace.org +Cloning into 'hy.domains.mdgspace.org'... +#0 building with "default" instance using docker driver + +#1 [internal] load build definition from Dockerfile +#1 transferring dockerfile: 225B done +#1 DONE 0.0s + +#2 [internal] load metadata for docker.io/library/python:latest +#2 DONE 0.7s + +#3 [internal] load .dockerignore +#3 transferring context: 2B done +#3 DONE 0.0s + +#4 [1/5] FROM docker.io/library/python:latest@sha256:7859853e7607927aa1d1b1a5a2f9e580ac90c2b66feeb1b77da97fed03b1ccbe +#4 resolve docker.io/library/python:latest@sha256:7859853e7607927aa1d1b1a5a2f9e580ac90c2b66feeb1b77da97fed03b1ccbe 0.0s done +#4 sha256:f6b1cea9aa1bba7fdf508248d1ab303a4cb11274997f39c9a0d6646f221379da 2.32kB / 2.32kB done +#4 sha256:ea2ebd905ab246ece277be25520ca0cfe82758b3d2b369e2fd69b374c1d6c7fa 5.86kB / 5.86kB done +#4 sha256:2e6afa3f266c11e8960349e7866203a9df478a50362bb5488c45fe39d99b2707 0B / 24.05MB 0.1s +#4 sha256:7859853e7607927aa1d1b1a5a2f9e580ac90c2b66feeb1b77da97fed03b1ccbe 9.72kB / 9.72kB done +#4 sha256:8cd46d290033f265db57fd808ac81c444ec5a5b3f189c3d6d85043b647336913 0B / 49.56MB 0.1s +#4 sha256:2e66a70da0bec13fb3d492fcdef60fd8a5ef0a1a65c4e8a4909e26742852f0f2 0B / 64.15MB 0.1s +#4 ... + +#5 [internal] load build context +#5 transferring context: 16.77MB 0.1s done +#5 DONE 0.2s + +#6 [2/5] WORKDIR /app +#6 CACHED + +#7 [3/5] COPY requirements.txt . +#7 ERROR: failed to calculate checksum of ref 44b139ae-3131-4796-9538-e5f44b9c0408::w1g4h9pt6iyidbf3u91xq1fq9: "/requirements.txt": not found +------ + > [3/5] COPY requirements.txt .: +------ +Dockerfile:3 +-------------------- + 1 | FROM python:latest + 2 | WORKDIR /app + 3 | >>> COPY requirements.txt . + 4 | RUN pip install --no-cache-dir -r requirements.txt + 5 | COPY . . +-------------------- +ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref 44b139ae-3131-4796-9538-e5f44b9c0408::w1g4h9pt6iyidbf3u91xq1fq9: "/requirements.txt": not found +Unable to find image 'hy.domains.mdgspace.org:latest' locally +docker: Error response from daemon: pull access denied for hy.domains.mdgspace.org, repository does not exist or may require 'docker login': denied: requested access to the resource is denied. +See 'docker run --help'. diff --git a/src/backend/dependencies.ts b/src/backend/dependencies.ts index 3bf13fa..9a51434 100644 --- a/src/backend/dependencies.ts +++ b/src/backend/dependencies.ts @@ -8,7 +8,7 @@ import { import { Session } from "https://deno.land/x/oak_sessions@v4.1.9/mod.ts"; import { create, verify } from "https://deno.land/x/djwt@v2.9.1/mod.ts"; import { exec } from "https://deno.land/x/exec@0.0.5/mod.ts"; -import * as Sentry from "npm:@sentry/node"; +import * as Sentry from 'https://deno.land/x/sentry/index.mjs'; import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts"; export { diff --git a/src/backend/server.ts b/src/backend/server.ts index ccbd303..1805ba2 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -28,9 +28,6 @@ const frontend: string = Deno.env.get("FRONTEND")!; Sentry.init({ dsn: dsn, - integrations: [ - new Sentry.Integrations.Http({ tracing: true }), - ], debug: true, tracesSampleRate: 1.0, }); From 8697c9f42afaf94c99b62d1e55d1f534f04a53bb Mon Sep 17 00:00:00 2001 From: jupiter Date: Sun, 27 Oct 2024 07:46:05 +0000 Subject: [PATCH 2/6] feat : adds cli of frorntend operations --- docker/named_pipe/nohup.out | 27 +++++ src/cli/index.js | 225 ++++++++++++++++++++++++++++++++++++ src/cli/package.json | 26 +++++ 3 files changed, 278 insertions(+) create mode 100644 src/cli/index.js create mode 100644 src/cli/package.json diff --git a/docker/named_pipe/nohup.out b/docker/named_pipe/nohup.out index c717cfa..64bc166 100644 --- a/docker/named_pipe/nohup.out +++ b/docker/named_pipe/nohup.out @@ -167,3 +167,30 @@ ERROR: failed to solve: failed to compute cache key: failed to calculate checksu Unable to find image 'hy.domains.mdgspace.org:latest' locally docker: Error response from daemon: pull access denied for hy.domains.mdgspace.org, repository does not exist or may require 'docker login': denied: requested access to the resource is denied. See 'docker run --help'. +1000 +Creating subdomain alpha.domains.mdgspace.org which redirects to https://github.com +Generating url.conf +url: https://github.com +1000 +Creating subdomain raj.domains.mdgspace.org which redirects to https://github.com/raj210809 +Generating url.conf +url: https://github.com/raj210809 +1000 +Deleting... tola.domains.mdgspace.org +Error response from daemon: No such container: tola.domains.mdgspace.org +Error response from daemon: No such container: tola.domains.mdgspace.org +Error response from daemon: No such image: tola.domains.mdgspace.org:latest +1000 +Creating subdomain toka.domains.mdgspace.org which redirects to https://github.com/amsorrytola +Generating url.conf +url: https://github.com/amsorrytola +1000 +Deleting... toka.domains.mdgspace.org +Error response from daemon: No such container: toka.domains.mdgspace.org +Error response from daemon: No such container: toka.domains.mdgspace.org +Error response from daemon: No such image: toka.domains.mdgspace.org:latest +1000 +Deleting... tola.domains.mdgspace.org +Error response from daemon: No such container: tola.domains.mdgspace.org +Error response from daemon: No such container: tola.domains.mdgspace.org +Error response from daemon: No such image: tola.domains.mdgspace.org:latest diff --git a/src/cli/index.js b/src/cli/index.js new file mode 100644 index 0000000..f8d34db --- /dev/null +++ b/src/cli/index.js @@ -0,0 +1,225 @@ +#!/usr/bin/env node +import inquirer from 'inquirer'; +import fetch from 'node-fetch'; +import dotenv from 'dotenv'; +import axios from 'axios'; +import chalk from 'chalk'; +import table from 'cli-table3'; +import { Command } from 'commander'; + +dotenv.config(); // Load environment variables + +const program = new Command(); + +let userApiKey = ''; +let user = ''; +const provider = 'github'; +const domain = 'domains.mdgspace.org'; + + +program + .name('domainforge') + .description('A CLI tool to manage domains') + .option('--token ', 'Provide user API token') + .action(async (options) => { + if (options.token) { + userApiKey = options.token; + user = await verifyApiKey(userApiKey); + if (user === 'not verified') { + console.error('❌ Invalid API key! Please try again.'); + process.exit(1); + } else { + console.log('✅ API key verified!'); + } + await showOptions(); // Show options if the API key is valid + } else { + console.error('❌ Please provide a valid token using --token.'); + process.exit(1); + } + }); + +program.parse(process.argv); // Parse the arguments passed + +async function verifyApiKey(apiKey) { + try { + const response = await axios.post(`${process.env.BACKEND}/auth/jwt`, { + jwt_token: apiKey, + provider: 'github', + }); + return response.data; + } catch (error) { + console.error(chalk.red('Error verifying API key:'), error.message); + return 'not verified'; + } +} + +async function selectResourceType() { + const { resourceType } = await inquirer.prompt([ + { + type: 'list', + name: 'resourceType', + message: 'Select the resource type:', + choices: ['URL', 'PORT', 'GITHUB'], + }, + ]); + return resourceType; +} + +function secureInput(input) { + const blockedPhrases = [ + ';', '&', '|', '&&', '||', '>', '>>', '<', '<<', '$', '(', ')', '{', '}', + '`', '"', '!', '~', '*', '?', '[', ']', '#', '%', '+', 'curl', 'wget', 'rm', + 'tail', 'cat', 'grep', 'nc', 'xxd', 'apt', 'echo', 'pwd', 'ping', 'more', + 'tail', 'usermod', 'bash', 'sudo', ',', + ]; + return !blockedPhrases.some((phrase) => input.includes(phrase)); +} + +async function promptUser(question) { + const { userInput } = await inquirer.prompt({ + type: 'input', + name: 'userInput', + message: question, + }); + + if (!secureInput(userInput)) { + console.error('Invalid input detected! Please try again.'); + return promptUser(question); // Retry if input is invalid + } + return userInput; +} + +async function createDomain() { + let subdomain = await promptUser('Enter subdomain (subdomain.domains.mdgspace.org):'); + let resourceType = await selectResourceType(); + let resource = await promptUser('Enter resource:'); + let envContent = ''; + let staticContent = ''; + let dockerfilePresent = false; + let port = ''; + let stack = ''; + let buildCmds = ''; + + if (resourceType === 'GITHUB') { + envContent = await promptUser('Enter environment content:'); + staticContent = await promptUser('Enter static content:'); + const { dockerConfirm } = await inquirer.prompt({ + type: 'confirm', + name: 'dockerfilePresent', + message: 'Is Dockerfile present?', + }); + dockerfilePresent = dockerConfirm; + port = await promptUser('Enter port:'); + stack = await promptUser('Enter stack (NodeJS/Python):'); + buildCmds = await promptUser('Enter build commands:'); + } + + const payload = { + subdomain: `${subdomain}.${domain}`, + resource_type: resourceType, + resource, + env_content: envContent, + static_content: staticContent, + dockerfile_present: dockerfilePresent, + port, + build_cmds: buildCmds, + stack, + author: user, + date: new Date().toLocaleDateString(), + token: userApiKey, + provider, + }; + + try { + const response = await axios.post(`${process.env.BACKEND}/map`, payload); + if (response.data.status === 'success') { + console.log(`✅ Domain '${subdomain}.${domain}' created successfully!`); + } else { + console.log('❌ Domain creation failed!'); + } + } catch (error) { + console.error(chalk.red('Error creating domain:'), error.message); + } +} + +async function deleteDomain() { + const domainName = await promptUser('Enter domain name to delete:'); + try { + const response = await axios.post(`${process.env.BACKEND}/mapdel`, { + author: user, + token: userApiKey, + provider, + subdomain: domainName, + }); + if (response.data.deletedCount === 1) { + console.log(`✅ Domain '${domainName}' deleted successfully!`); + } else { + console.log('❌ Domain deletion failed!'); + } + } catch (error) { + console.error(chalk.red('Error deleting domain:'), error.message); + } +} + +async function listDomains() { + try { + const response = await axios.get( + `${process.env.BACKEND}/map?user=${user}&token=${userApiKey}&provider=${provider}` + ); + const data = response.data; + + if (!data.length) { + console.log(chalk.yellow('No domains found!')); + return; + } + + const domainTable = new table({ + head: ['Date', 'Sub-Domain', 'Resource Type', 'Resource'], + colWidths: [15, 30, 15, 40], // Adjust column widths as needed + }); + + data.forEach((domain) => { + domainTable.push([ + chalk.green(domain.date), + chalk.blue(domain.subdomain), + chalk.cyan(domain.resource_type), + chalk.magenta(domain.resource), + ]); + }); + + console.log(domainTable.toString()); // Display the table + } catch (error) { + console.error(chalk.red('Error fetching domains:'), error.message); + } +} + +async function showOptions() { + const { option } = await inquirer.prompt({ + type: 'list', + name: 'option', + message: 'What would you like to do?', + choices: [ + { name: 'List all domains', value: 'list' }, + { name: 'Create a new domain', value: 'create' }, + { name: 'Delete a domain', value: 'delete' }, + { name: 'Exit', value: 'exit' }, + ], + }); + + switch (option) { + case 'create': + await createDomain(); + break; + case 'delete': + await deleteDomain(); + break; + case 'list': + await listDomains(); + break; + case 'exit': + console.log('Goodbye!'); + process.exit(0); + } + + await showOptions(); +} diff --git a/src/cli/package.json b/src/cli/package.json new file mode 100644 index 0000000..de1a2d6 --- /dev/null +++ b/src/cli/package.json @@ -0,0 +1,26 @@ +{ + "name": "cli", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "bin": { + "domainforge": "./index.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "axios": "^1.7.7", + "chalk": "^5.3.0", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "dotenv": "^16.4.5", + "inquirer": "^12.0.1", + "node-fetch": "^3.3.2" + } + } + \ No newline at end of file From 8eab0aa56a1f55d8f4bf43736ade9e00f60305bd Mon Sep 17 00:00:00 2001 From: jupiter Date: Mon, 28 Oct 2024 09:31:53 +0000 Subject: [PATCH 3/6] rfac : improved fs and migrate to typescript --- src/cli/features/authUser.ts | 15 +++ src/cli/features/createDomain.ts | 95 +++++++++++++ src/cli/features/deleteDomain.ts | 23 ++++ src/cli/features/listDomain.ts | 45 +++++++ src/cli/index.js | 225 ------------------------------- src/cli/index.ts | 77 +++++++++++ src/cli/package.json | 56 ++++---- src/cli/tsconfig.json | 13 ++ src/cli/utils/promptTaker.ts | 25 ++++ 9 files changed, 325 insertions(+), 249 deletions(-) create mode 100644 src/cli/features/authUser.ts create mode 100644 src/cli/features/createDomain.ts create mode 100644 src/cli/features/deleteDomain.ts create mode 100644 src/cli/features/listDomain.ts delete mode 100644 src/cli/index.js create mode 100644 src/cli/index.ts create mode 100644 src/cli/tsconfig.json create mode 100644 src/cli/utils/promptTaker.ts diff --git a/src/cli/features/authUser.ts b/src/cli/features/authUser.ts new file mode 100644 index 0000000..eb1cc4f --- /dev/null +++ b/src/cli/features/authUser.ts @@ -0,0 +1,15 @@ +import axios from 'axios'; +import chalk from 'chalk'; + +export async function verifyApiKey(apiKey: string, provider: string , backendUrl : string) { + try { + const response = await axios.post(`${backendUrl}/auth/jwt`, { + jwt_token: apiKey, + provider: provider, + }); + return response.data; + } catch (error) { + console.error(chalk.red('Error verifying API key:'), error); + return 'not verified'; + } + } \ No newline at end of file diff --git a/src/cli/features/createDomain.ts b/src/cli/features/createDomain.ts new file mode 100644 index 0000000..cc7bd40 --- /dev/null +++ b/src/cli/features/createDomain.ts @@ -0,0 +1,95 @@ +import axios from "axios" +import chalk from 'chalk' +import inquirer from 'inquirer' +import { promptUser } from '../utils/promptTaker.js'; + +let domain = 'domains.mdgspace.org'; + +async function selectResourceType() { + const { resourceType } = await inquirer.prompt([ + { + type: 'list', + name: 'resourceType', + message: 'Select the resource type:', + choices: ['URL', 'PORT', 'GITHUB'], + }, + ]); + return resourceType; + } + async function selectDockerPresent() { + const { resourceType } = await inquirer.prompt([ + { + type: 'list', + name: 'resourceType', + message: 'Is Dockerfile present?', + choices: ['No', 'Yes'], + }, + ]); + return resourceType; + } + + + async function selectStack() { + const { Stack } = await inquirer.prompt([ + { + type: 'list', + name: 'Stack', // Must match the key you destructure + message: 'Select the tech stack:', + choices: ['Python', 'NodeJS'], + }, + ]); + return Stack; + } + +export async function createDomain(userApiKey : string, user : string , provider : string , backendUrl : string) { + let subdomain : string = ''; + let resourceType : string = ''; + let resource : string= ''; + let envContent : string = ''; + let staticContent : string = ''; + let dockerfilePresent : string = ""; + let port : string = ''; + let stack : string = ''; + let buildCmds : string = ''; + + subdomain = await promptUser('Enter subdomain (subdomain.domains.mdgspace.org):'); + resourceType = await selectResourceType(); + resource = await promptUser('Enter resource:'); + + if (resourceType === 'GitHub') { + envContent = await promptUser('Enter environment content:'); + staticContent = await promptUser('Enter static content:'); + dockerfilePresent =await selectDockerPresent(); + port = await promptUser('Enter port:'); + stack = await promptUser('Enter stack (NodeJS/Python):'); + buildCmds = await promptUser('Enter build commands:'); + } + + const payload = { + subdomain: `${subdomain}.${domain}`, + resource_type: resourceType, + resource, + env_content: envContent, + static_content: staticContent, + dockerfile_present: dockerfilePresent, + port, + build_cmds: buildCmds, + stack, + author: user, + date: new Date().toLocaleDateString(), + token: userApiKey, + provider, + }; + try { + const response = await axios.post(`${backendUrl}/map`, payload); + console.log(response.data); + if (response.data.status === 'success') { + console.log(`✅ Domain '${subdomain}.${domain}' created successfully!`); + } else { + console.log('❌ Domain creation failed!'); + console.log("Either the domain exist or the domain is not created"); + } + } catch (error) { + console.error(chalk.red('Error creating domain:'), error); + } +} \ No newline at end of file diff --git a/src/cli/features/deleteDomain.ts b/src/cli/features/deleteDomain.ts new file mode 100644 index 0000000..35cf717 --- /dev/null +++ b/src/cli/features/deleteDomain.ts @@ -0,0 +1,23 @@ +const axios = require('axios'); +const chalk = require('chalk'); +import { promptUser } from '../utils/promptTaker.js'; + +export async function deleteDomain(userApiKey : string, user : string, provider : string , backendUrl : string) { + const domain = await promptUser('Enter domain name to delete:'); + try { + const response = await axios.post(`${backendUrl}/mapdel`, { + author: user, + token: userApiKey, + provider, + subdomain: domain, + }) + if (response.data.deletedCount === 1) { + console.log(`✅ Domain '${domain}' deleted successfully!`); + } + else { + console.log('❌ Domain deletion failed!'); + } + } catch (error) { + console.error(chalk.red('Error deleting domain:'), error); + } + } \ No newline at end of file diff --git a/src/cli/features/listDomain.ts b/src/cli/features/listDomain.ts new file mode 100644 index 0000000..042aa00 --- /dev/null +++ b/src/cli/features/listDomain.ts @@ -0,0 +1,45 @@ +import axios from 'axios'; +import chalk from 'chalk'; +import table from 'cli-table3'; + +interface domain { + _id: string, + date: string, + subdomain: string, + resource_type: string, + resource: string, + author: string, +} + +export async function listDomains(user : string , userApiKey : string, provider : string , backendUrl : string) { + try { + const response = await axios.get( + `${backendUrl}/map?user=${user}&token=${userApiKey}&provider=${provider}` + ); + const data = response.data; + + if (!data.length) { + console.log(chalk.yellow('No domains found!')); + return; + } + + const domainTable = new table({ + head: ['Date', 'Sub-Domain', 'Resource Type', 'Resource'], + colWidths: [15, 30, 15, 40], // Adjust column widths as needed + }); + + data.forEach((domain : domain) => { + domainTable.push([ + chalk.green(domain.date), + chalk.blue(domain.subdomain), + chalk.cyan(domain.resource_type), + chalk.magenta(domain.resource), + ]); + }); + + console.log(domainTable.toString()); // Display the table + + } catch (error) { + console.error(chalk.red('Error fetching domains:'), error); + } + } \ No newline at end of file diff --git a/src/cli/index.js b/src/cli/index.js deleted file mode 100644 index f8d34db..0000000 --- a/src/cli/index.js +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env node -import inquirer from 'inquirer'; -import fetch from 'node-fetch'; -import dotenv from 'dotenv'; -import axios from 'axios'; -import chalk from 'chalk'; -import table from 'cli-table3'; -import { Command } from 'commander'; - -dotenv.config(); // Load environment variables - -const program = new Command(); - -let userApiKey = ''; -let user = ''; -const provider = 'github'; -const domain = 'domains.mdgspace.org'; - - -program - .name('domainforge') - .description('A CLI tool to manage domains') - .option('--token ', 'Provide user API token') - .action(async (options) => { - if (options.token) { - userApiKey = options.token; - user = await verifyApiKey(userApiKey); - if (user === 'not verified') { - console.error('❌ Invalid API key! Please try again.'); - process.exit(1); - } else { - console.log('✅ API key verified!'); - } - await showOptions(); // Show options if the API key is valid - } else { - console.error('❌ Please provide a valid token using --token.'); - process.exit(1); - } - }); - -program.parse(process.argv); // Parse the arguments passed - -async function verifyApiKey(apiKey) { - try { - const response = await axios.post(`${process.env.BACKEND}/auth/jwt`, { - jwt_token: apiKey, - provider: 'github', - }); - return response.data; - } catch (error) { - console.error(chalk.red('Error verifying API key:'), error.message); - return 'not verified'; - } -} - -async function selectResourceType() { - const { resourceType } = await inquirer.prompt([ - { - type: 'list', - name: 'resourceType', - message: 'Select the resource type:', - choices: ['URL', 'PORT', 'GITHUB'], - }, - ]); - return resourceType; -} - -function secureInput(input) { - const blockedPhrases = [ - ';', '&', '|', '&&', '||', '>', '>>', '<', '<<', '$', '(', ')', '{', '}', - '`', '"', '!', '~', '*', '?', '[', ']', '#', '%', '+', 'curl', 'wget', 'rm', - 'tail', 'cat', 'grep', 'nc', 'xxd', 'apt', 'echo', 'pwd', 'ping', 'more', - 'tail', 'usermod', 'bash', 'sudo', ',', - ]; - return !blockedPhrases.some((phrase) => input.includes(phrase)); -} - -async function promptUser(question) { - const { userInput } = await inquirer.prompt({ - type: 'input', - name: 'userInput', - message: question, - }); - - if (!secureInput(userInput)) { - console.error('Invalid input detected! Please try again.'); - return promptUser(question); // Retry if input is invalid - } - return userInput; -} - -async function createDomain() { - let subdomain = await promptUser('Enter subdomain (subdomain.domains.mdgspace.org):'); - let resourceType = await selectResourceType(); - let resource = await promptUser('Enter resource:'); - let envContent = ''; - let staticContent = ''; - let dockerfilePresent = false; - let port = ''; - let stack = ''; - let buildCmds = ''; - - if (resourceType === 'GITHUB') { - envContent = await promptUser('Enter environment content:'); - staticContent = await promptUser('Enter static content:'); - const { dockerConfirm } = await inquirer.prompt({ - type: 'confirm', - name: 'dockerfilePresent', - message: 'Is Dockerfile present?', - }); - dockerfilePresent = dockerConfirm; - port = await promptUser('Enter port:'); - stack = await promptUser('Enter stack (NodeJS/Python):'); - buildCmds = await promptUser('Enter build commands:'); - } - - const payload = { - subdomain: `${subdomain}.${domain}`, - resource_type: resourceType, - resource, - env_content: envContent, - static_content: staticContent, - dockerfile_present: dockerfilePresent, - port, - build_cmds: buildCmds, - stack, - author: user, - date: new Date().toLocaleDateString(), - token: userApiKey, - provider, - }; - - try { - const response = await axios.post(`${process.env.BACKEND}/map`, payload); - if (response.data.status === 'success') { - console.log(`✅ Domain '${subdomain}.${domain}' created successfully!`); - } else { - console.log('❌ Domain creation failed!'); - } - } catch (error) { - console.error(chalk.red('Error creating domain:'), error.message); - } -} - -async function deleteDomain() { - const domainName = await promptUser('Enter domain name to delete:'); - try { - const response = await axios.post(`${process.env.BACKEND}/mapdel`, { - author: user, - token: userApiKey, - provider, - subdomain: domainName, - }); - if (response.data.deletedCount === 1) { - console.log(`✅ Domain '${domainName}' deleted successfully!`); - } else { - console.log('❌ Domain deletion failed!'); - } - } catch (error) { - console.error(chalk.red('Error deleting domain:'), error.message); - } -} - -async function listDomains() { - try { - const response = await axios.get( - `${process.env.BACKEND}/map?user=${user}&token=${userApiKey}&provider=${provider}` - ); - const data = response.data; - - if (!data.length) { - console.log(chalk.yellow('No domains found!')); - return; - } - - const domainTable = new table({ - head: ['Date', 'Sub-Domain', 'Resource Type', 'Resource'], - colWidths: [15, 30, 15, 40], // Adjust column widths as needed - }); - - data.forEach((domain) => { - domainTable.push([ - chalk.green(domain.date), - chalk.blue(domain.subdomain), - chalk.cyan(domain.resource_type), - chalk.magenta(domain.resource), - ]); - }); - - console.log(domainTable.toString()); // Display the table - } catch (error) { - console.error(chalk.red('Error fetching domains:'), error.message); - } -} - -async function showOptions() { - const { option } = await inquirer.prompt({ - type: 'list', - name: 'option', - message: 'What would you like to do?', - choices: [ - { name: 'List all domains', value: 'list' }, - { name: 'Create a new domain', value: 'create' }, - { name: 'Delete a domain', value: 'delete' }, - { name: 'Exit', value: 'exit' }, - ], - }); - - switch (option) { - case 'create': - await createDomain(); - break; - case 'delete': - await deleteDomain(); - break; - case 'list': - await listDomains(); - break; - case 'exit': - console.log('Goodbye!'); - process.exit(0); - } - - await showOptions(); -} diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..f53dd74 --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,77 @@ +#!/usr/bin/env node +import inquirer from 'inquirer'; +import dotenv from 'dotenv'; +import axios from 'axios'; +import chalk from 'chalk'; +import { Command } from 'commander'; +import { listDomains } from './features/listDomain.js'; +import { createDomain } from './features/createDomain.js'; +import { deleteDomain } from './features/deleteDomain.js'; +import { verifyApiKey } from './features/authUser.js'; + +dotenv.config(); + +const program = new Command(); + +let userApiKey = ''; +let user = ''; +let backendUrl = ''; +const provider = 'github'; + + +program + .name('domainforge') + .description('A CLI tool to manage domains') + .option('--backend ', 'Set the backend URL') + .option('--token ', 'Provide user API token') + .helpOption('-h, --help', 'Display help for command') + .action(async (options) => { + if (options.token && options.backend) { + userApiKey = await options.token; + backendUrl = await options.backend; + user = await verifyApiKey(userApiKey , provider , backendUrl); + console.log(user); + if (user === 'not verified') { + console.error('❌ Invalid API key! Please try again.'); + process.exit(1); + } else { + console.log('✅ API key verified!'); + } + await showOptions(); // Show options if the API key is valid + } else { + console.error('❌ Please provide a valid token using --token.'); + process.exit(1); + } + }); + +program.parse(process.argv); + +async function showOptions() { + const { option } = await inquirer.prompt({ + type: 'list', + name: 'option', + message: 'What would you like to do?', + choices: [ + { name: 'List all domains', value: 'list' }, + { name: 'Create a new domain', value: 'create' }, + { name: 'Delete a domain', value: 'delete' }, + { name: 'Exit', value: 'exit' }, + ], + }); + + switch (option) { + case 'create': + await createDomain(userApiKey, user, provider , backendUrl); + break; + case 'delete': + await deleteDomain(userApiKey, user, provider , backendUrl); + break; + case 'list': + await listDomains(user, userApiKey, provider , backendUrl); + break; + case 'exit': + console.log('Goodbye!'); + process.exit(0); + } + await showOptions(); +} diff --git a/src/cli/package.json b/src/cli/package.json index de1a2d6..39f9518 100644 --- a/src/cli/package.json +++ b/src/cli/package.json @@ -1,26 +1,34 @@ { - "name": "cli", - "version": "1.0.0", - "main": "index.js", - "type": "module", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "bin": { - "domainforge": "./index.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "description": "", - "dependencies": { - "axios": "^1.7.7", - "chalk": "^5.3.0", - "cli-table3": "^0.6.5", - "commander": "^12.1.0", - "dotenv": "^16.4.5", - "inquirer": "^12.0.1", - "node-fetch": "^3.3.2" - } + "name": "domainforge-cli", + "version": "1.0.2", + "main": "dist/index.js", + "type": "commonjs", + "scripts": { + "build": "tsc", + "start": "node dist/index.js" + }, + "bin": { + "domainforge": "./dist/index.js" + }, + "keywords": [ + "CLI", + "DomainForge", + "domains" + ], + "author": ".mdgspace", + "license": "ISC", + "description": "It's a CLI tool to host or manage subdomains associated by the domainforge API", + "dependencies": { + "axios": "^1.7.7", + "chalk": "^4.1.2", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "dotenv": "^16.4.5", + "inquirer": "^12.0.1", + "node-fetch": "^3.3.2" + }, + "devDependencies": { + "@types/node": "^22.8.1", + "typescript": "^5.6.3" } - \ No newline at end of file +} diff --git a/src/cli/tsconfig.json b/src/cli/tsconfig.json new file mode 100644 index 0000000..a8bbb8d --- /dev/null +++ b/src/cli/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["./**/*.ts"], // Includes all .ts files in the project. + "exclude": ["./node_modules", "./dist"] // Exclude unnecessary folders. + } \ No newline at end of file diff --git a/src/cli/utils/promptTaker.ts b/src/cli/utils/promptTaker.ts new file mode 100644 index 0000000..cb31d9b --- /dev/null +++ b/src/cli/utils/promptTaker.ts @@ -0,0 +1,25 @@ +import inquirer from 'inquirer'; + +function secureInput(input : string) { + const blockedPhrases = [ + ';', '&', '|', '&&', '||', '>', '>>', '<', '<<', '$', '(', ')', '{', '}', + '`', '"', '!', '~', '*', '?', '[', ']', '#', '%', '+', 'curl', 'wget', 'rm', + 'tail', 'cat', 'grep', 'nc', 'xxd', 'apt', 'echo', 'pwd', 'ping', 'more', + 'tail', 'usermod', 'bash', 'sudo', ',', + ]; + return !blockedPhrases.some((phrase) => input.includes(phrase)); + } + + export async function promptUser(question : string) { + const { userInput } = await inquirer.prompt({ + type: 'input', + name: 'userInput', + message: question, + }); + + if (!secureInput(userInput)) { + console.error('Invalid input detected! Please try again.'); + return promptUser(question); // Retry if input is invalid + } + return userInput; + } \ No newline at end of file From 57d917a3d5f1c04718d2be8fd3df7defb9903416 Mon Sep 17 00:00:00 2001 From: jupiter Date: Mon, 28 Oct 2024 22:22:51 +0000 Subject: [PATCH 4/6] feat: adds ApiKey functionality in both frontend and backend --- src/backend/auth/github.ts | 5 +- src/backend/utils/apiKeyGen.ts | 26 ++++++++++ src/backend/utils/jwt.ts | 24 +++++++++- src/frontend/src/components/ApiKeyModal.vue | 53 +++++++++++++++++++++ src/frontend/src/components/Home.vue | 43 ++++++++++------- src/frontend/src/utils/authorize.ts | 8 ++-- 6 files changed, 137 insertions(+), 22 deletions(-) create mode 100644 src/backend/utils/apiKeyGen.ts create mode 100644 src/frontend/src/components/ApiKeyModal.vue diff --git a/src/backend/auth/github.ts b/src/backend/auth/github.ts index 6044a97..6016eac 100644 --- a/src/backend/auth/github.ts +++ b/src/backend/auth/github.ts @@ -1,6 +1,7 @@ import { Context, Sentry } from "../dependencies.ts"; import { checkUser } from "../db.ts"; import { checkJWT, createJWT } from "../utils/jwt.ts"; +import { generateApiKey } from "../utils/apiKeyGen.ts"; async function githubAuth(ctx: Context, id: string, secret: string) { await authenticateAndCreateJWT(ctx, id, secret, "github"); @@ -94,7 +95,9 @@ async function handleJwtAuthentication(ctx: Context) { } const jwt_token = document.jwt_token; const provider = document.provider; - ctx.response.body = await checkJWT(provider, jwt_token); + const user = await checkJWT(provider, jwt_token); + const apiKey = generateApiKey(user) + ctx.response.body = {user , apiKey}; } export { githubAuth, gitlabAuth, handleJwtAuthentication }; diff --git a/src/backend/utils/apiKeyGen.ts b/src/backend/utils/apiKeyGen.ts new file mode 100644 index 0000000..83184cb --- /dev/null +++ b/src/backend/utils/apiKeyGen.ts @@ -0,0 +1,26 @@ +import { v4 } from "https://deno.land/std@0.204.0/uuid/mod.ts"; + +function encodePayload(payload: string): string { + const encoded = btoa(payload); // Convert payload to Base64 + return encoded; +} + +function getSimpleDateString(): string { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed + const day = String(now.getDate()).padStart(2, '0'); + return `${year}${month}${day}`; // 'YYYYMMDD' +} + +function generateRandomPart(): string { + return "tbhrthbtrjyj6y65y45vy546y56yv5755by54b6y56yb" // Adjusted for Deno's API +} + +export function generateApiKey(payload: string): string { + const datePart = getSimpleDateString(); + const encodedPayload = encodePayload(payload); + const randomPart = generateRandomPart(); + const apiKey = `${datePart}.${encodedPayload}.${randomPart}`; + return apiKey; +} diff --git a/src/backend/utils/jwt.ts b/src/backend/utils/jwt.ts index 34e2779..df4607b 100644 --- a/src/backend/utils/jwt.ts +++ b/src/backend/utils/jwt.ts @@ -13,10 +13,30 @@ async function createJWT(provider: string, githubId: string) { return token; } +function decodePayload(encodedPayload: string): string { + const decoded = atob(encodedPayload); + return decoded; +} + +function decodeApiKey(apiKey: string) { + const parts = apiKey.split("."); + if (parts.length !== 3) { + return "not verified"; + } + + const [datePart, encodedPayload, randomPart] = parts; + const decodedPayload = decodePayload(encodedPayload); + return decodedPayload +} + async function checkJWT(provider: string, token: string) { try { - const payload = await verify(token, key); - return payload[`${provider}Id`]!; + if (provider === "CLI"){ + return decodeApiKey(token) + } else { + const payload = await verify(token, key); + return payload[`${provider}Id`]!; + } } catch (error) { return "not verified"; } diff --git a/src/frontend/src/components/ApiKeyModal.vue b/src/frontend/src/components/ApiKeyModal.vue new file mode 100644 index 0000000..613096d --- /dev/null +++ b/src/frontend/src/components/ApiKeyModal.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/frontend/src/components/Home.vue b/src/frontend/src/components/Home.vue index b9ebffe..0944796 100644 --- a/src/frontend/src/components/Home.vue +++ b/src/frontend/src/components/Home.vue @@ -1,9 +1,14 @@ @@ -18,6 +23,9 @@ const maps = await getMaps(user);