From ad57fa56f7b7ccb17b35a87148a599c18230a83e Mon Sep 17 00:00:00 2001 From: "Jyotirmoy Bandyopadhyaya [Bravo68]" Date: Tue, 16 May 2023 19:35:51 +0530 Subject: [PATCH] =?UTF-8?q?Create=20CONTRIBUTING.md=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintignore | 4 +- .gitignore | 17 +- .graphqlrc.yaml | 4 +- .nvmrc | 1 + CONTRIBUTING.md | 119 ++++++ README.md | 29 +- package.json | 74 +--- .env.example => packages/api/.env.example | 0 packages/api/.gitignore | 2 + Dockerfile => packages/api/Dockerfile | 0 packages/api/README.md | 60 +++ {bin => packages/api/bin}/fetch-gql-schema.sh | 0 {configs => packages/api/configs}/index.ts | 0 .../api/controllers}/apikey.controller.ts | 0 .../api/controllers}/gists.controller.ts | 0 .../api/controllers}/sxcu.controller.ts | 0 .../api/controllers}/upload.controller.ts | 0 .../api/controllers}/urlstore.controller.ts | 0 .../api/data}/uploader.service.ts | 0 .../api/graphql}/schema.graphql | 402 ++++++++++++++++-- {hasura => packages/api/hasura}/config.yaml | 0 .../api/hasura}/metadata/actions.graphql | 0 .../api/hasura}/metadata/actions.yaml | 0 .../api/hasura}/metadata/allow_list.yaml | 0 .../api/hasura}/metadata/api_limits.yaml | 0 .../api/hasura}/metadata/backend_configs.yaml | 0 .../api/hasura}/metadata/cron_triggers.yaml | 0 .../hasura}/metadata/databases/databases.yaml | 0 .../default/tables/public_apikeys.yaml | 0 .../default/tables/public_gists.yaml | 0 .../default/tables/public_shorturls.yaml | 0 .../default/tables/public_uploads.yaml | 0 .../databases/default/tables/tables.yaml | 0 .../graphql_schema_introspection.yaml | 0 .../api/hasura}/metadata/inherited_roles.yaml | 0 .../api/hasura}/metadata/metrics_config.yaml | 0 .../api/hasura}/metadata/network.yaml | 0 .../api/hasura}/metadata/opentelemetry.yaml | 0 .../hasura}/metadata/query_collections.yaml | 0 .../api/hasura}/metadata/remote_schemas.yaml | 0 .../api/hasura}/metadata/rest_endpoints.yaml | 0 .../api/hasura}/metadata/version.yaml | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../1683385486210_run_sql_migration/down.sql | 0 .../1683385486210_run_sql_migration/up.sql | 0 .../1683385777576_run_sql_migration/down.sql | 0 .../1683385777576_run_sql_migration/up.sql | 0 .../1683386226447_run_sql_migration/down.sql | 0 .../1683386226447_run_sql_migration/up.sql | 0 .../1683386282101_run_sql_migration/down.sql | 0 .../1683386282101_run_sql_migration/up.sql | 0 .../1683386346978_run_sql_migration/down.sql | 0 .../1683386346978_run_sql_migration/up.sql | 0 .../1683386492737_run_sql_migration/down.sql | 0 .../1683386492737_run_sql_migration/up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../api/helpers}/axios_client.ts | 2 +- .../api/helpers}/cache.factory.ts | 0 .../api/helpers}/gql_clent.ts | 0 {helpers => packages/api/helpers}/index.ts | 2 +- .../api/helpers}/upload.factory.ts | 0 index.ts => packages/api/index.ts | 5 +- .../api/interfaces}/apikey.interface.d.ts | 0 .../api/interfaces}/config.interface.d.ts | 0 .../api/interfaces}/gists.interface.d.ts | 0 .../api/interfaces}/sxcu.interface.d.ts | 0 .../api/interfaces}/upload.interface.d.ts | 0 .../api/interfaces}/urlstore.interface.d.ts | 0 .../api/libs}/customErrHandler.ts | 0 {libs => packages/api/libs}/error.ts | 0 {libs => packages/api/libs}/index.ts | 0 {libs => packages/api/libs}/middleware.ts | 4 +- {libs => packages/api/libs}/utilities.ts | 2 +- .../api/middlewares}/apikey_check.ts | 0 packages/api/package.json | 66 +++ .../api/routes}/apikey.routes.ts | 0 .../api/routes}/config.routes.ts | 0 .../api/routes}/gist.routes.ts | 0 {routes => packages/api/routes}/index.ts | 5 +- .../api/routes}/upload.routes.ts | 0 {routes => packages/api/routes}/url.routes.ts | 0 .../api/services}/apikey.service.ts | 1 - .../api/services}/gist.service.ts | 0 .../api/services}/sxcu.service.ts | 0 .../api/services}/upload.service.ts | 16 +- .../api/services}/urlstore.service.ts | 0 tsconfig.json => packages/api/tsconfig.json | 8 +- {types => packages/api/types}/index.d.ts | 0 {uploads => packages/api/uploads}/.gitkeep | 0 ...nHGwQ0vRScphQ1IIpfs-logo-1024-ice-text.png | Bin 0 -> 72110 bytes packages/cli/package.json | 27 ++ packages/cli/src/commands/index.ts | 12 + packages/cli/src/commands/info.ts | 11 + packages/cli/src/functions/info.ts | 4 + packages/cli/src/shx.ts | 9 + packages/cli/tsconfig.json | 24 ++ packages/frontend/.eslintrc.json | 3 + packages/frontend/.gitignore | 35 ++ packages/frontend/README.md | 38 ++ packages/frontend/next.config.js | 6 + packages/frontend/package.json | 25 ++ packages/frontend/pages/_app.tsx | 6 + packages/frontend/pages/_document.tsx | 13 + packages/frontend/pages/api/hello.ts | 13 + packages/frontend/pages/index.tsx | 118 +++++ packages/frontend/postcss.config.js | 6 + packages/frontend/public/favicon.ico | Bin 0 -> 39535 bytes packages/frontend/public/next.svg | 1 + packages/frontend/public/vercel.svg | 1 + packages/frontend/styles/globals.css | 27 ++ packages/frontend/tailwind.config.js | 18 + packages/frontend/tsconfig.json | 23 + 124 files changed, 1114 insertions(+), 129 deletions(-) create mode 100644 .nvmrc create mode 100644 CONTRIBUTING.md rename .env.example => packages/api/.env.example (100%) create mode 100644 packages/api/.gitignore rename Dockerfile => packages/api/Dockerfile (100%) create mode 100644 packages/api/README.md rename {bin => packages/api/bin}/fetch-gql-schema.sh (100%) rename {configs => packages/api/configs}/index.ts (100%) rename {controllers => packages/api/controllers}/apikey.controller.ts (100%) rename {controllers => packages/api/controllers}/gists.controller.ts (100%) rename {controllers => packages/api/controllers}/sxcu.controller.ts (100%) rename {controllers => packages/api/controllers}/upload.controller.ts (100%) rename {controllers => packages/api/controllers}/urlstore.controller.ts (100%) rename {data => packages/api/data}/uploader.service.ts (100%) rename {graphql => packages/api/graphql}/schema.graphql (83%) rename {hasura => packages/api/hasura}/config.yaml (100%) rename {hasura => packages/api/hasura}/metadata/actions.graphql (100%) rename {hasura => packages/api/hasura}/metadata/actions.yaml (100%) rename {hasura => packages/api/hasura}/metadata/allow_list.yaml (100%) rename {hasura => packages/api/hasura}/metadata/api_limits.yaml (100%) rename {hasura => packages/api/hasura}/metadata/backend_configs.yaml (100%) rename {hasura => packages/api/hasura}/metadata/cron_triggers.yaml (100%) rename {hasura => packages/api/hasura}/metadata/databases/databases.yaml (100%) rename {hasura => packages/api/hasura}/metadata/databases/default/tables/public_apikeys.yaml (100%) rename {hasura => packages/api/hasura}/metadata/databases/default/tables/public_gists.yaml (100%) rename {hasura => packages/api/hasura}/metadata/databases/default/tables/public_shorturls.yaml (100%) rename {hasura => packages/api/hasura}/metadata/databases/default/tables/public_uploads.yaml (100%) rename {hasura => packages/api/hasura}/metadata/databases/default/tables/tables.yaml (100%) rename {hasura => packages/api/hasura}/metadata/graphql_schema_introspection.yaml (100%) rename {hasura => packages/api/hasura}/metadata/inherited_roles.yaml (100%) rename {hasura => packages/api/hasura}/metadata/metrics_config.yaml (100%) rename {hasura => packages/api/hasura}/metadata/network.yaml (100%) rename {hasura => packages/api/hasura}/metadata/opentelemetry.yaml (100%) rename {hasura => packages/api/hasura}/metadata/query_collections.yaml (100%) rename {hasura => packages/api/hasura}/metadata/remote_schemas.yaml (100%) rename {hasura => packages/api/hasura}/metadata/rest_endpoints.yaml (100%) rename {hasura => packages/api/hasura}/metadata/version.yaml (100%) rename {hasura => packages/api/hasura}/migrations/default/1683384792406_generate_api_key_func/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683384792406_generate_api_key_func/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683384939518_create_table_public_apikeys/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683384939518_create_table_public_apikeys/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683385211855_create_table_public_uploads/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683385211855_create_table_public_uploads/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683385486210_run_sql_migration/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683385486210_run_sql_migration/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683385777576_run_sql_migration/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683385777576_run_sql_migration/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386226447_run_sql_migration/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386226447_run_sql_migration/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386282101_run_sql_migration/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386282101_run_sql_migration/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386346978_run_sql_migration/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386346978_run_sql_migration/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386492737_run_sql_migration/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386492737_run_sql_migration/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386905959_create_table_public_shorturls/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386905959_create_table_public_shorturls/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683387751628_create_table_public_gists/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683387751628_create_table_public_gists/up.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/down.sql (100%) rename {hasura => packages/api/hasura}/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/up.sql (100%) rename {helpers => packages/api/helpers}/axios_client.ts (99%) rename {helpers => packages/api/helpers}/cache.factory.ts (100%) rename {helpers => packages/api/helpers}/gql_clent.ts (100%) rename {helpers => packages/api/helpers}/index.ts (73%) rename {helpers => packages/api/helpers}/upload.factory.ts (100%) rename index.ts => packages/api/index.ts (96%) rename {interfaces => packages/api/interfaces}/apikey.interface.d.ts (100%) rename {interfaces => packages/api/interfaces}/config.interface.d.ts (100%) rename {interfaces => packages/api/interfaces}/gists.interface.d.ts (100%) rename {interfaces => packages/api/interfaces}/sxcu.interface.d.ts (100%) rename {interfaces => packages/api/interfaces}/upload.interface.d.ts (100%) rename {interfaces => packages/api/interfaces}/urlstore.interface.d.ts (100%) rename {libs => packages/api/libs}/customErrHandler.ts (100%) rename {libs => packages/api/libs}/error.ts (100%) rename {libs => packages/api/libs}/index.ts (100%) rename {libs => packages/api/libs}/middleware.ts (97%) rename {libs => packages/api/libs}/utilities.ts (99%) rename {middlewares => packages/api/middlewares}/apikey_check.ts (100%) create mode 100644 packages/api/package.json rename {routes => packages/api/routes}/apikey.routes.ts (100%) rename {routes => packages/api/routes}/config.routes.ts (100%) rename {routes => packages/api/routes}/gist.routes.ts (100%) rename {routes => packages/api/routes}/index.ts (93%) rename {routes => packages/api/routes}/upload.routes.ts (100%) rename {routes => packages/api/routes}/url.routes.ts (100%) rename {services => packages/api/services}/apikey.service.ts (98%) rename {services => packages/api/services}/gist.service.ts (100%) rename {services => packages/api/services}/sxcu.service.ts (100%) rename {services => packages/api/services}/upload.service.ts (94%) rename {services => packages/api/services}/urlstore.service.ts (100%) rename tsconfig.json => packages/api/tsconfig.json (67%) rename {types => packages/api/types}/index.d.ts (100%) rename {uploads => packages/api/uploads}/.gitkeep (100%) create mode 100644 packages/api/uploads/_7Igi_nHGwQ0vRScphQ1IIpfs-logo-1024-ice-text.png create mode 100644 packages/cli/package.json create mode 100644 packages/cli/src/commands/index.ts create mode 100644 packages/cli/src/commands/info.ts create mode 100644 packages/cli/src/functions/info.ts create mode 100644 packages/cli/src/shx.ts create mode 100644 packages/cli/tsconfig.json create mode 100644 packages/frontend/.eslintrc.json create mode 100644 packages/frontend/.gitignore create mode 100644 packages/frontend/README.md create mode 100644 packages/frontend/next.config.js create mode 100644 packages/frontend/package.json create mode 100644 packages/frontend/pages/_app.tsx create mode 100644 packages/frontend/pages/_document.tsx create mode 100644 packages/frontend/pages/api/hello.ts create mode 100644 packages/frontend/pages/index.tsx create mode 100644 packages/frontend/postcss.config.js create mode 100644 packages/frontend/public/favicon.ico create mode 100644 packages/frontend/public/next.svg create mode 100644 packages/frontend/public/vercel.svg create mode 100644 packages/frontend/styles/globals.css create mode 100644 packages/frontend/tailwind.config.js create mode 100644 packages/frontend/tsconfig.json diff --git a/.eslintignore b/.eslintignore index 40bfc89..2709d9e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -121,4 +121,6 @@ jwtRS256.key jwtRS256.key.pub #markdown files -**/*.md \ No newline at end of file +**/*.md + +*/**/.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index 23a5824..2caeae7 100644 --- a/.gitignore +++ b/.gitignore @@ -131,9 +131,10 @@ dist .husky -yarn.lock -package-lock.json -build +*/**/yarn.lock +*/**/package-lock.json +*/**/build +*/**/dist !uploads/.gitkeep uploads/*.png @@ -146,4 +147,12 @@ uploads/*.mp4 uploads/*.mp3 uploads/*.zip uploads/*.rar -uploads/*.pdf \ No newline at end of file +uploads/*.pdf + +*/**/node_modules +*/**/.env + +yarn.lock +package-lock.json +build +dist \ No newline at end of file diff --git a/.graphqlrc.yaml b/.graphqlrc.yaml index 3151b3f..494194e 100644 --- a/.graphqlrc.yaml +++ b/.graphqlrc.yaml @@ -1,5 +1,5 @@ -schema: 'graphql/schema.graphql' -documents: 'services/*.ts' +schema: 'packages/api/graphql/schema.graphql' +documents: 'packages/api/services/*.ts' extensions: languageService: cacheSchemaFileForLookup: true diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..b1215e8 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v18.16.0 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..203e167 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,119 @@ +## Contributing Guidelines + +Thank you for considering contributing to our custom ShareX server! We appreciate your interest in making our project better. To ensure a smooth collaboration, please take a moment to review and follow these guidelines. + +## Table of Contents + +- [Contributing Guidelines](#contributing-guidelines) +- [Table of Contents](#table-of-contents) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Installation](#installation) +- [Project Structure](#project-structure) + - [API](#api) + - [CLI](#cli) + - [Dashboard](#dashboard) +- [Contribution Process](#contribution-process) + - [Reporting Issues](#reporting-issues) + - [Submitting Pull Requests](#submitting-pull-requests) + +## Getting Started + +To contribute to our custom ShareX server, follow these steps to set up the development environment. + +### Prerequisites + +- Node.js (version 18.X.X) +- WSL (Windows Subsystem for Linux) (for Windows users) +- Yarn (version 2.X.X) + +### Installation + +1. Fork the repository on GitHub. +2. Clone the forked repository to your local machine. +3. Install the required dependencies by running the following command in the project root directory: + + ``` + npm install + ``` + +4. Edit `.env` inside `packages/api`. Start the API server by running the following command: + + ``` + cd packages/api + yarn dev + ``` + +5. Start the CLI by running the following command: + + ``` + cd packages/cli + yarn r + ``` + +6. Edit `.env` inside `packages/frontend`. Start the Dashboard by running the following command: + + ``` + cd packages/frontend + yarn dev + ``` + +## Project Structure + +Our custom ShareX server is organized as a monorepo with the following packages: + +### API + +The API package contains the server-side code responsible for handling file, image, and text uploads, as well as URL shortening. It provides the core functionality of the ShareX server. + +### CLI + +The CLI package includes the command-line interface (CLI) for interacting with the ShareX server from the terminal. It allows users to perform various actions, such as uploading files, images, or text, and generating short URLs. + +### Dashboard + +The Dashboard package consists of the web-based admin dashboard for managing files, images, or text, and generated short URLs. It provides an intuitive interface for administrators to manage the ShareX server. + +## Contribution Process + +We welcome contributions from the community. If you encounter issues, have ideas for improvements, or want to contribute code, please follow the guidelines below. + +### Reporting Issues + +If you encounter any problems while using the ShareX server, please open an issue on the GitHub repository. Make sure to include detailed information about the issue, including steps to reproduce, expected behavior, and any relevant error messages. + +### Submitting Pull Requests + +If you want to contribute code to the project, follow these steps: + +1. Create a fork of the repository on GitHub. + +2. Clone the forked repository to your local machine: + ```bash + git clone https://github.com//shx.git + cd shx + ``` + +3. Create a new branch for your changes: + ```bash + git checkout -b feat/new-feature + ``` +4. Make your changes to the codebase. + +5. Test your changes to ensure they work as intended. + +6. Commit your changes with a clear and descriptive commit message: + ```bash + git commit -m "Add new feature" + ``` + +7. Push to your branch: + ```bash + git push origin feat/new-feature + ``` + +8. Open a pull request on the GitHub repository. Make sure to include a detailed description of your changes. + +9. Wait for a maintainer to review your pull request. If there are any issues, you may be asked to make changes to your code. Otherwise, your pull request will be merged into the main branch. + +10. Celebrate! 🎉 \ No newline at end of file diff --git a/README.md b/README.md index dc4d903..a813532 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 🚀 **SHX SERVER** +# 🚀 **Project SHX** ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bravo68web/shx/build.yaml?style=for-the-badge) ![GitHub last commit](https://img.shields.io/github/last-commit/bravo68web/shx?style=for-the-badge) @@ -11,7 +11,9 @@ ## 📝 Description -Shx is a custom ShareX server that allows you to upload and share files, images, text and URLs with ease. This server is built using Node.js and Express.js and stores the uploaded content on AWS S3 and Redis. +Shx is a custom ShareX server that allows you to upload and share files, images, +text and URLs with ease. This server is built using Node.js and Express.js and +stores the uploaded content on AWS S3 and Redis. ## 🛠️ Technologies Used @@ -21,6 +23,8 @@ Shx is a custom ShareX server that allows you to upload and share files, images, - Redis - Cloudflare R2 - Hasura Graphql (with PostgreSQL) +- Next.JS +- Commander.js ## 🚀 Features @@ -28,22 +32,8 @@ Shx is a custom ShareX server that allows you to upload and share files, images, - Image upload - Text upload - URL shortener - -## 🚀 Getting Started - -To get started with the project, follow these steps: - -1. Clone the repo. -2. Run `npm install` to install dependencies. -3. Copy the `.env.example` file and create a `.env` file with your environment variables. -4. Run `npm run dev` to start the development server. - -## 📜 Scripts - -- `npm run dev`: Starts the development server. -- `npm run build`: Builds the project. -- `npm start`: Starts the project. -- `npm run prettier`: Runs Prettier to format code. +- CLI Support +- Admin Dashboard ## 📝 License @@ -53,4 +43,5 @@ For more information, please see the `LICENSE` file. ## 📧 Contact -If you have any questions or would like to contribute to the project, please contact `hi@b68.dev`. \ No newline at end of file +If you have any questions or would like to contribute to the project, please +contact `hi@b68.dev`. \ No newline at end of file diff --git a/package.json b/package.json index fd6246c..27c8967 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,21 @@ { - "name": "shx-daddy", + "name": "shx", "version": "1.0.0", - "description": "My ShareX Server acting as Daddy", - "main": "index.ts", - "repository": "https://github.com/bravo68web/shx.git", - "author": { - "email": "hi@b68.dev", - "name": "Jyotirmoy Bandyopadhayaya", - "url": "https://b68.dev" - }, - "license": "ISC", - "type": "module", + "description": "Private Data Hosting Service", + "repository": "git@github.com:BRAVO68WEB/shx.git", + "author": "Jyotirmoy Bandyopadhyaya [Bravo68] ", + "license": "MIT", "private": true, - "dependencies": { - "@aws-sdk/client-s3": "^3.226.0", - "axios": "^1.2.1", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "envfile": "^6.18.0", - "express": "^4.18.2", - "form-data": "^4.0.0", - "graphql": "^16.6.0", - "graphql-request": "^5.0.0", - "helmet": "^6.0.1", - "joi": "^17.7.0", - "morgan": "^1.10.0", - "multer": "^1.4.5-lts.1", - "multer-s3": "^3.0.1", - "napi-nanoid": "^0.0.4", - "node-cache": "^5.1.2", - "nodemailer": "^6.8.0", - "redis": "^4.5.1", - "sharp": "^0.32.1" - }, + "workspaces": [ + "packages/*" + ], "scripts": { - "dev": "concurrently \"npm run dev:express\" \"npm run dev:hasura\"", - "dev:hasura": "cd hasura && hasura --skip-update-check --envfile ../.env console", - "dev:express": "cross-env NODE_ENV=development nodemon -x node --no-warnings --experimental-specifier-resolution=node --loader ts-node/esm index.ts --signal SIGKILL --ignore node_modules", - "build": "tsc -p tsconfig.json", - "start": "node --es-module-specifier-resolution=node --loader ts-node/esm ./build/index.js", "prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,scss,md}\"", "prepare": "husky install", - "fetch:schemas": "bash bin/fetch-gql-schema.sh", "configure-husky": "npx husky install && npx husky add .husky/pre-commit \"npx --no-install lint-staged\"" }, - "devDependencies": { - "@swc/core": "^1.3.23", - "@swc/wasm": "^1.3.23", - "@types/cors": "^2.8.13", - "@types/express": "^4.17.14", - "@types/morgan": "^1.9.3", - "@types/multer": "^1.4.7", - "@typescript-eslint/eslint-plugin": "^5.54.1", - "@typescript-eslint/parser": "^5.54.1", - "concurrently": "^7.6.0", - "cross-env": "^7.0.3", - "eslint": "^8.35.0", - "eslint-config-prettier": "^8.7.0", - "graphqurl": "^1.0.1", - "hasura-cli": "^2.15.1", - "husky": "^8.0.3", - "lint-staged": "^13.1.2", - "nodemon": "^2.0.22", - "prettier": "^2.8.2", - "ts-node": "^10.9.1", - "typescript": "^4.9.3" - }, "lint-staged": { - "**/*.{js,json,ts,css}": [ + "**/*.{js,ts}": [ "eslint --fix", "prettier --write" ] @@ -76,5 +24,9 @@ "hooks": { "pre-commit": "lint-staged" } + }, + "devDependencies": { + "husky": "^8.0.3", + "prettier": "^2.8.8" } } diff --git a/.env.example b/packages/api/.env.example similarity index 100% rename from .env.example rename to packages/api/.env.example diff --git a/packages/api/.gitignore b/packages/api/.gitignore new file mode 100644 index 0000000..2319e55 --- /dev/null +++ b/packages/api/.gitignore @@ -0,0 +1,2 @@ +.env.prod +.env.dev \ No newline at end of file diff --git a/Dockerfile b/packages/api/Dockerfile similarity index 100% rename from Dockerfile rename to packages/api/Dockerfile diff --git a/packages/api/README.md b/packages/api/README.md new file mode 100644 index 0000000..31763cb --- /dev/null +++ b/packages/api/README.md @@ -0,0 +1,60 @@ +# 🚀 **SHX API** + +![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bravo68web/shx/build.yaml?style=for-the-badge) +![GitHub last commit](https://img.shields.io/github/last-commit/bravo68web/shx?style=for-the-badge) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/bravo68web/shx?style=for-the-badge) +![Snyk Vulnerabilities for GitHub Repo](https://img.shields.io/snyk/vulnerabilities/github/bravo68web/shx?style=for-the-badge) + +[![forthebadge](https://forthebadge.com/images/badges/made-with-typescript.svg)](https://forthebadge.com) +[![forthebadge](https://forthebadge.com/images/badges/powered-by-black-magic.svg)](https://forthebadge.com) +[![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)](https://forthebadge.com) + +## 📝 Description + +Shx is a custom ShareX server that allows you to upload and share files, images, +text and URLs with ease. This server is built using Node.js and Express.js and +stores the uploaded content on AWS S3 and Redis. + +## 🛠️ Technologies Used + +- Node.js +- Express.js +- TypeScript +- Redis +- Cloudflare R2 +- Hasura Graphql (with PostgreSQL) + +## 🚀 Features + +- File upload +- Image upload +- Text upload +- URL shortener + +## 🚀 Getting Started + +To get started with the project, follow these steps: + +1. Clone the repo. +2. Run `npm install` to install dependencies. +3. Copy the `.env.example` file and create a `.env` file with your environment + variables. +4. Run `npm run dev` to start the development server. + +## 📜 Scripts + +- `npm run dev`: Starts the development server. +- `npm run build`: Builds the project. +- `npm start`: Starts the project. +- `npm run prettier`: Runs Prettier to format code. + +## 📝 License + +This project is licensed under the ISC License. + +For more information, please see the `LICENSE` file. + +## 📧 Contact + +If you have any questions or would like to contribute to the project, please +contact `hi@b68.dev`. diff --git a/bin/fetch-gql-schema.sh b/packages/api/bin/fetch-gql-schema.sh similarity index 100% rename from bin/fetch-gql-schema.sh rename to packages/api/bin/fetch-gql-schema.sh diff --git a/configs/index.ts b/packages/api/configs/index.ts similarity index 100% rename from configs/index.ts rename to packages/api/configs/index.ts diff --git a/controllers/apikey.controller.ts b/packages/api/controllers/apikey.controller.ts similarity index 100% rename from controllers/apikey.controller.ts rename to packages/api/controllers/apikey.controller.ts diff --git a/controllers/gists.controller.ts b/packages/api/controllers/gists.controller.ts similarity index 100% rename from controllers/gists.controller.ts rename to packages/api/controllers/gists.controller.ts diff --git a/controllers/sxcu.controller.ts b/packages/api/controllers/sxcu.controller.ts similarity index 100% rename from controllers/sxcu.controller.ts rename to packages/api/controllers/sxcu.controller.ts diff --git a/controllers/upload.controller.ts b/packages/api/controllers/upload.controller.ts similarity index 100% rename from controllers/upload.controller.ts rename to packages/api/controllers/upload.controller.ts diff --git a/controllers/urlstore.controller.ts b/packages/api/controllers/urlstore.controller.ts similarity index 100% rename from controllers/urlstore.controller.ts rename to packages/api/controllers/urlstore.controller.ts diff --git a/data/uploader.service.ts b/packages/api/data/uploader.service.ts similarity index 100% rename from data/uploader.service.ts rename to packages/api/data/uploader.service.ts diff --git a/graphql/schema.graphql b/packages/api/graphql/schema.graphql similarity index 83% rename from graphql/schema.graphql rename to packages/api/graphql/schema.graphql index 0beec49..cfa91ef 100644 --- a/graphql/schema.graphql +++ b/packages/api/graphql/schema.graphql @@ -99,8 +99,80 @@ input String_comparison_exp { """API Keys Allowed""" type apikeys { created_at: timestamptz! + + """An array relationship""" + gists( + """distinct select on columns""" + distinct_on: [gists_select_column!] + + """limit the number of rows returned""" + limit: Int + + """skip the first n rows. Use only with order_by""" + offset: Int + + """sort the rows by one or more columns""" + order_by: [gists_order_by!] + + """filter the rows returned""" + where: gists_bool_exp + ): [gists!]! + + """An aggregate relationship""" + gists_aggregate( + """distinct select on columns""" + distinct_on: [gists_select_column!] + + """limit the number of rows returned""" + limit: Int + + """skip the first n rows. Use only with order_by""" + offset: Int + + """sort the rows by one or more columns""" + order_by: [gists_order_by!] + + """filter the rows returned""" + where: gists_bool_exp + ): gists_aggregate! key: String! keyID: uuid! + + """An array relationship""" + shorturls( + """distinct select on columns""" + distinct_on: [shorturls_select_column!] + + """limit the number of rows returned""" + limit: Int + + """skip the first n rows. Use only with order_by""" + offset: Int + + """sort the rows by one or more columns""" + order_by: [shorturls_order_by!] + + """filter the rows returned""" + where: shorturls_bool_exp + ): [shorturls!]! + + """An aggregate relationship""" + shorturls_aggregate( + """distinct select on columns""" + distinct_on: [shorturls_select_column!] + + """limit the number of rows returned""" + limit: Int + + """skip the first n rows. Use only with order_by""" + offset: Int + + """sort the rows by one or more columns""" + order_by: [shorturls_order_by!] + + """filter the rows returned""" + where: shorturls_bool_exp + ): shorturls_aggregate! updated_at: timestamptz! """An array relationship""" @@ -165,8 +237,12 @@ input apikeys_bool_exp { _not: apikeys_bool_exp _or: [apikeys_bool_exp!] created_at: timestamptz_comparison_exp + gists: gists_bool_exp + gists_aggregate: gists_aggregate_bool_exp key: String_comparison_exp keyID: uuid_comparison_exp + shorturls: shorturls_bool_exp + shorturls_aggregate: shorturls_aggregate_bool_exp updated_at: timestamptz_comparison_exp uploads: uploads_bool_exp uploads_aggregate: uploads_aggregate_bool_exp @@ -192,8 +268,10 @@ input type for inserting data into table "apikeys" """ input apikeys_insert_input { created_at: timestamptz + gists: gists_arr_rel_insert_input key: String keyID: uuid + shorturls: shorturls_arr_rel_insert_input updated_at: timestamptz uploads: uploads_arr_rel_insert_input } @@ -247,8 +325,10 @@ input apikeys_on_conflict { """Ordering options when selecting data from "apikeys".""" input apikeys_order_by { created_at: order_by + gists_aggregate: gists_aggregate_order_by key: order_by keyID: order_by + shorturls_aggregate: shorturls_aggregate_order_by updated_at: order_by uploads_aggregate: uploads_aggregate_order_by } @@ -342,6 +422,8 @@ enum cursor_ordering { columns and relationships of "gists" """ type gists { + """An object relationship""" + apikey: apikeys! apikeyUsed: uuid! content: String! created_on: timestamptz! @@ -362,6 +444,33 @@ type gists_aggregate { nodes: [gists!]! } +input gists_aggregate_bool_exp { + bool_and: gists_aggregate_bool_exp_bool_and + bool_or: gists_aggregate_bool_exp_bool_or + count: gists_aggregate_bool_exp_count +} + +input gists_aggregate_bool_exp_bool_and { + arguments: gists_select_column_gists_aggregate_bool_exp_bool_and_arguments_columns! + distinct: Boolean + filter: gists_bool_exp + predicate: Boolean_comparison_exp! +} + +input gists_aggregate_bool_exp_bool_or { + arguments: gists_select_column_gists_aggregate_bool_exp_bool_or_arguments_columns! + distinct: Boolean + filter: gists_bool_exp + predicate: Boolean_comparison_exp! +} + +input gists_aggregate_bool_exp_count { + arguments: [gists_select_column!] + distinct: Boolean + filter: gists_bool_exp + predicate: Int_comparison_exp! +} + """ aggregate fields of "gists" """ @@ -379,11 +488,45 @@ type gists_aggregate_fields { variance: gists_variance_fields } +""" +order by aggregate values of table "gists" +""" +input gists_aggregate_order_by { + avg: gists_avg_order_by + count: order_by + max: gists_max_order_by + min: gists_min_order_by + stddev: gists_stddev_order_by + stddev_pop: gists_stddev_pop_order_by + stddev_samp: gists_stddev_samp_order_by + sum: gists_sum_order_by + var_pop: gists_var_pop_order_by + var_samp: gists_var_samp_order_by + variance: gists_variance_order_by +} + +""" +input type for inserting array relation for remote table "gists" +""" +input gists_arr_rel_insert_input { + data: [gists_insert_input!]! + + """upsert condition""" + on_conflict: gists_on_conflict +} + """aggregate avg on columns""" type gists_avg_fields { views: Float } +""" +order by avg() on columns of table "gists" +""" +input gists_avg_order_by { + views: order_by +} + """ Boolean expression to filter rows from the table "gists". All fields are combined with a logical 'AND'. """ @@ -391,6 +534,7 @@ input gists_bool_exp { _and: [gists_bool_exp!] _not: gists_bool_exp _or: [gists_bool_exp!] + apikey: apikeys_bool_exp apikeyUsed: uuid_comparison_exp content: String_comparison_exp created_on: timestamptz_comparison_exp @@ -434,6 +578,7 @@ input gists_inc_input { input type for inserting data into table "gists" """ input gists_insert_input { + apikey: apikeys_obj_rel_insert_input apikeyUsed: uuid content: String created_on: timestamptz @@ -458,6 +603,20 @@ type gists_max_fields { views: numeric } +""" +order by max() on columns of table "gists" +""" +input gists_max_order_by { + apikeyUsed: order_by + content: order_by + created_on: order_by + creator_ip: order_by + gistID: order_by + gist_url_key: order_by + passkey: order_by + views: order_by +} + """aggregate min on columns""" type gists_min_fields { apikeyUsed: uuid @@ -470,6 +629,20 @@ type gists_min_fields { views: numeric } +""" +order by min() on columns of table "gists" +""" +input gists_min_order_by { + apikeyUsed: order_by + content: order_by + created_on: order_by + creator_ip: order_by + gistID: order_by + gist_url_key: order_by + passkey: order_by + views: order_by +} + """ response of any mutation on the table "gists" """ @@ -492,6 +665,7 @@ input gists_on_conflict { """Ordering options when selecting data from "gists".""" input gists_order_by { + apikey: apikeys_order_by apikeyUsed: order_by content: order_by created_on: order_by @@ -544,6 +718,28 @@ enum gists_select_column { views } +""" +select "gists_aggregate_bool_exp_bool_and_arguments_columns" columns of table "gists" +""" +enum gists_select_column_gists_aggregate_bool_exp_bool_and_arguments_columns { + """column name""" + isOneTimeOnly + + """column name""" + isPrivate +} + +""" +select "gists_aggregate_bool_exp_bool_or_arguments_columns" columns of table "gists" +""" +enum gists_select_column_gists_aggregate_bool_exp_bool_or_arguments_columns { + """column name""" + isOneTimeOnly + + """column name""" + isPrivate +} + """ input type for updating data in table "gists" """ @@ -565,16 +761,37 @@ type gists_stddev_fields { views: Float } +""" +order by stddev() on columns of table "gists" +""" +input gists_stddev_order_by { + views: order_by +} + """aggregate stddev_pop on columns""" type gists_stddev_pop_fields { views: Float } +""" +order by stddev_pop() on columns of table "gists" +""" +input gists_stddev_pop_order_by { + views: order_by +} + """aggregate stddev_samp on columns""" type gists_stddev_samp_fields { views: Float } +""" +order by stddev_samp() on columns of table "gists" +""" +input gists_stddev_samp_order_by { + views: order_by +} + """ Streaming cursor of the table "gists" """ @@ -605,6 +822,13 @@ type gists_sum_fields { views: numeric } +""" +order by sum() on columns of table "gists" +""" +input gists_sum_order_by { + views: order_by +} + """ update columns of table "gists" """ @@ -656,16 +880,37 @@ type gists_var_pop_fields { views: Float } +""" +order by var_pop() on columns of table "gists" +""" +input gists_var_pop_order_by { + views: order_by +} + """aggregate var_samp on columns""" type gists_var_samp_fields { views: Float } +""" +order by var_samp() on columns of table "gists" +""" +input gists_var_samp_order_by { + views: order_by +} + """aggregate variance on columns""" type gists_variance_fields { views: Float } +""" +order by variance() on columns of table "gists" +""" +input gists_variance_order_by { + views: order_by +} + """mutation root""" type mutation_root { """ @@ -1015,9 +1260,7 @@ type query_root { """fetch data from the table: "apikeys" using primary key columns""" apikeys_by_pk(keyID: uuid!): apikeys - """ - fetch data from the table: "gists" - """ + """An array relationship""" gists( """distinct select on columns""" distinct_on: [gists_select_column!] @@ -1035,9 +1278,7 @@ type query_root { where: gists_bool_exp ): [gists!]! - """ - fetch aggregated fields from the table: "gists" - """ + """An aggregate relationship""" gists_aggregate( """distinct select on columns""" distinct_on: [gists_select_column!] @@ -1058,9 +1299,7 @@ type query_root { """fetch data from the table: "gists" using primary key columns""" gists_by_pk(gistID: uuid!): gists - """ - fetch data from the table: "shorturls" - """ + """An array relationship""" shorturls( """distinct select on columns""" distinct_on: [shorturls_select_column!] @@ -1078,9 +1317,7 @@ type query_root { where: shorturls_bool_exp ): [shorturls!]! - """ - fetch aggregated fields from the table: "shorturls" - """ + """An aggregate relationship""" shorturls_aggregate( """distinct select on columns""" distinct_on: [shorturls_select_column!] @@ -1143,6 +1380,8 @@ type query_root { """All Short URLs""" type shorturls { + """An object relationship""" + apikey: apikeys! apikeyUsed: uuid! clicks: Int! created_on: timestamptz! @@ -1160,6 +1399,17 @@ type shorturls_aggregate { nodes: [shorturls!]! } +input shorturls_aggregate_bool_exp { + count: shorturls_aggregate_bool_exp_count +} + +input shorturls_aggregate_bool_exp_count { + arguments: [shorturls_select_column!] + distinct: Boolean + filter: shorturls_bool_exp + predicate: Int_comparison_exp! +} + """ aggregate fields of "shorturls" """ @@ -1177,11 +1427,45 @@ type shorturls_aggregate_fields { variance: shorturls_variance_fields } +""" +order by aggregate values of table "shorturls" +""" +input shorturls_aggregate_order_by { + avg: shorturls_avg_order_by + count: order_by + max: shorturls_max_order_by + min: shorturls_min_order_by + stddev: shorturls_stddev_order_by + stddev_pop: shorturls_stddev_pop_order_by + stddev_samp: shorturls_stddev_samp_order_by + sum: shorturls_sum_order_by + var_pop: shorturls_var_pop_order_by + var_samp: shorturls_var_samp_order_by + variance: shorturls_variance_order_by +} + +""" +input type for inserting array relation for remote table "shorturls" +""" +input shorturls_arr_rel_insert_input { + data: [shorturls_insert_input!]! + + """upsert condition""" + on_conflict: shorturls_on_conflict +} + """aggregate avg on columns""" type shorturls_avg_fields { clicks: Float } +""" +order by avg() on columns of table "shorturls" +""" +input shorturls_avg_order_by { + clicks: order_by +} + """ Boolean expression to filter rows from the table "shorturls". All fields are combined with a logical 'AND'. """ @@ -1189,6 +1473,7 @@ input shorturls_bool_exp { _and: [shorturls_bool_exp!] _not: shorturls_bool_exp _or: [shorturls_bool_exp!] + apikey: apikeys_bool_exp apikeyUsed: uuid_comparison_exp clicks: Int_comparison_exp created_on: timestamptz_comparison_exp @@ -1224,6 +1509,7 @@ input shorturls_inc_input { input type for inserting data into table "shorturls" """ input shorturls_insert_input { + apikey: apikeys_obj_rel_insert_input apikeyUsed: uuid clicks: Int created_on: timestamptz @@ -1244,6 +1530,19 @@ type shorturls_max_fields { urlID: uuid } +""" +order by max() on columns of table "shorturls" +""" +input shorturls_max_order_by { + apikeyUsed: order_by + clicks: order_by + created_on: order_by + creator_ip: order_by + original_url: order_by + short_key: order_by + urlID: order_by +} + """aggregate min on columns""" type shorturls_min_fields { apikeyUsed: uuid @@ -1255,6 +1554,19 @@ type shorturls_min_fields { urlID: uuid } +""" +order by min() on columns of table "shorturls" +""" +input shorturls_min_order_by { + apikeyUsed: order_by + clicks: order_by + created_on: order_by + creator_ip: order_by + original_url: order_by + short_key: order_by + urlID: order_by +} + """ response of any mutation on the table "shorturls" """ @@ -1277,6 +1589,7 @@ input shorturls_on_conflict { """Ordering options when selecting data from "shorturls".""" input shorturls_order_by { + apikey: apikeys_order_by apikeyUsed: order_by clicks: order_by created_on: order_by @@ -1335,16 +1648,37 @@ type shorturls_stddev_fields { clicks: Float } +""" +order by stddev() on columns of table "shorturls" +""" +input shorturls_stddev_order_by { + clicks: order_by +} + """aggregate stddev_pop on columns""" type shorturls_stddev_pop_fields { clicks: Float } +""" +order by stddev_pop() on columns of table "shorturls" +""" +input shorturls_stddev_pop_order_by { + clicks: order_by +} + """aggregate stddev_samp on columns""" type shorturls_stddev_samp_fields { clicks: Float } +""" +order by stddev_samp() on columns of table "shorturls" +""" +input shorturls_stddev_samp_order_by { + clicks: order_by +} + """ Streaming cursor of the table "shorturls" """ @@ -1372,6 +1706,13 @@ type shorturls_sum_fields { clicks: Int } +""" +order by sum() on columns of table "shorturls" +""" +input shorturls_sum_order_by { + clicks: order_by +} + """ update columns of table "shorturls" """ @@ -1414,16 +1755,37 @@ type shorturls_var_pop_fields { clicks: Float } +""" +order by var_pop() on columns of table "shorturls" +""" +input shorturls_var_pop_order_by { + clicks: order_by +} + """aggregate var_samp on columns""" type shorturls_var_samp_fields { clicks: Float } +""" +order by var_samp() on columns of table "shorturls" +""" +input shorturls_var_samp_order_by { + clicks: order_by +} + """aggregate variance on columns""" type shorturls_variance_fields { clicks: Float } +""" +order by variance() on columns of table "shorturls" +""" +input shorturls_variance_order_by { + clicks: order_by +} + type subscription_root { """ fetch data from the table: "apikeys" @@ -1482,9 +1844,7 @@ type subscription_root { where: apikeys_bool_exp ): [apikeys!]! - """ - fetch data from the table: "gists" - """ + """An array relationship""" gists( """distinct select on columns""" distinct_on: [gists_select_column!] @@ -1502,9 +1862,7 @@ type subscription_root { where: gists_bool_exp ): [gists!]! - """ - fetch aggregated fields from the table: "gists" - """ + """An aggregate relationship""" gists_aggregate( """distinct select on columns""" distinct_on: [gists_select_column!] @@ -1539,9 +1897,7 @@ type subscription_root { where: gists_bool_exp ): [gists!]! - """ - fetch data from the table: "shorturls" - """ + """An array relationship""" shorturls( """distinct select on columns""" distinct_on: [shorturls_select_column!] @@ -1559,9 +1915,7 @@ type subscription_root { where: shorturls_bool_exp ): [shorturls!]! - """ - fetch aggregated fields from the table: "shorturls" - """ + """An aggregate relationship""" shorturls_aggregate( """distinct select on columns""" distinct_on: [shorturls_select_column!] diff --git a/hasura/config.yaml b/packages/api/hasura/config.yaml similarity index 100% rename from hasura/config.yaml rename to packages/api/hasura/config.yaml diff --git a/hasura/metadata/actions.graphql b/packages/api/hasura/metadata/actions.graphql similarity index 100% rename from hasura/metadata/actions.graphql rename to packages/api/hasura/metadata/actions.graphql diff --git a/hasura/metadata/actions.yaml b/packages/api/hasura/metadata/actions.yaml similarity index 100% rename from hasura/metadata/actions.yaml rename to packages/api/hasura/metadata/actions.yaml diff --git a/hasura/metadata/allow_list.yaml b/packages/api/hasura/metadata/allow_list.yaml similarity index 100% rename from hasura/metadata/allow_list.yaml rename to packages/api/hasura/metadata/allow_list.yaml diff --git a/hasura/metadata/api_limits.yaml b/packages/api/hasura/metadata/api_limits.yaml similarity index 100% rename from hasura/metadata/api_limits.yaml rename to packages/api/hasura/metadata/api_limits.yaml diff --git a/hasura/metadata/backend_configs.yaml b/packages/api/hasura/metadata/backend_configs.yaml similarity index 100% rename from hasura/metadata/backend_configs.yaml rename to packages/api/hasura/metadata/backend_configs.yaml diff --git a/hasura/metadata/cron_triggers.yaml b/packages/api/hasura/metadata/cron_triggers.yaml similarity index 100% rename from hasura/metadata/cron_triggers.yaml rename to packages/api/hasura/metadata/cron_triggers.yaml diff --git a/hasura/metadata/databases/databases.yaml b/packages/api/hasura/metadata/databases/databases.yaml similarity index 100% rename from hasura/metadata/databases/databases.yaml rename to packages/api/hasura/metadata/databases/databases.yaml diff --git a/hasura/metadata/databases/default/tables/public_apikeys.yaml b/packages/api/hasura/metadata/databases/default/tables/public_apikeys.yaml similarity index 100% rename from hasura/metadata/databases/default/tables/public_apikeys.yaml rename to packages/api/hasura/metadata/databases/default/tables/public_apikeys.yaml diff --git a/hasura/metadata/databases/default/tables/public_gists.yaml b/packages/api/hasura/metadata/databases/default/tables/public_gists.yaml similarity index 100% rename from hasura/metadata/databases/default/tables/public_gists.yaml rename to packages/api/hasura/metadata/databases/default/tables/public_gists.yaml diff --git a/hasura/metadata/databases/default/tables/public_shorturls.yaml b/packages/api/hasura/metadata/databases/default/tables/public_shorturls.yaml similarity index 100% rename from hasura/metadata/databases/default/tables/public_shorturls.yaml rename to packages/api/hasura/metadata/databases/default/tables/public_shorturls.yaml diff --git a/hasura/metadata/databases/default/tables/public_uploads.yaml b/packages/api/hasura/metadata/databases/default/tables/public_uploads.yaml similarity index 100% rename from hasura/metadata/databases/default/tables/public_uploads.yaml rename to packages/api/hasura/metadata/databases/default/tables/public_uploads.yaml diff --git a/hasura/metadata/databases/default/tables/tables.yaml b/packages/api/hasura/metadata/databases/default/tables/tables.yaml similarity index 100% rename from hasura/metadata/databases/default/tables/tables.yaml rename to packages/api/hasura/metadata/databases/default/tables/tables.yaml diff --git a/hasura/metadata/graphql_schema_introspection.yaml b/packages/api/hasura/metadata/graphql_schema_introspection.yaml similarity index 100% rename from hasura/metadata/graphql_schema_introspection.yaml rename to packages/api/hasura/metadata/graphql_schema_introspection.yaml diff --git a/hasura/metadata/inherited_roles.yaml b/packages/api/hasura/metadata/inherited_roles.yaml similarity index 100% rename from hasura/metadata/inherited_roles.yaml rename to packages/api/hasura/metadata/inherited_roles.yaml diff --git a/hasura/metadata/metrics_config.yaml b/packages/api/hasura/metadata/metrics_config.yaml similarity index 100% rename from hasura/metadata/metrics_config.yaml rename to packages/api/hasura/metadata/metrics_config.yaml diff --git a/hasura/metadata/network.yaml b/packages/api/hasura/metadata/network.yaml similarity index 100% rename from hasura/metadata/network.yaml rename to packages/api/hasura/metadata/network.yaml diff --git a/hasura/metadata/opentelemetry.yaml b/packages/api/hasura/metadata/opentelemetry.yaml similarity index 100% rename from hasura/metadata/opentelemetry.yaml rename to packages/api/hasura/metadata/opentelemetry.yaml diff --git a/hasura/metadata/query_collections.yaml b/packages/api/hasura/metadata/query_collections.yaml similarity index 100% rename from hasura/metadata/query_collections.yaml rename to packages/api/hasura/metadata/query_collections.yaml diff --git a/hasura/metadata/remote_schemas.yaml b/packages/api/hasura/metadata/remote_schemas.yaml similarity index 100% rename from hasura/metadata/remote_schemas.yaml rename to packages/api/hasura/metadata/remote_schemas.yaml diff --git a/hasura/metadata/rest_endpoints.yaml b/packages/api/hasura/metadata/rest_endpoints.yaml similarity index 100% rename from hasura/metadata/rest_endpoints.yaml rename to packages/api/hasura/metadata/rest_endpoints.yaml diff --git a/hasura/metadata/version.yaml b/packages/api/hasura/metadata/version.yaml similarity index 100% rename from hasura/metadata/version.yaml rename to packages/api/hasura/metadata/version.yaml diff --git a/hasura/migrations/default/1683384792406_generate_api_key_func/down.sql b/packages/api/hasura/migrations/default/1683384792406_generate_api_key_func/down.sql similarity index 100% rename from hasura/migrations/default/1683384792406_generate_api_key_func/down.sql rename to packages/api/hasura/migrations/default/1683384792406_generate_api_key_func/down.sql diff --git a/hasura/migrations/default/1683384792406_generate_api_key_func/up.sql b/packages/api/hasura/migrations/default/1683384792406_generate_api_key_func/up.sql similarity index 100% rename from hasura/migrations/default/1683384792406_generate_api_key_func/up.sql rename to packages/api/hasura/migrations/default/1683384792406_generate_api_key_func/up.sql diff --git a/hasura/migrations/default/1683384939518_create_table_public_apikeys/down.sql b/packages/api/hasura/migrations/default/1683384939518_create_table_public_apikeys/down.sql similarity index 100% rename from hasura/migrations/default/1683384939518_create_table_public_apikeys/down.sql rename to packages/api/hasura/migrations/default/1683384939518_create_table_public_apikeys/down.sql diff --git a/hasura/migrations/default/1683384939518_create_table_public_apikeys/up.sql b/packages/api/hasura/migrations/default/1683384939518_create_table_public_apikeys/up.sql similarity index 100% rename from hasura/migrations/default/1683384939518_create_table_public_apikeys/up.sql rename to packages/api/hasura/migrations/default/1683384939518_create_table_public_apikeys/up.sql diff --git a/hasura/migrations/default/1683385211855_create_table_public_uploads/down.sql b/packages/api/hasura/migrations/default/1683385211855_create_table_public_uploads/down.sql similarity index 100% rename from hasura/migrations/default/1683385211855_create_table_public_uploads/down.sql rename to packages/api/hasura/migrations/default/1683385211855_create_table_public_uploads/down.sql diff --git a/hasura/migrations/default/1683385211855_create_table_public_uploads/up.sql b/packages/api/hasura/migrations/default/1683385211855_create_table_public_uploads/up.sql similarity index 100% rename from hasura/migrations/default/1683385211855_create_table_public_uploads/up.sql rename to packages/api/hasura/migrations/default/1683385211855_create_table_public_uploads/up.sql diff --git a/hasura/migrations/default/1683385486210_run_sql_migration/down.sql b/packages/api/hasura/migrations/default/1683385486210_run_sql_migration/down.sql similarity index 100% rename from hasura/migrations/default/1683385486210_run_sql_migration/down.sql rename to packages/api/hasura/migrations/default/1683385486210_run_sql_migration/down.sql diff --git a/hasura/migrations/default/1683385486210_run_sql_migration/up.sql b/packages/api/hasura/migrations/default/1683385486210_run_sql_migration/up.sql similarity index 100% rename from hasura/migrations/default/1683385486210_run_sql_migration/up.sql rename to packages/api/hasura/migrations/default/1683385486210_run_sql_migration/up.sql diff --git a/hasura/migrations/default/1683385777576_run_sql_migration/down.sql b/packages/api/hasura/migrations/default/1683385777576_run_sql_migration/down.sql similarity index 100% rename from hasura/migrations/default/1683385777576_run_sql_migration/down.sql rename to packages/api/hasura/migrations/default/1683385777576_run_sql_migration/down.sql diff --git a/hasura/migrations/default/1683385777576_run_sql_migration/up.sql b/packages/api/hasura/migrations/default/1683385777576_run_sql_migration/up.sql similarity index 100% rename from hasura/migrations/default/1683385777576_run_sql_migration/up.sql rename to packages/api/hasura/migrations/default/1683385777576_run_sql_migration/up.sql diff --git a/hasura/migrations/default/1683386226447_run_sql_migration/down.sql b/packages/api/hasura/migrations/default/1683386226447_run_sql_migration/down.sql similarity index 100% rename from hasura/migrations/default/1683386226447_run_sql_migration/down.sql rename to packages/api/hasura/migrations/default/1683386226447_run_sql_migration/down.sql diff --git a/hasura/migrations/default/1683386226447_run_sql_migration/up.sql b/packages/api/hasura/migrations/default/1683386226447_run_sql_migration/up.sql similarity index 100% rename from hasura/migrations/default/1683386226447_run_sql_migration/up.sql rename to packages/api/hasura/migrations/default/1683386226447_run_sql_migration/up.sql diff --git a/hasura/migrations/default/1683386282101_run_sql_migration/down.sql b/packages/api/hasura/migrations/default/1683386282101_run_sql_migration/down.sql similarity index 100% rename from hasura/migrations/default/1683386282101_run_sql_migration/down.sql rename to packages/api/hasura/migrations/default/1683386282101_run_sql_migration/down.sql diff --git a/hasura/migrations/default/1683386282101_run_sql_migration/up.sql b/packages/api/hasura/migrations/default/1683386282101_run_sql_migration/up.sql similarity index 100% rename from hasura/migrations/default/1683386282101_run_sql_migration/up.sql rename to packages/api/hasura/migrations/default/1683386282101_run_sql_migration/up.sql diff --git a/hasura/migrations/default/1683386346978_run_sql_migration/down.sql b/packages/api/hasura/migrations/default/1683386346978_run_sql_migration/down.sql similarity index 100% rename from hasura/migrations/default/1683386346978_run_sql_migration/down.sql rename to packages/api/hasura/migrations/default/1683386346978_run_sql_migration/down.sql diff --git a/hasura/migrations/default/1683386346978_run_sql_migration/up.sql b/packages/api/hasura/migrations/default/1683386346978_run_sql_migration/up.sql similarity index 100% rename from hasura/migrations/default/1683386346978_run_sql_migration/up.sql rename to packages/api/hasura/migrations/default/1683386346978_run_sql_migration/up.sql diff --git a/hasura/migrations/default/1683386492737_run_sql_migration/down.sql b/packages/api/hasura/migrations/default/1683386492737_run_sql_migration/down.sql similarity index 100% rename from hasura/migrations/default/1683386492737_run_sql_migration/down.sql rename to packages/api/hasura/migrations/default/1683386492737_run_sql_migration/down.sql diff --git a/hasura/migrations/default/1683386492737_run_sql_migration/up.sql b/packages/api/hasura/migrations/default/1683386492737_run_sql_migration/up.sql similarity index 100% rename from hasura/migrations/default/1683386492737_run_sql_migration/up.sql rename to packages/api/hasura/migrations/default/1683386492737_run_sql_migration/up.sql diff --git a/hasura/migrations/default/1683386905959_create_table_public_shorturls/down.sql b/packages/api/hasura/migrations/default/1683386905959_create_table_public_shorturls/down.sql similarity index 100% rename from hasura/migrations/default/1683386905959_create_table_public_shorturls/down.sql rename to packages/api/hasura/migrations/default/1683386905959_create_table_public_shorturls/down.sql diff --git a/hasura/migrations/default/1683386905959_create_table_public_shorturls/up.sql b/packages/api/hasura/migrations/default/1683386905959_create_table_public_shorturls/up.sql similarity index 100% rename from hasura/migrations/default/1683386905959_create_table_public_shorturls/up.sql rename to packages/api/hasura/migrations/default/1683386905959_create_table_public_shorturls/up.sql diff --git a/hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/down.sql b/packages/api/hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/down.sql similarity index 100% rename from hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/down.sql rename to packages/api/hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/down.sql diff --git a/hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/up.sql b/packages/api/hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/up.sql similarity index 100% rename from hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/up.sql rename to packages/api/hasura/migrations/default/1683386943002_alter_table_public_shorturls_alter_column_creater_ip/up.sql diff --git a/hasura/migrations/default/1683387751628_create_table_public_gists/down.sql b/packages/api/hasura/migrations/default/1683387751628_create_table_public_gists/down.sql similarity index 100% rename from hasura/migrations/default/1683387751628_create_table_public_gists/down.sql rename to packages/api/hasura/migrations/default/1683387751628_create_table_public_gists/down.sql diff --git a/hasura/migrations/default/1683387751628_create_table_public_gists/up.sql b/packages/api/hasura/migrations/default/1683387751628_create_table_public_gists/up.sql similarity index 100% rename from hasura/migrations/default/1683387751628_create_table_public_gists/up.sql rename to packages/api/hasura/migrations/default/1683387751628_create_table_public_gists/up.sql diff --git a/hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/down.sql b/packages/api/hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/down.sql similarity index 100% rename from hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/down.sql rename to packages/api/hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/down.sql diff --git a/hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/up.sql b/packages/api/hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/up.sql similarity index 100% rename from hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/up.sql rename to packages/api/hasura/migrations/default/1683387777285_alter_table_public_gists_alter_column_gist_url_key/up.sql diff --git a/helpers/axios_client.ts b/packages/api/helpers/axios_client.ts similarity index 99% rename from helpers/axios_client.ts rename to packages/api/helpers/axios_client.ts index ff9ffab..ab93c0e 100644 --- a/helpers/axios_client.ts +++ b/packages/api/helpers/axios_client.ts @@ -28,4 +28,4 @@ axiosInstance.interceptors.response.use( newError.config.metadata.endTime - newError.config.metadata.startTime; return Promise.reject(newError); } -); \ No newline at end of file +); diff --git a/helpers/cache.factory.ts b/packages/api/helpers/cache.factory.ts similarity index 100% rename from helpers/cache.factory.ts rename to packages/api/helpers/cache.factory.ts diff --git a/helpers/gql_clent.ts b/packages/api/helpers/gql_clent.ts similarity index 100% rename from helpers/gql_clent.ts rename to packages/api/helpers/gql_clent.ts diff --git a/helpers/index.ts b/packages/api/helpers/index.ts similarity index 73% rename from helpers/index.ts rename to packages/api/helpers/index.ts index 4e36b92..3b531d1 100644 --- a/helpers/index.ts +++ b/packages/api/helpers/index.ts @@ -1,4 +1,4 @@ export * from './cache.factory'; export * from './gql_clent'; export * from './axios_client'; -export * from './upload.factory' \ No newline at end of file +export * from './upload.factory'; diff --git a/helpers/upload.factory.ts b/packages/api/helpers/upload.factory.ts similarity index 100% rename from helpers/upload.factory.ts rename to packages/api/helpers/upload.factory.ts diff --git a/index.ts b/packages/api/index.ts similarity index 96% rename from index.ts rename to packages/api/index.ts index 9ad4372..6ed508d 100644 --- a/index.ts +++ b/packages/api/index.ts @@ -5,7 +5,6 @@ import morgan from 'morgan'; import helmet from 'helmet'; import { hgqlInit } from './helpers'; -import routes from './routes'; import { errorHandler, notFoundHandler } from './libs'; import pkg from './package.json' assert { type: 'json' }; import configStore from './configs'; @@ -22,6 +21,10 @@ const configs = new configStore(isDev); const configKeys = await configs.getConfigStore(); const urlStoreController = new URLStoreController(); +console.log('🔑', 'Master Key', configKeys.MASTER_KEY); + +import routes from './routes'; + hgqlInit(); CacheClient.init(configKeys.CACHE_ENV as CacheEnvironment); diff --git a/interfaces/apikey.interface.d.ts b/packages/api/interfaces/apikey.interface.d.ts similarity index 100% rename from interfaces/apikey.interface.d.ts rename to packages/api/interfaces/apikey.interface.d.ts diff --git a/interfaces/config.interface.d.ts b/packages/api/interfaces/config.interface.d.ts similarity index 100% rename from interfaces/config.interface.d.ts rename to packages/api/interfaces/config.interface.d.ts diff --git a/interfaces/gists.interface.d.ts b/packages/api/interfaces/gists.interface.d.ts similarity index 100% rename from interfaces/gists.interface.d.ts rename to packages/api/interfaces/gists.interface.d.ts diff --git a/interfaces/sxcu.interface.d.ts b/packages/api/interfaces/sxcu.interface.d.ts similarity index 100% rename from interfaces/sxcu.interface.d.ts rename to packages/api/interfaces/sxcu.interface.d.ts diff --git a/interfaces/upload.interface.d.ts b/packages/api/interfaces/upload.interface.d.ts similarity index 100% rename from interfaces/upload.interface.d.ts rename to packages/api/interfaces/upload.interface.d.ts diff --git a/interfaces/urlstore.interface.d.ts b/packages/api/interfaces/urlstore.interface.d.ts similarity index 100% rename from interfaces/urlstore.interface.d.ts rename to packages/api/interfaces/urlstore.interface.d.ts diff --git a/libs/customErrHandler.ts b/packages/api/libs/customErrHandler.ts similarity index 100% rename from libs/customErrHandler.ts rename to packages/api/libs/customErrHandler.ts diff --git a/libs/error.ts b/packages/api/libs/error.ts similarity index 100% rename from libs/error.ts rename to packages/api/libs/error.ts diff --git a/libs/index.ts b/packages/api/libs/index.ts similarity index 100% rename from libs/index.ts rename to packages/api/libs/index.ts diff --git a/libs/middleware.ts b/packages/api/libs/middleware.ts similarity index 97% rename from libs/middleware.ts rename to packages/api/libs/middleware.ts index 9141ec4..845e2ac 100644 --- a/libs/middleware.ts +++ b/packages/api/libs/middleware.ts @@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express'; import Joi from 'joi'; import { CustomError, NotFoundError } from './error'; import { pick } from './utilities'; -import { configKeys } from ".." +import { configKeys } from '..'; export const errorHandler = async ( err: any, @@ -58,7 +58,7 @@ export const validate = }) ); } - + Object.assign(req, value); return next(); diff --git a/libs/utilities.ts b/packages/api/libs/utilities.ts similarity index 99% rename from libs/utilities.ts rename to packages/api/libs/utilities.ts index 401cd60..e268c4c 100644 --- a/libs/utilities.ts +++ b/packages/api/libs/utilities.ts @@ -142,4 +142,4 @@ export const getSortColumn = ( export const is_uuid = (value: string) => { const regex = /^()/; return regex.test(value); -}; \ No newline at end of file +}; diff --git a/middlewares/apikey_check.ts b/packages/api/middlewares/apikey_check.ts similarity index 100% rename from middlewares/apikey_check.ts rename to packages/api/middlewares/apikey_check.ts diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 0000000..96e9af3 --- /dev/null +++ b/packages/api/package.json @@ -0,0 +1,66 @@ +{ + "name": "shx-api", + "version": "1.0.0", + "description": "My ShareX Server acting as Daddy", + "main": "index.ts", + "repository": "https://github.com/bravo68web/shx.git", + "author": { + "email": "hi@b68.dev", + "name": "Jyotirmoy Bandyopadhayaya", + "url": "https://b68.dev" + }, + "license": "ISC", + "type": "module", + "private": true, + "dependencies": { + "@aws-sdk/client-s3": "^3.226.0", + "axios": "^1.2.1", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "envfile": "^6.18.0", + "express": "^4.18.2", + "form-data": "^4.0.0", + "graphql": "^16.6.0", + "graphql-request": "^5.0.0", + "helmet": "^6.0.1", + "joi": "^17.7.0", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "multer-s3": "^3.0.1", + "napi-nanoid": "^0.0.4", + "node-cache": "^5.1.2", + "nodemailer": "^6.8.0", + "redis": "^4.5.1", + "sharp": "^0.32.1" + }, + "scripts": { + "dev": "concurrently \"npm run dev:express\" \"npm run dev:hasura\"", + "dev:hasura": "cd hasura && hasura --skip-update-check --envfile ../.env console", + "dev:express": "cross-env NODE_ENV=development nodemon -x node --no-warnings --experimental-specifier-resolution=node --loader ts-node/esm index.ts --signal SIGKILL --ignore node_modules", + "build": "tsc -p tsconfig.json", + "start": "node --es-module-specifier-resolution=node --loader ts-node/esm ./build/index.js", + "fetch:schemas": "bash bin/fetch-gql-schema.sh" + }, + "devDependencies": { + "@swc/core": "^1.3.23", + "@swc/wasm": "^1.3.23", + "@types/cors": "^2.8.13", + "@types/express": "^4.17.14", + "@types/morgan": "^1.9.3", + "@types/multer": "^1.4.7", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", + "concurrently": "^7.6.0", + "cross-env": "^7.0.3", + "eslint": "^8.35.0", + "eslint-config-prettier": "^8.7.0", + "graphqurl": "^1.0.1", + "hasura-cli": "^2.15.1", + "husky": "^8.0.3", + "lint-staged": "^13.1.2", + "nodemon": "^2.0.22", + "prettier": "^2.8.2", + "ts-node": "^10.9.1", + "typescript": "^4.9.3" + } +} diff --git a/routes/apikey.routes.ts b/packages/api/routes/apikey.routes.ts similarity index 100% rename from routes/apikey.routes.ts rename to packages/api/routes/apikey.routes.ts diff --git a/routes/config.routes.ts b/packages/api/routes/config.routes.ts similarity index 100% rename from routes/config.routes.ts rename to packages/api/routes/config.routes.ts diff --git a/routes/gist.routes.ts b/packages/api/routes/gist.routes.ts similarity index 100% rename from routes/gist.routes.ts rename to packages/api/routes/gist.routes.ts diff --git a/routes/index.ts b/packages/api/routes/index.ts similarity index 93% rename from routes/index.ts rename to packages/api/routes/index.ts index 408db0d..b4eb733 100644 --- a/routes/index.ts +++ b/packages/api/routes/index.ts @@ -31,7 +31,10 @@ const loadRoutes = async (dirPath: string, prefix = '/') => { router.use(modRoute, mod.default); } } else if (f.isDirectory()) { - await loadRoutes(path.resolve(dirPath, f.name), prefix + f.name + '/'); + await loadRoutes( + path.resolve(dirPath, f.name), + path.join(prefix, f.name, '/') + ); } }); }; diff --git a/routes/upload.routes.ts b/packages/api/routes/upload.routes.ts similarity index 100% rename from routes/upload.routes.ts rename to packages/api/routes/upload.routes.ts diff --git a/routes/url.routes.ts b/packages/api/routes/url.routes.ts similarity index 100% rename from routes/url.routes.ts rename to packages/api/routes/url.routes.ts diff --git a/services/apikey.service.ts b/packages/api/services/apikey.service.ts similarity index 98% rename from services/apikey.service.ts rename to packages/api/services/apikey.service.ts index 55b6b87..481e1ef 100644 --- a/services/apikey.service.ts +++ b/packages/api/services/apikey.service.ts @@ -57,7 +57,6 @@ export default class APIKeyService implements IAPIKeyService { } public async listS(masterKey: string): Promise { - console.log(masterKey); if (masterKey !== configKeys.MASTER_KEY) throw new Error('Invalid master key'); const query = gql` diff --git a/services/gist.service.ts b/packages/api/services/gist.service.ts similarity index 100% rename from services/gist.service.ts rename to packages/api/services/gist.service.ts diff --git a/services/sxcu.service.ts b/packages/api/services/sxcu.service.ts similarity index 100% rename from services/sxcu.service.ts rename to packages/api/services/sxcu.service.ts diff --git a/services/upload.service.ts b/packages/api/services/upload.service.ts similarity index 94% rename from services/upload.service.ts rename to packages/api/services/upload.service.ts index b734f48..753252f 100644 --- a/services/upload.service.ts +++ b/packages/api/services/upload.service.ts @@ -9,11 +9,15 @@ import axios from 'axios'; import fs from 'fs'; import { nanoid } from 'napi-nanoid'; -const uploaderService = new UploaderService(configKeys.R2_BUCKET_NAME); - export default class Uploader implements IUploaderService { + uploaderService: UploaderService; + + constructor() { + this.uploaderService = new UploaderService(configKeys.R2_BUCKET_NAME); + } + public uploadS = async (file: any, meta: UserMeta) => { - await uploaderService.uploadFile( + await this.uploaderService.uploadFile( configKeys.R2_BUCKET_FOLDER!, file.newName, file.buffer, @@ -56,7 +60,7 @@ export default class Uploader implements IUploaderService { const image: any = sharp(file.buffer); await image.toFormat('jpeg'); const buffer: any = await image.toBuffer(); - await uploaderService.uploadFile( + await this.uploaderService.uploadFile( configKeys.R2_BUCKET_FOLDER!, file.newName, buffer, @@ -102,7 +106,7 @@ export default class Uploader implements IUploaderService { const image: any = sharp(rawImage); await image.toFormat('jpeg'); const buffer: any = await image.toBuffer(); - await uploaderService.uploadFile( + await this.uploaderService.uploadFile( configKeys.R2_BUCKET_FOLDER!, filename, buffer, @@ -167,7 +171,7 @@ export default class Uploader implements IUploaderService { public uploadFileViaURLS = async (url: string, meta: UserMeta) => { const filename = await this.downloadFile(url); - await uploaderService.uploadFile( + await this.uploaderService.uploadFile( configKeys.R2_BUCKET_FOLDER!, filename, fs.readFileSync(`uploads/${filename}`), diff --git a/services/urlstore.service.ts b/packages/api/services/urlstore.service.ts similarity index 100% rename from services/urlstore.service.ts rename to packages/api/services/urlstore.service.ts diff --git a/tsconfig.json b/packages/api/tsconfig.json similarity index 67% rename from tsconfig.json rename to packages/api/tsconfig.json index 0ec745e..8c33452 100644 --- a/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,22 +1,22 @@ { "compilerOptions": { "lib": ["es2018", "es5", "dom"], - "typeRoots": ["node_modules/@types", "./types"], + "typeRoots": ["node_modules/@types", "types"], "resolveJsonModule": true, "esModuleInterop": true, "target": "ES2017", "strict": true, "module": "ESNext", "moduleResolution": "node", - "outDir": "./build", + "outDir": "build", "emitDecoratorMetadata": true, "experimentalDecorators": true, "declaration": true, "sourceMap": false, "noImplicitAny": false }, - "exclude": ["./node_modules/**/*", "./build/**/*"], - "include": ["./**/*.ts", "./**/*.tsx", "./**/*.json", "./**/*.js"], + "exclude": ["node_modules/**/*", "build/**/*"], + "include": ["**/*.ts", "**/*.tsx", "**/*.json", "**/*.js"], "ts-node": { "swc": true }, diff --git a/types/index.d.ts b/packages/api/types/index.d.ts similarity index 100% rename from types/index.d.ts rename to packages/api/types/index.d.ts diff --git a/uploads/.gitkeep b/packages/api/uploads/.gitkeep similarity index 100% rename from uploads/.gitkeep rename to packages/api/uploads/.gitkeep diff --git a/packages/api/uploads/_7Igi_nHGwQ0vRScphQ1IIpfs-logo-1024-ice-text.png b/packages/api/uploads/_7Igi_nHGwQ0vRScphQ1IIpfs-logo-1024-ice-text.png new file mode 100644 index 0000000000000000000000000000000000000000..27a82629fafa46814cd814b7ed21d4c9c79a5d62 GIT binary patch literal 72110 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 { + return infoCMD(program); + }; +} diff --git a/packages/cli/src/commands/info.ts b/packages/cli/src/commands/info.ts new file mode 100644 index 0000000..41b61ed --- /dev/null +++ b/packages/cli/src/commands/info.ts @@ -0,0 +1,11 @@ +import { Command } from 'commander'; +import infoFn from '../functions/info'; + +export default (program: Command) => { + return program + .command('info') + .description('Show info about the CLI') + .action(() => { + infoFn(); + }); +}; diff --git a/packages/cli/src/functions/info.ts b/packages/cli/src/functions/info.ts new file mode 100644 index 0000000..4ce4557 --- /dev/null +++ b/packages/cli/src/functions/info.ts @@ -0,0 +1,4 @@ +export default () => { + console.log('This is the CLI for SHX API !!'); + return; +}; diff --git a/packages/cli/src/shx.ts b/packages/cli/src/shx.ts new file mode 100644 index 0000000..59a72a1 --- /dev/null +++ b/packages/cli/src/shx.ts @@ -0,0 +1,9 @@ +#!/usr/bin/env node +import { Command } from 'commander'; +import { loadCommads } from './commands'; + +const program = new Command(); + +new loadCommads(program); + +program.parse(process.argv); diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 0000000..7fa1652 --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["es2018", "es5", "dom"], + "typeRoots": ["node_modules/@types", "types"], + "resolveJsonModule": true, + "esModuleInterop": true, + "target": "ES2017", + "strict": true, + "module": "commonjs", + "moduleResolution": "node", + "outDir": "build", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "declaration": true, + "sourceMap": false, + "noImplicitAny": false, + "types": ["node"] + }, + "exclude": ["node_modules/**/*", "build/**/*"], + "include": ["**/*.ts", "**/*.tsx", "**/*.json", "**/*.js"], + "ts-node": { + "swc": true + }, +} diff --git a/packages/frontend/.eslintrc.json b/packages/frontend/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/packages/frontend/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/frontend/.gitignore b/packages/frontend/.gitignore new file mode 100644 index 0000000..8f322f0 --- /dev/null +++ b/packages/frontend/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/frontend/README.md b/packages/frontend/README.md new file mode 100644 index 0000000..965a122 --- /dev/null +++ b/packages/frontend/README.md @@ -0,0 +1,38 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/packages/frontend/next.config.js b/packages/frontend/next.config.js new file mode 100644 index 0000000..7ca34f0 --- /dev/null +++ b/packages/frontend/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +module.exports = nextConfig; diff --git a/packages/frontend/package.json b/packages/frontend/package.json new file mode 100644 index 0000000..40b87e6 --- /dev/null +++ b/packages/frontend/package.json @@ -0,0 +1,25 @@ +{ + "name": "frontend", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@types/node": "20.1.5", + "@types/react": "18.2.6", + "@types/react-dom": "18.2.4", + "autoprefixer": "10.4.14", + "eslint": "8.40.0", + "eslint-config-next": "13.4.2", + "next": "13.4.2", + "postcss": "8.4.23", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwindcss": "3.3.2", + "typescript": "5.0.4" + } +} diff --git a/packages/frontend/pages/_app.tsx b/packages/frontend/pages/_app.tsx new file mode 100644 index 0000000..3b2235d --- /dev/null +++ b/packages/frontend/pages/_app.tsx @@ -0,0 +1,6 @@ +import '/styles/globals.css' +import type { AppProps } from 'next/app' + +export default function App({ Component, pageProps }: AppProps) { + return +} diff --git a/packages/frontend/pages/_document.tsx b/packages/frontend/pages/_document.tsx new file mode 100644 index 0000000..54e8bf3 --- /dev/null +++ b/packages/frontend/pages/_document.tsx @@ -0,0 +1,13 @@ +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + +
+ + + + ) +} diff --git a/packages/frontend/pages/api/hello.ts b/packages/frontend/pages/api/hello.ts new file mode 100644 index 0000000..5b60947 --- /dev/null +++ b/packages/frontend/pages/api/hello.ts @@ -0,0 +1,13 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from 'next'; + +type Data = { + name: string; +}; + +export default function handler( + req: NextApiRequest, + res: NextApiResponse +) { + res.status(200).json({ name: 'John Doe' }); +} diff --git a/packages/frontend/pages/index.tsx b/packages/frontend/pages/index.tsx new file mode 100644 index 0000000..14f2678 --- /dev/null +++ b/packages/frontend/pages/index.tsx @@ -0,0 +1,118 @@ +import Image from 'next/image' +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function Home() { + return ( +
+
+

+ Get started by editing  + pages/index.tsx +

+ +
+ +
+ Next.js Logo +
+ + +
+ ) +} diff --git a/packages/frontend/postcss.config.js b/packages/frontend/postcss.config.js new file mode 100644 index 0000000..e873f1a --- /dev/null +++ b/packages/frontend/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/frontend/public/favicon.ico b/packages/frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4570eb8d9269ad58b17fecbec6d630cded56f507 GIT binary patch literal 39535 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 literal 0 HcmV?d00001 diff --git a/packages/frontend/public/next.svg b/packages/frontend/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/packages/frontend/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/frontend/public/vercel.svg b/packages/frontend/public/vercel.svg new file mode 100644 index 0000000..d2f8422 --- /dev/null +++ b/packages/frontend/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/frontend/styles/globals.css b/packages/frontend/styles/globals.css new file mode 100644 index 0000000..fd81e88 --- /dev/null +++ b/packages/frontend/styles/globals.css @@ -0,0 +1,27 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} diff --git a/packages/frontend/tailwind.config.js b/packages/frontend/tailwind.config.js new file mode 100644 index 0000000..0dceef8 --- /dev/null +++ b/packages/frontend/tailwind.config.js @@ -0,0 +1,18 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': + 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + }, + }, + }, + plugins: [], +}; diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json new file mode 100644 index 0000000..9be0fa4 --- /dev/null +++ b/packages/frontend/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "paths": { + "*/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +}