diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..eb116d5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,79 @@ +name: "🐛 Bug Report" +description: Create a new ticket for a bug. +title: "🐛 [BUG] - " +labels: ["bug"] +assignees: devarshishimpi +body: + - type: textarea + id: description + attributes: + label: "Description" + description: Please enter an explicit description of your issue + placeholder: Short and explicit description of your incident... + validations: + required: true + - type: input + id: reprod-url + attributes: + label: "Reproduction URL" + description: Please enter your GitHub URL to provide a reproduction of the issue + placeholder: ex. https://github.com/USERNAME/REPO-NAME + validations: + required: true + - type: textarea + id: reprod + attributes: + label: "Reproduction steps" + description: Please enter an explicit description of your issue + value: | + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + render: bash + validations: + required: true + - type: textarea + id: screenshot + attributes: + label: "Screenshots" + description: If applicable, add screenshots to help explain your problem. + value: | + ![DESCRIPTION](LINK.png) + render: bash + validations: + required: false + - type: textarea + id: logs + attributes: + label: "Logs" + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: bash + validations: + required: false + - type: dropdown + id: browsers + attributes: + label: "Browsers" + description: What browsers are you seeing the problem on ? + multiple: true + options: + - Firefox + - Chrome + - Safari + - Microsoft Edge + - Opera + validations: + required: false + - type: dropdown + id: os + attributes: + label: "OS" + description: What is the impacted environment ? + multiple: true + options: + - Windows + - Linux + - Mac + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/contact-support.yml b/.github/ISSUE_TEMPLATE/contact-support.yml new file mode 100644 index 0000000..55b7111 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/contact-support.yml @@ -0,0 +1,51 @@ +name: Contact Support +description: Contact support and get help +title: Contact Support +labels: "support" +assignees: devarshishimpi +body: + - type: markdown + attributes: + value: | + # Contact Support + ## If you need support from staff but don't want to create it publicly, you may contact at devarshishimpi@gmail.com + - type: textarea + id: description + attributes: + label: Description + description: Describe your issue or question here + placeholder: Tell us what you need help with + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + placeholder: Add screenshots here + validations: + required: false + - type: textarea + id: logs + attributes: + label: Logs + description: If applicable, add logs to help explain your problem. + placeholder: Add logs here + validations: + required: false + - type: input + id: contact + attributes: + label: Contact Details + description: How can we get in touch with you? + placeholder: ex. + validations: + required: true + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..2f065ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,62 @@ +name: "💡 Feature Request" +description: Create a new ticket for a new feature request +title: "💡 [REQUEST] - <title>" +labels: ["question"] +assignees: devarshishimpi +body: + - type: input + id: start_date + attributes: + label: "Start Date" + description: Start of development + placeholder: "month/day/year" + validations: + required: false + - type: textarea + id: implementation_pr + attributes: + label: "Implementation PR" + description: Pull request used + placeholder: "#Pull Request ID" + validations: + required: false + - type: textarea + id: reference_issues + attributes: + label: "Reference Issues" + description: Common issues + placeholder: "#Issues IDs" + validations: + required: false + - type: textarea + id: summary + attributes: + label: "Summary" + description: Provide a brief explanation of the feature + placeholder: Describe in a few lines your feature request + validations: + required: true + - type: textarea + id: basic_example + attributes: + label: "Basic Example" + description: Indicate here some basic examples of your feature. + placeholder: A few specific words about your feature request. + validations: + required: true + - type: textarea + id: drawbacks + attributes: + label: "Drawbacks" + description: What are the drawbacks/impacts of your feature request ? + placeholder: Identify the drawbacks and impacts while being neutral on your feature request + validations: + required: true + - type: textarea + id: unresolved_question + attributes: + label: "Unresolved questions" + description: What questions still remain unresolved ? + placeholder: Identify any unresolved issues. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml new file mode 100644 index 0000000..06d0aa4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.yml @@ -0,0 +1,23 @@ +name: Other +description: Describe anything here. +title: Other <subject> +labels: [question, others] +assignees: devarshishimpi +body: + - type: markdown + attributes: + value: "# Other issue" + - type: textarea + id: issuedescription + attributes: + label: What would you like to share? + description: Provide a clear and concise explanation of your issue. + validations: + required: true + - type: textarea + id: extrainfo + attributes: + label: Additional information + description: Is there anything else I should know about this issue? + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..2265cff --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ +## Describe your changes + +## Screenshots - If Any (Optional) + +## Issue ticket number and link - If Any + +## Checklist before requesting a review + +- [ ] I have performed a self-review of my code. + +- [ ] Followed the repository's [Contributing Guidelines](https://github.com/devarshishimpi/staticstorm/blob/main/CONTRIBUTING.md). + +- [ ] I ran the app and tested it locally to verify that it works as expected. + +- [ ] I have starred the repository. + +## Additional Information (Optional) diff --git a/.github/workflows/create-comment-issues-pr.yml b/.github/workflows/create-comment-issues-pr.yml new file mode 100644 index 0000000..1613504 --- /dev/null +++ b/.github/workflows/create-comment-issues-pr.yml @@ -0,0 +1,23 @@ +name: Create comment on Issue/PR + +on: + issues: + types: [opened] + issue_comment: + types: [created] + pull_request_target: + types: [opened] + pull_request_review_comment: + types: [created] + +jobs: + welcome: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: EddieHubCommunity/gh-action-community/src/welcome@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: "Hey @${{ github.actor }}, Thanks for contributing and Congrats on opening Issue :tada:" + pr-message: "Hey @${{ github.actor }}, Thanks for contributing and Congrats on opening PR :tada:." + footer: "We will try to review as soon as possible and a maintainer will get back to you soon!" diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1b8ac88 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +# Ignore artifacts: +build +coverage diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 01612ef..e9480d1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b869a76..dd81382 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,19 @@ -MIT License +# Contributing -Copyright (c) 2023 Devarshi Shimpi +When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Please note we have a [Code of Conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +## Issue Process -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +1. Before creating a new issue, search existing issues to ensure your topic or concern hasn't been discussed before. If you find a related issue, you can join the discussion there. +2. If you can't find an existing issue, create a new one, providing a clear and descriptive title, as well as a detailed description of the problem or feature request. +3. Engage in constructive discussions on the issue, providing additional context or information when needed. + +## Pull Request Process + +1. Make sure that you are assigned to the GitHub issue related to your Pull Request (PR). +2. Ensure any install or build dependencies are removed before the end of the layer when doing a build. +3. Update `README.md` with details of changes to the interface: this includes new environment variables, exposed ports, useful file locations, and container parameters. +4. Increase the version numbers in any examples files and the `README.md` to the new version that this PR would represent. +5. You may merge the PR once you have gained approval, or you may request approval from the maintainers who have write access to the repository. diff --git a/README.md b/README.md index 1a127f8..61e71c0 100644 --- a/README.md +++ b/README.md @@ -6,111 +6,114 @@ <hr> +<!-- Visit At <a href="http://staticstorm.coderush.tech" target="_blank">staticstorm.coderush.tech</a> +--> -## Deployment +## 🚀 Getting Started -### Deploying Backend +### Deployment -To deploy the backend. Make a new Linux Linode. Install Nginx and NodeJS. Then run the following commands. +To deploy both the frontend and backend, follow these steps: -<hr> +#### Prerequisites -```bash - git clone https://github.com/devarshishimpi/staticstormhackathon -``` +Before you begin, ensure that you have the following prerequisites in place: -```bash - cd backend -``` +- Linux Machine (eg: Ubuntu) +- Nodejs v16 or above +- Nginx Server -```bash - npm install -``` +#### Once you are done with that you may proceed to deploy the application: -```bash - sudo cp -rf . /var/www/html -``` +1. Clone the project repository: ```bash - sudo vim /etc/nginx/sites-available/default +git clone https://github.com/devarshishimpi/staticstorm +cd staticstorm ``` -Edit the file and change the following section to this - -```nginx -server { - listen 80; - server_name _; - - location / { - proxy_pass http://localhost:8181; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } -} -``` +2. Navigate to the frontend directory: ```bash - sudo service nginx restart +cd frontend ``` - -Then go to `http://yourip:30001` +3. Install dependencies: +```bash +npm install +``` -### Deploying Frontend - -To deploy the frontend. Make a new Linux Linode. Install Nginx and NodeJS. Then run the following commands. - -<hr> +4. Build the frontend: ```bash - git clone https://github.com/devarshishimpi/staticstormhackathon +npm run build ``` +5. Copy the frontend files to the server's HTML directory: +6. + ```bash - cd frontend +sudo cp -rf build /var/www/html ``` +6. Navigate to the server directory: + ```bash - npm install +cd ../server ``` +7. Install backend dependencies: + ```bash - npm run build +npm install ``` +8. Copy the backend files to the server's HTML directory: + ```bash - sudo cp -rf build /var/www/html +sudo cp -rf . /var/www/html ``` +9. Edit the Nginx configuration file: + ```bash - sudo vim /etc/nginx/sites-available/default +sudo vim /etc/nginx/sites-available/default ``` -Edit the file and change the following section to this +10. Update the Nginx configuration to include both frontend and backend as separate locations: ```nginx - server { - listen 80 default_server; - listen [::]:80 default_server; +server { + listen 80 default_server; + listen [::]:80 default_server; - root /var/www/html/build; + root /var/www/html; - index index.html index.htm index.nginx-debian.html; + index index.html index.htm index.nginx-debian.html; - server_name _; + server_name _; - location / { - try_files $uri $uri/ /index.html; - } + location / { + try_files $uri $uri/ /index.html; } + + location /api/ { + proxy_pass http://localhost:8181; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} ``` +11. Restart Nginx to apply the configuration changes\*\*: + ```bash - sudo service nginx restart +sudo service nginx restart ``` -Navigate to `http://youripaddress` \ No newline at end of file +12. Access Your Application + +You can now access your application in a web browser by navigating to `http://youripaddress`. The frontend will be served from the root, and the backend API will be available at `http://youripaddress/api`. diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index 30bc162..0000000 --- a/backend/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/node_modules \ No newline at end of file diff --git a/backend/db.js b/backend/db.js deleted file mode 100644 index 2b3faab..0000000 --- a/backend/db.js +++ /dev/null @@ -1,13 +0,0 @@ -require('dotenv').config(); - -const mongoose = require('mongoose'); - -const mongoURI = process.env.mongoURI; - -const connectToMongo = () => { - mongoose.connect(mongoURI, { dbName: 'staticstorm' }, () => { - console.log("Connected To Mongo Successfully!!"); - }) -} - -module.exports = connectToMongo; diff --git a/backend/index.js b/backend/index.js deleted file mode 100644 index ec521c7..0000000 --- a/backend/index.js +++ /dev/null @@ -1,25 +0,0 @@ -const express = require('express'); -const connectToMongo = require('./db'); -const cors = require('cors'); - -connectToMongo(); - -const app = express(); -app.use(express.json()); -app.use(cors()); - -const PORT = process.env.PORT || 8181; - - -app.get('/', (req, res) => { - res.send("Hi!!"); -}); - -// Available Routes -app.use('/api/auth', require('./routes/auth')); -app.use('/api/deploy', require('./routes/deploy')); -app.use('/api/projects', require('./routes/projects')); - -app.listen(PORT, () => { - console.log(`Server started at http://localhost:${PORT}`); -}); diff --git a/backend/models/Port.js b/backend/models/Port.js deleted file mode 100644 index 29f5b8d..0000000 --- a/backend/models/Port.js +++ /dev/null @@ -1,11 +0,0 @@ -const mongoose = require('mongoose'); -const { Schema } = require('mongoose'); - -const PortSchema = new Schema({ - nextFreePort: { - type: Number, - required: true - } -}, {timestamps: true}); - -module.exports = mongoose.model('port', PortSchema); diff --git a/backend/models/Project.js b/backend/models/Project.js deleted file mode 100644 index 261773b..0000000 --- a/backend/models/Project.js +++ /dev/null @@ -1,35 +0,0 @@ -const mongoose = require('mongoose'); -const { Schema } = require('mongoose'); - -const ProjectSchema = new Schema({ - name: { - type: String, - required: true - }, - framework: { - type: String, - required: true - }, - rootDirectory: { - type: String, - required: true - }, - packageManager: { - type: String, - required: true - }, - githubRepoId: { - type: String, - required: true - }, - ownerId: { - type: String, - required: true - }, - port: { - type: Number, - required: true - } -}, {timestamps: true}); - -module.exports = mongoose.model('project', ProjectSchema); diff --git a/backend/models/User.js b/backend/models/User.js deleted file mode 100644 index dcbefe7..0000000 --- a/backend/models/User.js +++ /dev/null @@ -1,23 +0,0 @@ -const mongoose = require('mongoose'); -const { Schema } = require('mongoose'); - -const UserSchema = new Schema({ - name: { - type: String, - required: true - }, - githubId: { - type: String, - required: true - }, - githubLogin: { - type: String, - }, - projects: { // An array of project ids of the user - type: Array, - default: [] - }, - -}, {timestamps: true}); - -module.exports = mongoose.model('user', UserSchema); \ No newline at end of file diff --git a/backend/routes/auth.js b/backend/routes/auth.js deleted file mode 100644 index 197bed6..0000000 --- a/backend/routes/auth.js +++ /dev/null @@ -1,109 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const UserSchema = require('../models/User'); -// const { body, validationResult } = require('express-validator'); -var bcrypt = require('bcryptjs'); -var jwt = require('jsonwebtoken'); -// const fetchuser = require('../middleware/fetchuser'); -const axios = require('axios'); -const qs = require('querystring'); -require('dotenv').config(); - -const JWT_SECRET = process.env.JWT_SECRET; - - - - - - - - - -router.post('/', async (req, res) => { - axios.get( - 'https://api.github.com/user', - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // res.json(response.data); - const theUser = await UserSchema.findOne({ githubId: response.data.id }); - if (!theUser) { - // Create new account here!! - const newUser = await UserSchema.create({ - name: response.data.name, - githubId: response.data.id, - githubLogin: response.data.login - }); - - return res.status(200).json({ success: true }); - } - else { - // Logged in Successfully Here - return res.status(200).json({ success: true }); - } - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.get('/github', (req, res) => { - const code = req.query.code; - const clientId = process.env.GITHUB_CLIENT_ID; - const clientSecret = process.env.GITHUB_CLIENT_SECRET; - - axios.post( - 'https://github.com/login/oauth/access_token', - qs.stringify({ - client_id: clientId, - client_secret: clientSecret, - code: code - }), - { - headers: { - Accept: 'application/json' - } - } - ).then(response => { - const accessToken = response.data.access_token; - return res.redirect(`http://staticstorm.coderush.tech/verifyLogin?access_token=${accessToken}`); - }) - .catch(error => { - console.error(error); - res.send('An error occurred'); - }); -}); - - -module.exports = router; diff --git a/backend/routes/deploy.js b/backend/routes/deploy.js deleted file mode 100644 index 87e36bb..0000000 --- a/backend/routes/deploy.js +++ /dev/null @@ -1,1065 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const UserSchema = require('../models/User'); -const ProjectSchema = require('../models/Project'); -const PortSchema = require('../models/Port'); -// const { body, validationResult } = require('express-validator'); -var bcrypt = require('bcryptjs'); -var jwt = require('jsonwebtoken'); -// const fetchuser = require('../middleware/fetchuser'); -const axios = require('axios'); -const qs = require('querystring'); -const { spawn } = require('child_process'); -const { promises: fs } = require('fs'); -require('dotenv').config(); - -const JWT_SECRET = process.env.JWT_SECRET; - - - - - - - - - -router.post('/', async (req, res) => { - const { frameworkPreset, rootDirectory, subDomain, packageManager, githubRepoId } = req.body; - - axios.get( - 'https://api.github.com/user', - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // res.json(response.data); - const theUser = await UserSchema.findOne({ githubId: response.data.id }); - if (!theUser) { - return res.status(400).json({ success: false, error: "User don't exist" }); - } - else { - const alreadyExist = await ProjectSchema.findOne({ name: subDomain }); - if (alreadyExist) return res.status(401).json({ error: "Domain Unavailable" }); - const portArray = await PortSchema.find(); - const port = portArray[0].nextFreePort; - - const project = await ProjectSchema.create({ - name: subDomain, - packageManager, - framework: frameworkPreset, - rootDirectory, - ownerId: theUser.githubId, - githubRepoId, - port - }); - return res.status(200).json({ success: true, projectId: project._id }); - } - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// router.post('/clone', async (req, res) => { -// const theProject = await ProjectSchema.findById(req.body.projectId); -// axios.get( -// `https://api.github.com/repositories/${theProject.githubRepoId}`, -// { -// headers: { -// 'Authorization': `Token ${req.body.accessToken}` -// } -// } -// ).then(async response => { -// // console.log(response.data); -// process.chdir('/Users/devarshishimpi/Downloads/'); - -// const gitClone = spawn('git', ['clone', `https://${req.body.accessToken}@github.com/${response.data.full_name}.git`]); - -// let logs = ''; - -// gitClone.stdout.on('data', data => { -// logs += data.toString(); -// }); - -// gitClone.stderr.on('data', data => { -// logs += data.toString(); -// }); - -// gitClone.on('close', code => { -// if (code === 0) { -// return res.status(200).json({ -// logs, -// message: 'Repository cloned successfully' -// }); -// } else { -// return res.status(500).json({ -// logs, -// message: 'Failed to clone repository' -// }); -// } -// }); -// }) -// .catch(error => { -// console.error(error); -// res.json({ success: false }); -// }); -// }); - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/clone', async (req, res) => { - if (req.body.projectId) { - const theProject = await ProjectSchema.findById(req.body.projectId); - axios.get( - `https://api.github.com/repositories/${theProject.githubRepoId}`, - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // console.log(response.data); - const folderName = theProject.name; // name of the new folder - const path = '/projects'; // path to the parent directory of the new folder - const folderPath = `${path}/${folderName}`; // full path to the new folder - const mkdir = spawn('mkdir', [folderPath]); // create the new folder - - mkdir.on('close', () => { - process.chdir(folderPath); // change the current working directory to the new folder - const gitClone = spawn('git', ['clone', `https://${req.body.accessToken}@github.com/${response.data.full_name}.git`]); - - let logs = ''; - - gitClone.stdout.on('data', data => { - logs += data.toString(); - }); - - gitClone.stderr.on('data', data => { - logs += data.toString(); - }); - - gitClone.on('close', code => { - if (code === 0) { - return res.status(200).json({ - logs, - message: 'Repository cloned successfully' - }); - } else { - return res.status(500).json({ - logs, - message: 'Failed to clone repository' - }); - } - }); - }); - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); - } - else { - res.send("Internal server error!"); - } -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/install', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - axios.get( - `https://api.github.com/repositories/${theProject.githubRepoId}`, - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // console.log(response.data); - process.chdir(`/projects/${theProject.name}/${response.data.name}`); - - const install = spawn('npm', ['install', '--legacy-peer-deps', '-C', theProject.rootDirectory]); - - let logs = ''; - - install.stdout.on('data', data => { - logs += data.toString(); - }); - - install.stderr.on('data', data => { - logs += data.toString(); - }); - - install.on('close', code => { - if (code === 0) { - return res.status(200).json({ - logs, - message: 'Dependencies installed successfully' - }); - } else { - return res.status(500).json({ - logs, - message: 'Failed to install dependencies' - }); - } - }); - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/build', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - axios.get( - `https://api.github.com/repositories/${theProject.githubRepoId}`, - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // console.log(response.data); - process.chdir(`/projects/${theProject.name}/${response.data.name}`); - - const build = spawn('npm', ['run', 'build', '-C', theProject.rootDirectory]); - - let logs = ''; - - build.stdout.on('data', data => { - logs += data.toString(); - }); - - build.stderr.on('data', data => { - logs += data.toString(); - }); - - build.on('close', code => { - if (code === 0) { - return res.status(200).json({ - logs, - message: 'Build successful' - }); - } else { - return res.status(500).json({ - logs, - message: 'Build Failed' - }); - } - }); - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/copybuild', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - axios.get( - `https://api.github.com/repositories/${theProject.githubRepoId}`, - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // console.log(response.data); - const folderName = theProject.name; // name of the new folder - const path = '/var/www/html'; // path to the parent directory of the new folder - const folderPath = `${path}/${folderName}`; // full path to the new folder - const mkdir = spawn('mkdir', [folderPath]); // create the new folder - - mkdir.on('close', () => { - process.chdir(`/projects/${theProject.name}/${response.data.name}`); // change the current working directory to the new folder - process.chdir(theProject.rootDirectory); // change the current working directory to the new folder - const copyBuild = spawn('cp', ['-rf', 'build', folderPath]); - - let logs = ''; - - copyBuild.stdout.on('data', data => { - logs += data.toString(); - }); - - copyBuild.stderr.on('data', data => { - logs += data.toString(); - }); - - copyBuild.on('close', code => { - if (code === 0) { - return res.status(200).json({ - logs, - message: 'Copied Successfully' - }); - } else { - return res.status(500).json({ - logs, - message: 'Failed to copy' - }); - } - }); - }); - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/nginxconf', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - axios.get( - `https://api.github.com/repositories/${theProject.githubRepoId}`, - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - const nextFreePortArray = await PortSchema.find(); - const nextFreePort = nextFreePortArray[0].nextFreePort; - - const filePath = '/etc/nginx/sites-available/default'; - const newServerBlock1 = ` - server { - listen ${nextFreePort} default_server; - listen [::]:${nextFreePort} default_server; - - root /var/www/html/${theProject.name}/build; - - index index.html index.htm index.nginx-debian.html; - - server_name ${theProject.name}.staticstorm.coderush.tech; - - location / { - try_files $uri $uri/ /index.html; - } - } - `; - const newServerBlock2 = ` - server { - listen 80; - server_name ${theProject.name}.staticstorm.coderush.tech; - - location / { - proxy_pass http://localhost:${nextFreePort}; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - } - `; - - - fs.readFile(filePath, 'utf-8') - .then((data) => { - const newData = data + newServerBlock1 + newServerBlock2; - return fs.writeFile(filePath, newData, 'utf-8'); - }) - .then(() => { - console.log('File updated!'); - }) - .catch((err) => { - console.error(err); - }); - - - const updatePort = await PortSchema.updateOne({ nextFreePort: nextFreePort }, { nextFreePort: nextFreePort+1 }); - - res.json({ success: true }); - - - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/deleteconf', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - const nginxConfigPath = '/etc/nginx/sites-available/default'; - const projectToRemove = `${theProject.name}.staticstorm.coderush.tech`; - - try { - // Read the content of the nginx configuration file - let nginxConfig = await fs.readFile(nginxConfigPath, 'utf-8'); - // console.log(`Read nginx config: ${nginxConfig}`); - - // Find the two server blocks corresponding to the project to remove - const serverBlockRegex = new RegExp(`\\s*server {\\s*listen 80;\\s*server_name ${projectToRemove};\\s*location / {\\s*proxy_pass http://localhost:${theProject.port};[^}]*}\\s*}`, 'g'); - const matches = nginxConfig.match(serverBlockRegex) || []; - console.log(`Found ${matches.length} matches: ${matches}`); - - const serverBlocksToRemove = matches.join(''); - - // Remove the server blocks from the nginx configuration file content - nginxConfig = nginxConfig.replace(serverBlocksToRemove, ''); - // console.log(`Modified nginx config: ${nginxConfig}`); - - // Write the modified content back to the nginx configuration file - await fs.writeFile(nginxConfigPath, nginxConfig); - // console.log(`Wrote modified nginx config to ${nginxConfigPath}`); - - console.log(`Removed server blocks for project "${projectToRemove}" from nginx config.`); - } catch (err) { - console.error(`Error removing project "${projectToRemove}" from nginx config: ${err}`); - return res.json({ success: false, err }); - } - - - - - - - try { - // Read the content of the nginx configuration file - let nginxConfig = await fs.readFile(nginxConfigPath, 'utf-8'); - - // Find the server block corresponding to the project to remove - const serverBlockRegex = new RegExp(`\\s*server {\\s*listen ${theProject.port} default_server;\\s*listen \\[::\\]:${theProject.port} default_server;\\s*root /var/www/html/${theProject.name}/build;\\s*index index.html index.htm index.nginx-debian.html;\\s*server_name ${projectToRemove};\\s*location / {\\s*try_files \\$uri \\$uri/ /index.html;\\s*}\\s*}`, 'g'); - const matches = nginxConfig.match(serverBlockRegex) || []; - - // Remove the server block from the nginx configuration file content - const serverBlockToRemove = matches.join(''); - nginxConfig = nginxConfig.replace(serverBlockToRemove, ''); - - // Write the modified content back to the nginx configuration file - await fs.writeFile(nginxConfigPath, nginxConfig); - - console.log(`Removed server block for project "${projectToRemove}" from nginx config.`); - - return res.json({ success: true }); - - } catch (err) { - console.error(`Error removing project "${projectToRemove}" from nginx config: ${err}`); - return res.json({ success: false, err }); - } - - -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/deleteproject', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - - await fs.rmdir(`/projects/${theProject.name}`, { recursive: true }); - await fs.rmdir(`/var/www/html/${theProject.name}`, { recursive: true }); - - if (theProject) { - await theProject.delete(); - return res.json({ success: true }); - } - res.json({ success: false }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/configurewebhook', async (req, res) => { - const theProject = await ProjectSchema.findById(req.body.projectId); - const theUser = await UserSchema.findOne({ githubId: theProject.ownerId }); - // Define the repository owner, repository name, and access token - const owner = theUser.githubLogin; - const repoId = theProject.githubRepoId; - const accessToken = req.body.accessToken; - - - // Make the HTTP request to get the repository information - axios.get(`https://api.github.com/repositories/${repoId}`, { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'User-Agent': 'axios', - }, - }) - .then(response => { - const repoName = response.data.name; - - // Define the payload for the webhook creation request - const payload = { - name: 'web', - config: { - url: 'http://abcd.staticstorm.coderush.tech/api/deploy/triggerwebhook', - content_type: 'json', - }, - events: ['push'], - active: true, - }; - - // Make the HTTP request to create the webhook - axios.post(`https://api.github.com/repos/${owner}/${repoName}/hooks`, payload, { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${accessToken}`, - 'User-Agent': 'axios', - }, - }) - .then(response => { - console.log(`Webhook created: ${response.data.url}`); - res.json({ success: true }); - }) - .catch(error => { - console.error(`Error creating webhook: ${error}`); - res.json({ success: false }); - }); - }) - .catch(error => { - console.error(`Error getting repository information: ${error}`); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/triggerwebhook', async (req, res) => { - // Retrieve the payload data from the request body - const payload = req.body; - console.log(`Received webhook for ${payload.repository.full_name}`); - - - const theProject = await ProjectSchema.findOne({ githubRepoId: payload.repository.id }); - - - process.chdir(`/projects/${theProject.name}/${payload.repository.name}`); - - const pull = spawn('git', ['pull']); - - let logs1 = ''; - - pull.stdout.on('data', data => { - logs1 += data.toString(); - }); - - pull.stderr.on('data', data => { - logs1 += data.toString(); - }); - - pull.on('close', code => { - console.log(logs1); - if (code === 0) { - - - - - - - process.chdir(`/projects/${theProject.name}/${payload.repository.name}`); - - const install = spawn('npm', ['install', '--legacy-peer-deps', '-C', theProject.rootDirectory]); - - let logs2 = ''; - - install.stdout.on('data', data => { - logs2 += data.toString(); - }); - - install.stderr.on('data', data => { - logs2 += data.toString(); - }); - - install.on('close', code => { - console.log(logs2); - if (code === 0) { - - - - - - - - - process.chdir(`/projects/${theProject.name}/${payload.repository.name}`); - - const build = spawn('npm', ['run', 'build', '-C', theProject.rootDirectory]); - - let logs4 = ''; - - build.stdout.on('data', data => { - logs4 += data.toString(); - }); - - build.stderr.on('data', data => { - logs4 += data.toString(); - }); - - build.on('close', code => { - console.log(logs4); - if (code === 0) { - - - - - - - - - - const folderName = theProject.name; // name of the new folder - const path = '/var/www/html'; // path to the parent directory of the new folder - const folderPath = `${path}/${folderName}`; // full path to the new folder - const mkdir = spawn('mkdir', [folderPath]); // create the new folder - - mkdir.on('close', () => { - process.chdir(`/projects/${theProject.name}/${payload.repository.name}`); // change the current working directory to the new folder - process.chdir(theProject.rootDirectory); // change the current working directory to the new folder - const copyBuild = spawn('cp', ['-rf', 'build', folderPath]); - - let logs3 = ''; - - copyBuild.stdout.on('data', data => { - logs3 += data.toString(); - }); - - copyBuild.stderr.on('data', data => { - logs3 += data.toString(); - }); - - copyBuild.on('close', code => { - if (code === 0) { - console.log(logs3); - - return res.status(200).json({ - logs3, - message: 'Copied Successfully' - }); - } else { - return res.status(500).json({ - logs3, - message: 'Failed to copy' - }); - } - }); - }); - - - - - - - - - - - - - - } else { - return res.status(500).json({ - logs4, - message: 'Build Failed' - }); - } - }); - - - - - - - } else { - return res.status(500).json({ - logs2, - message: 'Failed to install dependencies' - }); - } - }); - - - - - - - - } else { - console.log(logs1); - return res.status(500).json({ - logs1, - message: 'Failed to pull new code' - }); - } - }); -}); - - - - - - - - - - - - - - - - - - - - - - - - - -router.post('/reloadnginx', async (req, res) => { - const reload = spawn('systemctl', ['restart', 'nginx']); - - res.status(200).json({ success: true}); - -}); - -module.exports = router; \ No newline at end of file diff --git a/backend/routes/projects.js b/backend/routes/projects.js deleted file mode 100644 index 93601bd..0000000 --- a/backend/routes/projects.js +++ /dev/null @@ -1,60 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const UserSchema = require('../models/User'); -const ProjectSchema = require('../models/Project'); -// const { body, validationResult } = require('express-validator'); -const axios = require('axios'); - - - - - - - - -// Get All Projects -router.post('/', async (req, res) => { - axios.get( - 'https://api.github.com/user', - { - headers: { - 'Authorization': `Token ${req.body.accessToken}` - } - } - ).then(async response => { - // res.json(response.data); - const allProjects = await ProjectSchema.find({ ownerId: response.data.id }); - return res.status(200).json(allProjects); - }) - .catch(error => { - console.error(error); - res.json({ success: false }); - }); -}); - - - - - - - - - - - - - - - -router.post('/delete', async (req, res) => { - res.json({ success: false }); - //const theProject = await ProjectSchema.findById(req.body.projectId); - - //if (theProject) { - // await theProject.delete(); - // return res.json({ success: true }); - //} - //res.json({ success: false }); -}); - -module.exports = router; diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 33ad091..12a703d 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/frontend/public/index.html b/frontend/public/index.html index 0e1e81a..a5b94b0 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js index 1f03afe..9382b9a 100644 --- a/frontend/src/App.test.js +++ b/frontend/src/App.test.js @@ -1,7 +1,7 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; +import { render, screen } from "@testing-library/react"; +import App from "./App"; -test('renders learn react link', () => { +test("renders learn react link", () => { render(<App />); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); diff --git a/frontend/src/Components/Deploy/Deploy.js b/frontend/src/Components/Deploy/Deploy.js index 718a5c2..47566da 100644 --- a/frontend/src/Components/Deploy/Deploy.js +++ b/frontend/src/Components/Deploy/Deploy.js @@ -7,96 +7,116 @@ const Deploy = ({ id }) => { console.log(id); const clone = async () => { - const accessToken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/clone', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const accessToken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/clone", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken, projectId: id }), }, - body: JSON.stringify({ accessToken , projectId: id }) - }); + ); const json = await response.json(); setCloningLogs(json.message); - } + }; const install = async () => { - const accessToken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/install', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const accessToken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/install", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken, projectId: id }), }, - body: JSON.stringify({ accessToken , projectId: id }) - }); + ); const json = await response.json(); setInstallLogs(json); - } + }; const build = async () => { - const accessToken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/build', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const accessToken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/build", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken, projectId: id }), }, - body: JSON.stringify({ accessToken , projectId: id }) - }); + ); const json = await response.json(); setBuildLogs(json); - } + }; const copyBuild = async () => { - const accessToken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/copybuild', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const accessToken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/copybuild", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken, projectId: id }), }, - body: JSON.stringify({ accessToken , projectId: id }) - }); + ); const json = await response.json(); // console.log(json); - } + }; const nginxConf = async () => { - const accessToken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/nginxconf', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const accessToken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/nginxconf", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken, projectId: id }), }, - body: JSON.stringify({ accessToken , projectId: id }) - }); + ); const json = await response.json(); console.log(json); - } - + }; const configureWebhook = async () => { - const accessToken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/configurewebhook', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const accessToken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/configurewebhook", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken, projectId: id }), }, - body: JSON.stringify({ accessToken , projectId: id }) - }); + ); const json = await response.json(); console.log(json); - } + }; const reloadNginx = async () => { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/reloadnginx', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/reloadnginx", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }, + ); const json = await response.json(); if (json.success) { window.location.href = "/dashboard"; } - } + }; useEffect(() => { const allThings = async () => { @@ -107,74 +127,106 @@ const Deploy = ({ id }) => { await nginxConf(); await configureWebhook(); await reloadNginx(); - } + }; allThings(); }, []); - return ( <div className="w-[90%]"> - <div className="mt-12"> - <div className="columns-2"> - <div className="mx-auto p-4 bg-gray-900 border border-gray-900 rounded-lg shadow"> - <div className="block p-3 border rounded-lg shadow bg-gray-800 border-gray-800"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Status</h5> - <p className="font-normal text-gray-400">🟢 Deploying</p> - </div> - <div className="mt-3 block p-3 border rounded-lg shadow bg-gray-800 border-gray-800"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Environment</h5> - <p className="font-normal text-gray-400">Production</p> - </div> - </div> - <div className="mx-auto p-4 bg-gray-900 border border-gray-900 rounded-lg shadow"> - <div className="block p-3 border rounded-lg shadow bg-gray-800 border-gray-800"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Duration</h5> - <p className="font-normal text-gray-400">1m</p> - </div> - <div className="mt-3 block p-5 border rounded-lg shadow bg-gray-800 border-gray-800"> - <button type="button" className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800">Visit</button> - <button type="button" className="focus:outline-none text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-red-600 hover:bg-red-700 focus:ring-red-900">Delete</button> - </div> - </div> - - </div> - <div className="mt-5 max-w-screen-xl mx-auto p-4 bg-gray-900 border border-gray-800 rounded-lg shadow"> - <div className=" max-w-screen-xl mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Cloning <small>{!cloningLogs && "(Processing...)"}</small></h5> - <p className="font-mono text-gray-300"> - {cloningLogs && cloningLogs.split("\n").map((line, index) => ( - <React.Fragment key={index}> - {cloningLogs === "Repository cloned successfully" ? `🟢 ${line}` : `🔴 Failed to clone repository`} - <br /> - </React.Fragment> - ))} - </p> + <div className="mt-12"> + <div className="columns-2"> + <div className="mx-auto p-4 bg-gray-900 border border-gray-900 rounded-lg shadow"> + <div className="block p-3 border rounded-lg shadow bg-gray-800 border-gray-800"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Status + </h5> + <p className="font-normal text-gray-400">🟢 Deploying</p> + </div> + <div className="mt-3 block p-3 border rounded-lg shadow bg-gray-800 border-gray-800"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Environment + </h5> + <p className="font-normal text-gray-400">Production</p> + </div> + </div> + <div className="mx-auto p-4 bg-gray-900 border border-gray-900 rounded-lg shadow"> + <div className="block p-3 border rounded-lg shadow bg-gray-800 border-gray-800"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Duration + </h5> + <p className="font-normal text-gray-400">1m</p> + </div> + <div className="mt-3 block p-5 border rounded-lg shadow bg-gray-800 border-gray-800"> + <button + type="button" + className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800" + > + Visit + </button> + <button + type="button" + className="focus:outline-none text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-red-600 hover:bg-red-700 focus:ring-red-900" + > + Delete + </button> + </div> + </div> </div> - <div className="mt-5 max-w-screen-xl mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Installing Dependencies <small>{!installLogs && "(Processing...)"}</small></h5> - <p className="font-mono text-gray-300"> - {installLogs && <React.Fragment> - {installLogs.logs} - <br /> <br /> - {installLogs.message === "Dependencies installed successfully" ? `🟢 ${installLogs.message}` : `🔴 Failed to install dependencies`} - <br /> - </React.Fragment>} - </p> + <div className="mt-5 max-w-screen-xl mx-auto p-4 bg-gray-900 border border-gray-800 rounded-lg shadow"> + <div className=" max-w-screen-xl mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Cloning <small>{!cloningLogs && "(Processing...)"}</small> + </h5> + <p className="font-mono text-gray-300"> + {cloningLogs && + cloningLogs.split("\n").map((line, index) => ( + <React.Fragment key={index}> + {cloningLogs === "Repository cloned successfully" + ? `🟢 ${line}` + : `🔴 Failed to clone repository`} + <br /> + </React.Fragment> + ))} + </p> + </div> + <div className="mt-5 max-w-screen-xl mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Installing Dependencies{" "} + <small>{!installLogs && "(Processing...)"}</small> + </h5> + <p className="font-mono text-gray-300"> + {installLogs && ( + <React.Fragment> + {installLogs.logs} + <br /> <br /> + {installLogs.message === "Dependencies installed successfully" + ? `🟢 ${installLogs.message}` + : `🔴 Failed to install dependencies`} + <br /> + </React.Fragment> + )} + </p> </div> - <div className="mt-5 max-w-screen-xl mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Building <small>{!buildLogs && "(Processing...)"}</small></h5> - <p className="font-mono text-gray-300"> - {buildLogs && <React.Fragment> - {buildLogs.logs} - <br /> <br /> - {buildLogs.message === "Build successful" ? `🟢 ${buildLogs.message}` : `🔴 Failed to build`} - <br /> - </React.Fragment>} - </p> + <div className="mt-5 max-w-screen-xl mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Building <small>{!buildLogs && "(Processing...)"}</small> + </h5> + <p className="font-mono text-gray-300"> + {buildLogs && ( + <React.Fragment> + {buildLogs.logs} + <br /> <br /> + {buildLogs.message === "Build successful" + ? `🟢 ${buildLogs.message}` + : `🔴 Failed to build`} + <br /> + </React.Fragment> + )} + </p> </div> + </div> </div> </div> - </div> ); }; diff --git a/frontend/src/Components/DeploySettings/DeploySettings.js b/frontend/src/Components/DeploySettings/DeploySettings.js index 0124633..e5fc842 100644 --- a/frontend/src/Components/DeploySettings/DeploySettings.js +++ b/frontend/src/Components/DeploySettings/DeploySettings.js @@ -1,94 +1,152 @@ import React, { useState } from "react"; const DeploySettings = ({ id }) => { - const [subDomain, setSubDomain] = useState(null); - const [isAvailable, setIsAvailable] = useState(null); - const [isAvailabilityAvailable, setIsAvailabilityAvailable] = useState(false); - const [frameworkPreset, setFrameworkPreset] = useState(1); - const [rootDirectory, setRootDirectory] = useState(null); - const [packageManager, setPackageManager] = useState(1); + const [subDomain, setSubDomain] = useState(null); + const [isAvailable, setIsAvailable] = useState(null); + const [isAvailabilityAvailable, setIsAvailabilityAvailable] = useState(false); + const [frameworkPreset, setFrameworkPreset] = useState(1); + const [rootDirectory, setRootDirectory] = useState(null); + const [packageManager, setPackageManager] = useState(1); - const checkIsAvailable = async () => { - // TO-DO - return true; - } - - const deploy = async () => { - const ia = await checkIsAvailable(); - if (ia) { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Token ${localStorage.getItem('access-token')}` - }, - body: JSON.stringify({ frameworkPreset, rootDirectory, subDomain, packageManager, accessToken: localStorage.getItem('access-token'), githubRepoId: id }) - }); - const json = await response.json(); - if (json.success) { - window.location.href = `/deploying/${json.projectId}`; - } - else if (response.status === 401) { - window.alert(json.error); - } - else { - window.alert("Failed"); - } - } + const checkIsAvailable = async () => { + // TO-DO + return true; + }; + + const deploy = async () => { + const ia = await checkIsAvailable(); + if (ia) { + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, + body: JSON.stringify({ + frameworkPreset, + rootDirectory, + subDomain, + packageManager, + accessToken: localStorage.getItem("access-token"), + githubRepoId: id, + }), + }, + ); + const json = await response.json(); + if (json.success) { + window.location.href = `/deploying/${json.projectId}`; + } else if (response.status === 401) { + window.alert(json.error); + } else { + window.alert("Failed"); + } } + }; return ( <div className="w-[90%]"> - <div className="flex flex-col items-center justify-center w-full"> + <div className="flex flex-col items-center justify-center w-full"> <div className="mt-12 w-full"> - <div className="w-full"> - <div className="mt-5 mx-auto p-4 bg-gray-900 border border-gray-800 rounded-lg shadow"> - <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-6 text-2xl font-bold tracking-tight text-white">Project Name</h5> - <div> - <h5 className="mb-6 text-lg tracking-tight text-white">The same will be your subdomain ( i.e. <strong>{!subDomain ? 'hello-world' : subDomain}</strong>.staticstorm.coderush.tech )</h5> - <input style={{ marginBottom: 10 }} value={subDomain} onChange={(e) => setSubDomain(e.target.value)} placeholder="hello-world" type="text" id="default-input" className="mt-5 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" /> + <div className="w-full"> + <div className="mt-5 mx-auto p-4 bg-gray-900 border border-gray-800 rounded-lg shadow"> + <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-6 text-2xl font-bold tracking-tight text-white"> + Project Name + </h5> + <div> + <h5 className="mb-6 text-lg tracking-tight text-white"> + The same will be your subdomain ( i.e.{" "} + <strong>{!subDomain ? "hello-world" : subDomain}</strong> + .staticstorm.coderush.tech ) + </h5> + <input + style={{ marginBottom: 10 }} + value={subDomain} + onChange={(e) => setSubDomain(e.target.value)} + placeholder="hello-world" + type="text" + id="default-input" + className="mt-5 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" + /> - { - isAvailabilityAvailable && + {isAvailabilityAvailable && ( <div> - {!isAvailable && <small className="text-red-500">❌ This domain is unavailable</small>} - {isAvailable && <small className="text-green-500">✅ This domain is available</small>} + {!isAvailable && ( + <small className="text-red-500"> + ❌ This domain is unavailable + </small> + )} + {isAvailable && ( + <small className="text-green-500"> + ✅ This domain is available + </small> + )} </div> - } - </div> - </div> - <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - - <h5 className="mb-6 text-2xl font-bold tracking-tight text-white">Frawork Preset</h5> - <select value={frameworkPreset} onChange={(e) => setFrameworkPreset(parseInt(e.target.value))} id="default" className="border mb-6 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"> - <option value={1} selected>Create React App</option> - <option value={2}>Vanilla.js</option> + )} + </div> + </div> + <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-6 text-2xl font-bold tracking-tight text-white"> + Frawork Preset + </h5> + <select + value={frameworkPreset} + onChange={(e) => setFrameworkPreset(parseInt(e.target.value))} + id="default" + className="border mb-6 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" + > + <option value={1} selected> + Create React App + </option> + <option value={2}>Vanilla.js</option> </select> - </div> - <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-6 text-2xl font-bold tracking-tight text-white">Root Directory</h5> - <div> - <input value={rootDirectory} onChange={(e) => setRootDirectory(e.target.value)} placeholder="./" type="text" id="default-input" className="mt-5 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" /> - </div> - </div> - <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - - <h5 className="mb-6 text-2xl font-bold tracking-tight text-white">Select Package Manager</h5> - <select value={packageManager} onChange={(e) => setPackageManager(parseInt(e.target.value))} id="default" className="border mb-6 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"> - <option value={1} selected>NPM</option> - <option value={2}>Yarn</option> + </div> + <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-6 text-2xl font-bold tracking-tight text-white"> + Root Directory + </h5> + <div> + <input + value={rootDirectory} + onChange={(e) => setRootDirectory(e.target.value)} + placeholder="./" + type="text" + id="default-input" + className="mt-5 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" + /> + </div> + </div> + <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-6 text-2xl font-bold tracking-tight text-white"> + Select Package Manager + </h5> + <select + value={packageManager} + onChange={(e) => setPackageManager(parseInt(e.target.value))} + id="default" + className="border mb-6 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" + > + <option value={1} selected> + NPM + </option> + <option value={2}>Yarn</option> </select> + </div> + <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <button + onClick={deploy} + type="button" + className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800" + > + Deploy Now + </button> + </div> + </div> + </div> </div> - <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <button onClick={deploy} type="button" className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800">Deploy Now</button> - </div> - </div> - - </div> - </div> - </div> </div> ); }; diff --git a/frontend/src/Components/GitLogin/GitLogin.js b/frontend/src/Components/GitLogin/GitLogin.js index c36c6a0..d094f85 100644 --- a/frontend/src/Components/GitLogin/GitLogin.js +++ b/frontend/src/Components/GitLogin/GitLogin.js @@ -1,24 +1,43 @@ import React from "react"; const GitLogin = () => { - const handleLogin = () => { window.location.href = `https://github.com/login/oauth/authorize?client_id=636a2eda665f3bc395b4&scope=user:email,repo`; }; return ( <div className="w-[90%] m-auto"> - <div className="w-full flex flex-col items-center justify-center"> - <div className="w-full mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-6 text-4xl font-bold tracking-tight text-center text-white">Login</h5> - <div className="w-full flex justify-center"> - <button onClick={handleLogin} type="button" className="justify-center text-white bg-black focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center focus:ring-gray-500 hover:bg-[#050708]/30 mr-2 mb-2"> - <svg className="w-4 h-4 mr-2 -ml-1" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> - Login with Github - </button> - </div> - </div> + <div className="w-full flex flex-col items-center justify-center"> + <div className="w-full mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-6 text-4xl font-bold tracking-tight text-center text-white"> + Login + </h5> + <div className="w-full flex justify-center"> + <button + onClick={handleLogin} + type="button" + className="justify-center text-white bg-black focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center focus:ring-gray-500 hover:bg-[#050708]/30 mr-2 mb-2" + > + <svg + className="w-4 h-4 mr-2 -ml-1" + aria-hidden="true" + focusable="false" + data-prefix="fab" + data-icon="github" + role="img" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 496 512" + > + <path + fill="currentColor" + d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" + ></path> + </svg> + Login with Github + </button> + </div> </div> + </div> </div> ); }; diff --git a/frontend/src/Components/NavBar/Navbar.js b/frontend/src/Components/NavBar/Navbar.js index da14857..f1dc6a6 100644 --- a/frontend/src/Components/NavBar/Navbar.js +++ b/frontend/src/Components/NavBar/Navbar.js @@ -1,208 +1,236 @@ -import React, { useEffect, useState } from 'react'; -import { Fragment } from 'react' -import { Disclosure, Menu, Transition } from '@headlessui/react' -import { Bars3Icon, BellIcon, XMarkIcon } from '@heroicons/react/24/outline' -import { useNavigate, Link, useLocation } from 'react-router-dom'; - +import React, { useEffect, useState } from "react"; +import { Fragment } from "react"; +import { Disclosure, Menu, Transition } from "@headlessui/react"; +import { Bars3Icon, BellIcon, XMarkIcon } from "@heroicons/react/24/outline"; +import { useNavigate, Link, useLocation } from "react-router-dom"; const Navbar = (props) => { - const [isLoggedIn, setIsLoggedIn] = useState(false); - const [imageURI, setImageURI] = useState(""); + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [imageURI, setImageURI] = useState(""); - const location = useLocation(); + const location = useLocation(); - const navigate = useNavigate(); + const navigate = useNavigate(); - console.log("Hi"); - const getUser = async () => { - const accesstoken = localStorage.getItem('access-token'); - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/auth/getuser' ,{ - method: 'POST', + console.log("Hi"); + const getUser = async () => { + const accesstoken = localStorage.getItem("access-token"); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/auth/getuser", + { + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, - body: JSON.stringify({ accessToken: accesstoken }) - }); - const json = await response.json(); - setImageURI(json); + body: JSON.stringify({ accessToken: accesstoken }), + }, + ); + const json = await response.json(); + setImageURI(json); + }; + + useEffect(() => { + if (localStorage.getItem("access-token")) { + setIsLoggedIn(true); + console.log("Hi, you're logged in!!"); + getUser(); + } else { + console.log("no, you are not logged in"); + setIsLoggedIn(false); } + }, []); + + const navigation = [ + { name: "Home", href: "/", current: props.focus === "home" }, + { name: "Features", href: "/#features", current: false }, + { + name: "Dashboard", + href: "/dashboard", + current: props.focus === "dashboard", + }, + { + name: "Github", + href: "https://github.com/devarshishimpi/staticstormhackathon", + current: false, + }, + ]; - useEffect(() => { - if (localStorage.getItem('access-token')) { - setIsLoggedIn(true); - console.log("Hi, you're logged in!!"); - getUser(); - } - else { - console.log("no, you are not logged in"); - setIsLoggedIn(false); - } - }, []); - - const navigation = [ - { name: 'Home', href: '/', current: (props.focus === "home") }, - { name: 'Features', href: '/#features', current: false }, - { name: 'Dashboard', href: '/dashboard', current: (props.focus === "dashboard") }, - { name: 'Github', href: 'https://github.com/devarshishimpi/staticstormhackathon', current: false }, - ] - - function classNames(...classes) { - return classes.filter(Boolean).join(' ') - } + function classNames(...classes) { + return classes.filter(Boolean).join(" "); + } + + const signout = () => { + localStorage.removeItem("access-token"); + window.location.href = "/"; + window.location.replace("/"); + }; - const signout = () => { - localStorage.removeItem('access-token'); - window.location.href = "/"; - window.location.replace("/"); - } - return ( <> - <Disclosure as="nav" className="bg-gray-800"> - {({ open }) => ( - <> - <div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"> - <div className="relative flex h-16 items-center justify-between"> - <div className="absolute inset-y-0 left-0 flex items-center sm:hidden"> - {/* Mobile menu button*/} - <Disclosure.Button className="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"> - <span className="sr-only">Open main menu</span> - {open ? ( - <XMarkIcon className="block h-6 w-6" aria-hidden="true" /> - ) : ( - <Bars3Icon className="block h-6 w-6" aria-hidden="true" /> - )} - </Disclosure.Button> - </div> - <div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start"> - <div className="flex flex-shrink-0 items-center"> - <img - className="block h-8 w-auto lg:hidden" - src="https://res.cloudinary.com/dvstech/image/upload/v1676718021/staticstormhackathonfiles/staticstormlogo_ukdxwt.png" - alt="Devcode" - /> - <img - className="hidden h-8 w-auto lg:block" - src="https://res.cloudinary.com/dvstech/image/upload/v1676718021/staticstormhackathonfiles/staticstormlogo_ukdxwt.png" - alt="Devcode" - /> + <Disclosure as="nav" className="bg-gray-800"> + {({ open }) => ( + <> + <div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"> + <div className="relative flex h-16 items-center justify-between"> + <div className="absolute inset-y-0 left-0 flex items-center sm:hidden"> + {/* Mobile menu button*/} + <Disclosure.Button className="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"> + <span className="sr-only">Open main menu</span> + {open ? ( + <XMarkIcon className="block h-6 w-6" aria-hidden="true" /> + ) : ( + <Bars3Icon className="block h-6 w-6" aria-hidden="true" /> + )} + </Disclosure.Button> </div> - <div className="hidden sm:ml-6 sm:block"> - <div className="flex space-x-4"> - {navigation.map((item) => ( - <a - key={item.name} - href={item.href} - className={classNames( - item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', - 'px-3 py-2 rounded-md text-sm font-medium' - )} - aria-current={item.current ? 'page' : undefined} - > - {item.name} - </a> - ))} + <div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start"> + <div className="flex flex-shrink-0 items-center"> + <img + className="block h-8 w-auto lg:hidden" + src="https://res.cloudinary.com/dvstech/image/upload/v1676718021/staticstormhackathonfiles/staticstormlogo_ukdxwt.png" + alt="Devcode" + /> + <img + className="hidden h-8 w-auto lg:block" + src="https://res.cloudinary.com/dvstech/image/upload/v1676718021/staticstormhackathonfiles/staticstormlogo_ukdxwt.png" + alt="Devcode" + /> + </div> + <div className="hidden sm:ml-6 sm:block"> + <div className="flex space-x-4"> + {navigation.map((item) => ( + <a + key={item.name} + href={item.href} + className={classNames( + item.current + ? "bg-gray-900 text-white" + : "text-gray-300 hover:bg-gray-700 hover:text-white", + "px-3 py-2 rounded-md text-sm font-medium", + )} + aria-current={item.current ? "page" : undefined} + > + {item.name} + </a> + ))} + </div> </div> </div> - </div> - <div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0"> - {isLoggedIn? - <> - {/*<button + <div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0"> + {isLoggedIn ? ( + <> + {/*<button type="button" className="rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" > <span className="sr-only">View notifications</span> <BellIcon className="h-6 w-6" aria-hidden="true" /> </button>*/} - - {/* Profile dropdown */} - <Menu as="div" className="relative ml-3"> - <div> - <Menu.Button className="flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"> - <span className="sr-only">Open user menu</span> - <img - className="h-8 w-8 rounded-full" - src={`https://ui-avatars.com/api/?name=${imageURI}&background=random`} - alt="" - /> - </Menu.Button> - </div> - <Transition - as={Fragment} - enter="transition ease-out duration-100" - enterFrom="transform opacity-0 scale-95" - enterTo="transform opacity-100 scale-100" - leave="transition ease-in duration-75" - leaveFrom="transform opacity-100 scale-100" - leaveTo="transform opacity-0 scale-95" - > - <Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> - <Menu.Item> - {({ active }) => ( - <a - href="/dashboard" - className={classNames(active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700')} - > - Dashboard - </a> - )} - </Menu.Item> - <Menu.Item> - {({ active }) => ( - <a - href="/newproject" - className={classNames(active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700')} - > - New Project - </a> - )} - </Menu.Item> - <Menu.Item> - {({ active }) => ( - <button - onClick={signout} - className={classNames(active ? 'bg-gray-100' : '', 'px-4 flex w-[100%] py-2 text-sm text-gray-700')} - > - Sign out - </button> - )} - </Menu.Item> - </Menu.Items> - </Transition> - </Menu> </> - : - <> - <Link to={'/login'} type="button" className="text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2">Login</Link> - </> - } + {/* Profile dropdown */} + <Menu as="div" className="relative ml-3"> + <div> + <Menu.Button className="flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"> + <span className="sr-only">Open user menu</span> + <img + className="h-8 w-8 rounded-full" + src={`https://ui-avatars.com/api/?name=${imageURI}&background=random`} + alt="" + /> + </Menu.Button> + </div> + <Transition + as={Fragment} + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> + <Menu.Item> + {({ active }) => ( + <a + href="/dashboard" + className={classNames( + active ? "bg-gray-100" : "", + "block px-4 py-2 text-sm text-gray-700", + )} + > + Dashboard + </a> + )} + </Menu.Item> + <Menu.Item> + {({ active }) => ( + <a + href="/newproject" + className={classNames( + active ? "bg-gray-100" : "", + "block px-4 py-2 text-sm text-gray-700", + )} + > + New Project + </a> + )} + </Menu.Item> + <Menu.Item> + {({ active }) => ( + <button + onClick={signout} + className={classNames( + active ? "bg-gray-100" : "", + "px-4 flex w-[100%] py-2 text-sm text-gray-700", + )} + > + Sign out + </button> + )} + </Menu.Item> + </Menu.Items> + </Transition> + </Menu>{" "} + </> + ) : ( + <> + <Link + to={"/login"} + type="button" + className="text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2" + > + Login + </Link> + </> + )} + </div> </div> </div> - </div> - <Disclosure.Panel className="sm:hidden"> - <div className="space-y-1 px-2 pt-2 pb-3"> - {navigation.map((item) => ( - <Disclosure.Button - key={item.name} - as="a" - href={item.href} - className={classNames( - item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', - 'block px-3 py-2 rounded-md text-base font-medium' - )} - aria-current={item.current ? 'page' : undefined} - > - {item.name} - </Disclosure.Button> - ))} - </div> - </Disclosure.Panel> - </> - )} - </Disclosure> + <Disclosure.Panel className="sm:hidden"> + <div className="space-y-1 px-2 pt-2 pb-3"> + {navigation.map((item) => ( + <Disclosure.Button + key={item.name} + as="a" + href={item.href} + className={classNames( + item.current + ? "bg-gray-900 text-white" + : "text-gray-300 hover:bg-gray-700 hover:text-white", + "block px-3 py-2 rounded-md text-base font-medium", + )} + aria-current={item.current ? "page" : undefined} + > + {item.name} + </Disclosure.Button> + ))} + </div> + </Disclosure.Panel> + </> + )} + </Disclosure> </> - ) -} + ); +}; -export default Navbar \ No newline at end of file +export default Navbar; diff --git a/frontend/src/Components/NewDeploy/NewDeploy.js b/frontend/src/Components/NewDeploy/NewDeploy.js index ea46a17..ce041d4 100644 --- a/frontend/src/Components/NewDeploy/NewDeploy.js +++ b/frontend/src/Components/NewDeploy/NewDeploy.js @@ -1,56 +1,102 @@ import React from "react"; import { Link } from "react-router-dom"; -const NewDeploy = ({ getNextProjects, getPreviousProjects, projects, nextPage }) => { +const NewDeploy = ({ + getNextProjects, + getPreviousProjects, + projects, + nextPage, +}) => { return ( <div className=""> - <div className="flex flex-col items-center justify-center"> + <div className="flex flex-col items-center justify-center"> <div className="mt-12 w-[90%]"> - <div className="mt-5 mx-auto p-4 bg-gray-900 border border-gray-800 rounded-lg shadow"> - <div className="mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <div className="columns-2 flex justify-center"> - <h5 className="mb-8 text-2xl font-bold tracking-tight text-white">Deploy from Github</h5> - {/* <div> + <div className="mt-5 mx-auto p-4 bg-gray-900 border border-gray-800 rounded-lg shadow"> + <div className="mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <div className="columns-2 flex justify-center"> + <h5 className="mb-8 text-2xl font-bold tracking-tight text-white"> + Deploy from Github + </h5> + {/* <div> <button type="button" className="float-right text-white bg-black focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center focus:ring-gray-500 hover:bg-[#050708]/30 mr-2 mb-2"> <svg className="w-4 h-4 mr-2 -ml-1" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> Grant Access to Github </button> </div> */} - </div> - <br></br> - { projects.map((project) => { - return ( - <div> + </div> + <br></br> + {projects.map((project) => { + return ( + <div> <div className="rounded-2 columns-2"> - <h5 className="mt-4 text-xl font-bold tracking-tight text-white">{project.full_name}</h5> - <div className="float-right"> - <Link to={`/selectconfig/${project.id}`} type="button" className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800">Deploy</Link> - </div> + <h5 className="mt-4 text-xl font-bold tracking-tight text-white"> + {project.full_name} + </h5> + <div className="float-right"> + <Link + to={`/selectconfig/${project.id}`} + type="button" + className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800" + > + Deploy + </Link> + </div> </div> <hr className="h-px my-8 border-0 bg-gray-700" /> - </div> - ); - })} - <div className="flex row justify-between mt-16"> - <button onClick={getPreviousProjects} type="button" disabled={nextPage <= 2} className={`text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-black ${nextPage <= 2 && 'opacity-40'} hover:bg-[#202020] focus:outline-none focus:ring-black`}>Previous</button> - <button onClick={getNextProjects} type="button" disabled={projects.length < 30} className={`text-white ${projects.length < 30 && 'opacity-40'} focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-black hover:bg-[#202020] focus:outline-none focus:ring-black`}>Next</button> - </div> - </div> - - <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - <h5 className="mb-2 text-2xl font-bold tracking-tight text-white">Deploy from URL</h5> - <div className="columns-2"> - <div> - <input placeholder="https://github.com/bytemakers/devcode" type="text" id="default-input" className="mt-5 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" /> + </div> + ); + })} + <div className="flex row justify-between mt-16"> + <button + onClick={getPreviousProjects} + type="button" + disabled={nextPage <= 2} + className={`text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-black ${ + nextPage <= 2 && "opacity-40" + } hover:bg-[#202020] focus:outline-none focus:ring-black`} + > + Previous + </button> + <button + onClick={getNextProjects} + type="button" + disabled={projects.length < 30} + className={`text-white ${ + projects.length < 30 && "opacity-40" + } focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-black hover:bg-[#202020] focus:outline-none focus:ring-black`} + > + Next + </button> + </div> </div> - <div className="float-right"> - <Link to={`/selectconfig/1`} type="button" className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800">Deploy</Link> + + <div className="mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> + <h5 className="mb-2 text-2xl font-bold tracking-tight text-white"> + Deploy from URL + </h5> + <div className="columns-2"> + <div> + <input + placeholder="https://github.com/bytemakers/devcode" + type="text" + id="default-input" + className="mt-5 text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" + /> + </div> + <div className="float-right"> + <Link + to={`/selectconfig/1`} + type="button" + className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800" + > + Deploy + </Link> + </div> + </div> </div> </div> - </div> - </div> - </div> </div> + </div> </div> ); }; diff --git a/frontend/src/Components/Projects/Projects.js b/frontend/src/Components/Projects/Projects.js index 328923c..dbf3aea 100644 --- a/frontend/src/Components/Projects/Projects.js +++ b/frontend/src/Components/Projects/Projects.js @@ -6,108 +6,115 @@ const Projects = ({ user }) => { console.log(user); const getAllProjects = async () => { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/projects', { - method: 'POST', + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/projects", + { + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, - body: JSON.stringify({ accessToken: localStorage.getItem('access-token') }) - }); + body: JSON.stringify({ + accessToken: localStorage.getItem("access-token"), + }), + }, + ); const json = await response.json(); console.log(json); setProjects(json); - } + }; useEffect(() => { getAllProjects(); }, []); - - const deleteProject = async (project) => { const shouldI = window.confirm(`Are you sure to delete ${project.name}?`); if (shouldI) { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/deleteconf', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/deleteconf", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ projectId: project._id }), }, - body: JSON.stringify({ projectId: project._id }) - }); + ); const json = await response.json(); console.log(json); deleteFromDb(project); } - } - - + }; const restartNginx = async () => { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/reloadnginx', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/reloadnginx", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }, + ); const json = await response.json(); if (json.success) { window.location.href = "/dashboard"; - } - else { + } else { window.alert("Internal Server Error!"); } - } - + }; const deleteFromDb = async (project) => { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/deploy/deleteproject', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/deploy/deleteproject", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ projectId: project._id }), }, - body: JSON.stringify({ projectId: project._id }) - }); + ); const json = await response.json(); console.log(json); restartNginx(); - } - + }; return ( <div className="w-[90%] m-auto"> <div className="w-full flex flex-col items-center justify-center"> <div class="w-full mt-5 mx-auto p-6 bg-gray-800 border border-gray-800 rounded-lg shadow"> - - {projects && projects.map((project) => { - return ( - <div key={project._id}> - <div className="rounded-2 columns-2 py-5"> - <h5 class="mt-2 text-xl font-bold tracking-tight text-white"> - {" "} - {project.name}.staticstorm.coderush.tech - </h5> - <div class="float-right"> - <a - href={`http://${project.name}.staticstorm.coderush.tech`} - target="_blank" - type="button" - class="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800 cursor-pointer" - > - Visit - </a> - <button - type="button" - className="focus:outline-none text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-red-600 hover:bg-red-700 focus:ring-red-900" - onClick={() => deleteProject(project)} - > - Delete - </button> + {projects && + projects.map((project) => { + return ( + <div key={project._id}> + <div className="rounded-2 columns-2 py-5"> + <h5 class="mt-2 text-xl font-bold tracking-tight text-white"> + {" "} + {project.name}.staticstorm.coderush.tech + </h5> + <div class="float-right"> + <a + href={`http://${project.name}.staticstorm.coderush.tech`} + target="_blank" + type="button" + class="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800 cursor-pointer" + > + Visit + </a> + <button + type="button" + className="focus:outline-none text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-red-600 hover:bg-red-700 focus:ring-red-900" + onClick={() => deleteProject(project)} + > + Delete + </button> + </div> + </div> + <hr class="h-px border-0 bg-gray-700"></hr> </div> - </div> - <hr class="h-px border-0 bg-gray-700"></hr> - </div> - ); - })} + ); + })} {/* <div className="rounded-2 columns-2"> <h5 class="mt-2 text-xl font-bold tracking-tight text-white"> {" "} diff --git a/frontend/src/Components/Sections/Hero.js b/frontend/src/Components/Sections/Hero.js index eee7acd..bfe96a3 100644 --- a/frontend/src/Components/Sections/Hero.js +++ b/frontend/src/Components/Sections/Hero.js @@ -1,22 +1,43 @@ const Hero = () => { - return ( <> - <section> - <div className="mt-10 py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-12"> - <h1 className="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl">Unleash the power of simple, fast and secure websites.</h1> - <p className="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 xl:px-48">Build simple, fast, and secure websites without worrying about technical expertise or maintenance.</p> - <div className="flex flex-col mb-8 lg:mb-16 space-y-4 sm:flex-row sm:justify-center sm:space-y-0 sm:space-x-4"> - <a href="/newproject" className="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"> - Deploy Now - <svg className="ml-2 -mr-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg> + <section> + <div className="mt-10 py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-12"> + <h1 className="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl"> + Unleash the power of simple, fast and secure websites. + </h1> + <p className="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 xl:px-48"> + Build simple, fast, and secure websites without worrying about + technical expertise or maintenance. + </p> + <div className="flex flex-col mb-8 lg:mb-16 space-y-4 sm:flex-row sm:justify-center sm:space-y-0 sm:space-x-4"> + <a + href="/newproject" + className="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300" + > + Deploy Now + <svg + className="ml-2 -mr-1 w-5 h-5" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + fill-rule="evenodd" + d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" + clip-rule="evenodd" + ></path> + </svg> + </a> + <a + href="/dashboard" + className="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center bg-gray-800 text-white rounded-lg hover:bg-gray-600 focus:ring-4 focus:ring-gray-100" + > + Visit Dashboard </a> - <a href="/dashboard" className="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center bg-gray-800 text-white rounded-lg hover:bg-gray-600 focus:ring-4 focus:ring-gray-100"> - Visit Dashboard - </a> + </div> </div> - </div> -</section> + </section> </> ); }; diff --git a/frontend/src/index.css b/frontend/src/index.css index 4eb7b67..aba1a02 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -4,31 +4,31 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } [data-theme="dark"] { color: #fff; background-color: #030414; - transition: all .4s cubic-bezier(0.23, 1, 0.320, 1); + transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); } .announce[data-theme="dark"] { color: #fff; background: transparent; border: 1px solid rgb(52, 52, 85); - transition: all .4s cubic-bezier(0.23, 1, 0.320, 1); + transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); } .color[data-theme="dark"] { color: #fff; background: transparent; - transition: all .4s cubic-bezier(0.23, 1, 0.320, 1); -} \ No newline at end of file + transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); +} diff --git a/frontend/src/index.js b/frontend/src/index.js index 26a2c71..06f1991 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,13 +1,11 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import App from "./App"; +import reportWebVitals from "./reportWebVitals"; -const root = ReactDOM.createRoot(document.getElementById('root')); -root.render( - <App /> -); +const root = ReactDOM.createRoot(document.getElementById("root")); +root.render(<App />); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) diff --git a/frontend/src/pages/Dashboard/Dashboard.js b/frontend/src/pages/Dashboard/Dashboard.js index 9824616..b16a499 100644 --- a/frontend/src/pages/Dashboard/Dashboard.js +++ b/frontend/src/pages/Dashboard/Dashboard.js @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; import { useNavigate } from "react-router-dom"; -import Projects from "../../Components/Projects/Projects" +import Projects from "../../Components/Projects/Projects"; const Dashboard = () => { const [user, setUser] = useState(null); @@ -9,27 +9,25 @@ const Dashboard = () => { const navigate = useNavigate(); const validateAccessToken = async () => { - const response = await fetch('https://api.github.com/user', { - method: 'GET', + const response = await fetch("https://api.github.com/user", { + method: "GET", headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, }); const json = await response.json(); if (!json.id) { - localStorage.removeItem('access-token'); - navigate('/login'); - } - else { + localStorage.removeItem("access-token"); + navigate("/login"); + } else { setUser(json); } - } + }; useEffect(() => { - if (!localStorage.getItem('access-token')) { - navigate('/login'); - } - else { + if (!localStorage.getItem("access-token")) { + navigate("/login"); + } else { validateAccessToken(); } }, []); @@ -38,14 +36,10 @@ const Dashboard = () => { <div className="flex items-center justify-center"> <Helmet> <title>StaticStorm | Dashboard - + - ); }; diff --git a/frontend/src/pages/Deploying/Deploying.js b/frontend/src/pages/Deploying/Deploying.js index 61ff9c7..0aa2054 100644 --- a/frontend/src/pages/Deploying/Deploying.js +++ b/frontend/src/pages/Deploying/Deploying.js @@ -1,6 +1,6 @@ import { Helmet } from "react-helmet"; import { useParams } from "react-router-dom"; -import Deploy from "../../Components/Deploy/Deploy" +import Deploy from "../../Components/Deploy/Deploy"; const NewProject = () => { const { id } = useParams(); @@ -9,14 +9,10 @@ const NewProject = () => {
StaticStorm | Deploying - + -
); }; diff --git a/frontend/src/pages/Home/Home.js b/frontend/src/pages/Home/Home.js index c82cd0b..2db7c5d 100644 --- a/frontend/src/pages/Home/Home.js +++ b/frontend/src/pages/Home/Home.js @@ -1,16 +1,12 @@ import { Helmet } from "react-helmet"; -import Hero from "../../Components/Sections/Hero.js" +import Hero from "../../Components/Sections/Hero.js"; const Home = () => { - return ( <> StaticStorm | Home - + diff --git a/frontend/src/pages/Login/Login.js b/frontend/src/pages/Login/Login.js index 08981ee..1a9cfa6 100644 --- a/frontend/src/pages/Login/Login.js +++ b/frontend/src/pages/Login/Login.js @@ -7,20 +7,16 @@ const Login = () => { const navigate = useNavigate(); useEffect(() => { - if (localStorage.getItem('access-token')) { - navigate('/dashboard'); + if (localStorage.getItem("access-token")) { + navigate("/dashboard"); } }, []); - return ( <> StaticStorm | Login - + diff --git a/frontend/src/pages/NewProject/NewProject.js b/frontend/src/pages/NewProject/NewProject.js index bc73024..3ced793 100644 --- a/frontend/src/pages/NewProject/NewProject.js +++ b/frontend/src/pages/NewProject/NewProject.js @@ -11,78 +11,83 @@ const NewProject = () => { const getProjectsFirstTime = async () => { const response = await fetch(`https://api.github.com/user/repos?page=1`, { - method: 'GET', + method: "GET", headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, }); const json = await response.json(); // console.log(json); setProjects(json); - } + }; const validateAccessToken = async () => { - const response = await fetch('https://api.github.com/user', { - method: 'GET', + const response = await fetch("https://api.github.com/user", { + method: "GET", headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, }); const json = await response.json(); if (!json.id) { - localStorage.removeItem('access-token'); - navigate('/login'); + localStorage.removeItem("access-token"); + navigate("/login"); } - } + }; useEffect(() => { - if (!localStorage.getItem('access-token')) { - navigate('/login'); - } - else { + if (!localStorage.getItem("access-token")) { + navigate("/login"); + } else { validateAccessToken(); getProjectsFirstTime(); } }, []); const getNextProjects = async () => { - const response = await fetch(`https://api.github.com/user/repos?page=${nextPage}`, { - method: 'GET', - headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } - }); + const response = await fetch( + `https://api.github.com/user/repos?page=${nextPage}`, + { + method: "GET", + headers: { + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, + }, + ); const json = await response.json(); console.log(json); - setNextPage(page => page+1); + setNextPage((page) => page + 1); setProjects(json); - } + }; const getPreviousProjects = async () => { - const response = await fetch(`https://api.github.com/user/repos?page=${nextPage-2}`, { - method: 'GET', - headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } - }); + const response = await fetch( + `https://api.github.com/user/repos?page=${nextPage - 2}`, + { + method: "GET", + headers: { + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, + }, + ); const json = await response.json(); console.log(json); - setNextPage(page => page-1); + setNextPage((page) => page - 1); setProjects(json); - } - - + }; return ( <> StaticStorm | New Project - + - + ); }; diff --git a/frontend/src/pages/SelectConfig/SelectConfig.js b/frontend/src/pages/SelectConfig/SelectConfig.js index 3daf2df..73c8510 100644 --- a/frontend/src/pages/SelectConfig/SelectConfig.js +++ b/frontend/src/pages/SelectConfig/SelectConfig.js @@ -1,63 +1,55 @@ import { useEffect } from "react"; import { Helmet } from "react-helmet"; import { useNavigate, useParams } from "react-router-dom"; -import DeploySettings from "../../Components/DeploySettings/DeploySettings" +import DeploySettings from "../../Components/DeploySettings/DeploySettings"; const SelectConfig = () => { const navigate = useNavigate(); let { id } = useParams(); const validateAccessToken = async () => { - const response = await fetch('https://api.github.com/user', { - method: 'GET', + const response = await fetch("https://api.github.com/user", { + method: "GET", headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, }); const json = await response.json(); if (!json.id) { - localStorage.removeItem('access-token'); - navigate('/login'); + localStorage.removeItem("access-token"); + navigate("/login"); } - } + }; - const getRepoDetails = async () => { const response = await fetch(`https://api.github.com/repositories/${id}`, { - method: 'GET', + method: "GET", headers: { - 'Authorization': `Token ${localStorage.getItem('access-token')}` - } + Authorization: `Token ${localStorage.getItem("access-token")}`, + }, }); const json = await response.json(); console.log(json); - } - + }; useEffect(() => { - if (!localStorage.getItem('access-token')) { - navigate('/login'); - } - else { + if (!localStorage.getItem("access-token")) { + navigate("/login"); + } else { validateAccessToken(); getRepoDetails(); } }, []); - return (
StaticStorm | New Project - + -
); }; diff --git a/frontend/src/reportWebVitals.js b/frontend/src/reportWebVitals.js index 5253d3a..9ecd33f 100644 --- a/frontend/src/reportWebVitals.js +++ b/frontend/src/reportWebVitals.js @@ -1,6 +1,6 @@ -const reportWebVitals = onPerfEntry => { +const reportWebVitals = (onPerfEntry) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(onPerfEntry); getFID(onPerfEntry); getFCP(onPerfEntry); diff --git a/frontend/src/setupTests.js b/frontend/src/setupTests.js index 8f2609b..1dd407a 100644 --- a/frontend/src/setupTests.js +++ b/frontend/src/setupTests.js @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom"; diff --git a/frontend/src/utils/VerifyLogin.js b/frontend/src/utils/VerifyLogin.js index c1ea188..0ab1447 100644 --- a/frontend/src/utils/VerifyLogin.js +++ b/frontend/src/utils/VerifyLogin.js @@ -1,40 +1,39 @@ -import React from 'react' -import { useNavigate, useSearchParams } from 'react-router-dom'; +import React from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; const VerifyLogin = () => { - const navigate = useNavigate(); + const navigate = useNavigate(); - const login = async () => { - const response = await fetch('http://abcd.staticstorm.coderush.tech/api/auth', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ accessToken }) - }); - - const json = await response.json(); - if (json.success) { - navigate('/dashboard'); - } - else { - navigate('/login') - } - } + const login = async () => { + const response = await fetch( + "http://abcd.staticstorm.coderush.tech/api/auth", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ accessToken }), + }, + ); - const [searchParams, setSearchParams] = useSearchParams(); - const accessToken = searchParams.get("access_token"); - if (!accessToken) { - navigate('/login'); - } - else { - login(); - localStorage.setItem('access-token', accessToken); + const json = await response.json(); + if (json.success) { + navigate("/dashboard"); + } else { + navigate("/login"); } + }; + + const [searchParams, setSearchParams] = useSearchParams(); + const accessToken = searchParams.get("access_token"); + if (!accessToken) { + navigate("/login"); + } else { + login(); + localStorage.setItem("access-token", accessToken); + } - return ( -
VerifyLogin
- ) -} + return
VerifyLogin
; +}; -export default VerifyLogin \ No newline at end of file +export default VerifyLogin; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 43c269b..6864a13 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,11 +1,9 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: [ - "./src/**/*.{js,jsx,ts,tsx}", - ], + content: ["./src/**/*.{js,jsx,ts,tsx}"], // adding the 'dark:' will change the theme - darkMode: ['class', '[data-theme="dark"]'], - corePlugins:{ - 'preflight': 'false', - }, -} \ No newline at end of file + darkMode: ["class", '[data-theme="dark"]'], + corePlugins: { + preflight: "false", + }, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..07d240d --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "staticstorm", + "version": "0.8.0-beta", + "description": "Unleash the power of simple, fast and secure websites.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "format": "prettier --check --ignore-path .gitignore .", + "format:fix": "prettier --write --ignore-path .gitignore ." + }, + "repository": { + "type": "git", + "url": "git+https://github.com/devarshishimpi/staticstorm.git" + }, + "author": "Devarshi Shimpi", + "license": "ISC", + "bugs": { + "url": "https://github.com/devarshishimpi/staticstorm/issues" + }, + "homepage": "https://github.com/devarshishimpi/staticstorm#readme", + "devDependencies": { + "prettier": "3.0.3" + } +} diff --git a/server/db.js b/server/db.js new file mode 100644 index 0000000..d1b21a0 --- /dev/null +++ b/server/db.js @@ -0,0 +1,13 @@ +require("dotenv").config(); + +const mongoose = require("mongoose"); + +const mongoURI = process.env.mongoURI; + +const connectToMongo = () => { + mongoose.connect(mongoURI, { dbName: "staticstorm" }, () => { + console.log("Connected To Mongo Successfully!!"); + }); +}; + +module.exports = connectToMongo; diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..2b2c433 --- /dev/null +++ b/server/index.js @@ -0,0 +1,24 @@ +const express = require("express"); +const connectToMongo = require("./db"); +const cors = require("cors"); + +connectToMongo(); + +const app = express(); +app.use(express.json()); +app.use(cors()); + +const PORT = process.env.PORT || 8181; + +app.get("/", (req, res) => { + res.send("Hi!!"); +}); + +// Available Routes +app.use("/api/auth", require("./routes/auth")); +app.use("/api/deploy", require("./routes/deploy")); +app.use("/api/projects", require("./routes/projects")); + +app.listen(PORT, () => { + console.log(`Server started at http://localhost:${PORT}`); +}); diff --git a/server/models/Port.js b/server/models/Port.js new file mode 100644 index 0000000..23f30c8 --- /dev/null +++ b/server/models/Port.js @@ -0,0 +1,14 @@ +const mongoose = require("mongoose"); +const { Schema } = require("mongoose"); + +const PortSchema = new Schema( + { + nextFreePort: { + type: Number, + required: true, + }, + }, + { timestamps: true }, +); + +module.exports = mongoose.model("port", PortSchema); diff --git a/server/models/Project.js b/server/models/Project.js new file mode 100644 index 0000000..dc9a57a --- /dev/null +++ b/server/models/Project.js @@ -0,0 +1,38 @@ +const mongoose = require("mongoose"); +const { Schema } = require("mongoose"); + +const ProjectSchema = new Schema( + { + name: { + type: String, + required: true, + }, + framework: { + type: String, + required: true, + }, + rootDirectory: { + type: String, + required: true, + }, + packageManager: { + type: String, + required: true, + }, + githubRepoId: { + type: String, + required: true, + }, + ownerId: { + type: String, + required: true, + }, + port: { + type: Number, + required: true, + }, + }, + { timestamps: true }, +); + +module.exports = mongoose.model("project", ProjectSchema); diff --git a/server/models/User.js b/server/models/User.js new file mode 100644 index 0000000..afc4dfa --- /dev/null +++ b/server/models/User.js @@ -0,0 +1,26 @@ +const mongoose = require("mongoose"); +const { Schema } = require("mongoose"); + +const UserSchema = new Schema( + { + name: { + type: String, + required: true, + }, + githubId: { + type: String, + required: true, + }, + githubLogin: { + type: String, + }, + projects: { + // An array of project ids of the user + type: Array, + default: [], + }, + }, + { timestamps: true }, +); + +module.exports = mongoose.model("user", UserSchema); diff --git a/backend/package-lock.json b/server/package-lock.json similarity index 100% rename from backend/package-lock.json rename to server/package-lock.json diff --git a/backend/package.json b/server/package.json similarity index 100% rename from backend/package.json rename to server/package.json diff --git a/server/routes/auth.js b/server/routes/auth.js new file mode 100644 index 0000000..30cd7ff --- /dev/null +++ b/server/routes/auth.js @@ -0,0 +1,75 @@ +const express = require("express"); +const router = express.Router(); +const UserSchema = require("../models/User"); +// const { body, validationResult } = require('express-validator'); +var bcrypt = require("bcryptjs"); +var jwt = require("jsonwebtoken"); +// const fetchuser = require('../middleware/fetchuser'); +const axios = require("axios"); +const qs = require("querystring"); +require("dotenv").config(); + +const JWT_SECRET = process.env.JWT_SECRET; + +router.post("/", async (req, res) => { + axios + .get("https://api.github.com/user", { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // res.json(response.data); + const theUser = await UserSchema.findOne({ githubId: response.data.id }); + if (!theUser) { + // Create new account here!! + const newUser = await UserSchema.create({ + name: response.data.name, + githubId: response.data.id, + githubLogin: response.data.login, + }); + + return res.status(200).json({ success: true }); + } else { + // Logged in Successfully Here + return res.status(200).json({ success: true }); + } + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +router.get("/github", (req, res) => { + const code = req.query.code; + const clientId = process.env.GITHUB_CLIENT_ID; + const clientSecret = process.env.GITHUB_CLIENT_SECRET; + + axios + .post( + "https://github.com/login/oauth/access_token", + qs.stringify({ + client_id: clientId, + client_secret: clientSecret, + code: code, + }), + { + headers: { + Accept: "application/json", + }, + }, + ) + .then((response) => { + const accessToken = response.data.access_token; + return res.redirect( + `http://staticstorm.coderush.tech/verifyLogin?access_token=${accessToken}`, + ); + }) + .catch((error) => { + console.error(error); + res.send("An error occurred"); + }); +}); + +module.exports = router; diff --git a/server/routes/deploy.js b/server/routes/deploy.js new file mode 100644 index 0000000..b0b4ce9 --- /dev/null +++ b/server/routes/deploy.js @@ -0,0 +1,664 @@ +const express = require("express"); +const router = express.Router(); +const UserSchema = require("../models/User"); +const ProjectSchema = require("../models/Project"); +const PortSchema = require("../models/Port"); +// const { body, validationResult } = require('express-validator'); +var bcrypt = require("bcryptjs"); +var jwt = require("jsonwebtoken"); +// const fetchuser = require('../middleware/fetchuser'); +const axios = require("axios"); +const qs = require("querystring"); +const { spawn } = require("child_process"); +const { promises: fs } = require("fs"); +require("dotenv").config(); + +const JWT_SECRET = process.env.JWT_SECRET; + +router.post("/", async (req, res) => { + const { + frameworkPreset, + rootDirectory, + subDomain, + packageManager, + githubRepoId, + } = req.body; + + axios + .get("https://api.github.com/user", { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // res.json(response.data); + const theUser = await UserSchema.findOne({ githubId: response.data.id }); + if (!theUser) { + return res + .status(400) + .json({ success: false, error: "User don't exist" }); + } else { + const alreadyExist = await ProjectSchema.findOne({ name: subDomain }); + if (alreadyExist) + return res.status(401).json({ error: "Domain Unavailable" }); + const portArray = await PortSchema.find(); + const port = portArray[0].nextFreePort; + + const project = await ProjectSchema.create({ + name: subDomain, + packageManager, + framework: frameworkPreset, + rootDirectory, + ownerId: theUser.githubId, + githubRepoId, + port, + }); + return res.status(200).json({ success: true, projectId: project._id }); + } + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +// router.post('/clone', async (req, res) => { +// const theProject = await ProjectSchema.findById(req.body.projectId); +// axios.get( +// `https://api.github.com/repositories/${theProject.githubRepoId}`, +// { +// headers: { +// 'Authorization': `Token ${req.body.accessToken}` +// } +// } +// ).then(async response => { +// // console.log(response.data); +// process.chdir('/Users/devarshishimpi/Downloads/'); + +// const gitClone = spawn('git', ['clone', `https://${req.body.accessToken}@github.com/${response.data.full_name}.git`]); + +// let logs = ''; + +// gitClone.stdout.on('data', data => { +// logs += data.toString(); +// }); + +// gitClone.stderr.on('data', data => { +// logs += data.toString(); +// }); + +// gitClone.on('close', code => { +// if (code === 0) { +// return res.status(200).json({ +// logs, +// message: 'Repository cloned successfully' +// }); +// } else { +// return res.status(500).json({ +// logs, +// message: 'Failed to clone repository' +// }); +// } +// }); +// }) +// .catch(error => { +// console.error(error); +// res.json({ success: false }); +// }); +// }); + +router.post("/clone", async (req, res) => { + if (req.body.projectId) { + const theProject = await ProjectSchema.findById(req.body.projectId); + axios + .get(`https://api.github.com/repositories/${theProject.githubRepoId}`, { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // console.log(response.data); + const folderName = theProject.name; // name of the new folder + const path = "/projects"; // path to the parent directory of the new folder + const folderPath = `${path}/${folderName}`; // full path to the new folder + const mkdir = spawn("mkdir", [folderPath]); // create the new folder + + mkdir.on("close", () => { + process.chdir(folderPath); // change the current working directory to the new folder + const gitClone = spawn("git", [ + "clone", + `https://${req.body.accessToken}@github.com/${response.data.full_name}.git`, + ]); + + let logs = ""; + + gitClone.stdout.on("data", (data) => { + logs += data.toString(); + }); + + gitClone.stderr.on("data", (data) => { + logs += data.toString(); + }); + + gitClone.on("close", (code) => { + if (code === 0) { + return res.status(200).json({ + logs, + message: "Repository cloned successfully", + }); + } else { + return res.status(500).json({ + logs, + message: "Failed to clone repository", + }); + } + }); + }); + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); + } else { + res.send("Internal server error!"); + } +}); + +router.post("/install", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + axios + .get(`https://api.github.com/repositories/${theProject.githubRepoId}`, { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // console.log(response.data); + process.chdir(`/projects/${theProject.name}/${response.data.name}`); + + const install = spawn("npm", [ + "install", + "--legacy-peer-deps", + "-C", + theProject.rootDirectory, + ]); + + let logs = ""; + + install.stdout.on("data", (data) => { + logs += data.toString(); + }); + + install.stderr.on("data", (data) => { + logs += data.toString(); + }); + + install.on("close", (code) => { + if (code === 0) { + return res.status(200).json({ + logs, + message: "Dependencies installed successfully", + }); + } else { + return res.status(500).json({ + logs, + message: "Failed to install dependencies", + }); + } + }); + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +router.post("/build", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + axios + .get(`https://api.github.com/repositories/${theProject.githubRepoId}`, { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // console.log(response.data); + process.chdir(`/projects/${theProject.name}/${response.data.name}`); + + const build = spawn("npm", [ + "run", + "build", + "-C", + theProject.rootDirectory, + ]); + + let logs = ""; + + build.stdout.on("data", (data) => { + logs += data.toString(); + }); + + build.stderr.on("data", (data) => { + logs += data.toString(); + }); + + build.on("close", (code) => { + if (code === 0) { + return res.status(200).json({ + logs, + message: "Build successful", + }); + } else { + return res.status(500).json({ + logs, + message: "Build Failed", + }); + } + }); + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +router.post("/copybuild", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + axios + .get(`https://api.github.com/repositories/${theProject.githubRepoId}`, { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // console.log(response.data); + const folderName = theProject.name; // name of the new folder + const path = "/var/www/html"; // path to the parent directory of the new folder + const folderPath = `${path}/${folderName}`; // full path to the new folder + const mkdir = spawn("mkdir", [folderPath]); // create the new folder + + mkdir.on("close", () => { + process.chdir(`/projects/${theProject.name}/${response.data.name}`); // change the current working directory to the new folder + process.chdir(theProject.rootDirectory); // change the current working directory to the new folder + const copyBuild = spawn("cp", ["-rf", "build", folderPath]); + + let logs = ""; + + copyBuild.stdout.on("data", (data) => { + logs += data.toString(); + }); + + copyBuild.stderr.on("data", (data) => { + logs += data.toString(); + }); + + copyBuild.on("close", (code) => { + if (code === 0) { + return res.status(200).json({ + logs, + message: "Copied Successfully", + }); + } else { + return res.status(500).json({ + logs, + message: "Failed to copy", + }); + } + }); + }); + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +router.post("/nginxconf", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + axios + .get(`https://api.github.com/repositories/${theProject.githubRepoId}`, { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + const nextFreePortArray = await PortSchema.find(); + const nextFreePort = nextFreePortArray[0].nextFreePort; + + const filePath = "/etc/nginx/sites-available/default"; + const newServerBlock1 = ` + server { + listen ${nextFreePort} default_server; + listen [::]:${nextFreePort} default_server; + + root /var/www/html/${theProject.name}/build; + + index index.html index.htm index.nginx-debian.html; + + server_name ${theProject.name}.staticstorm.coderush.tech; + + location / { + try_files $uri $uri/ /index.html; + } + } + `; + const newServerBlock2 = ` + server { + listen 80; + server_name ${theProject.name}.staticstorm.coderush.tech; + + location / { + proxy_pass http://localhost:${nextFreePort}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } + `; + + fs.readFile(filePath, "utf-8") + .then((data) => { + const newData = data + newServerBlock1 + newServerBlock2; + return fs.writeFile(filePath, newData, "utf-8"); + }) + .then(() => { + console.log("File updated!"); + }) + .catch((err) => { + console.error(err); + }); + + const updatePort = await PortSchema.updateOne( + { nextFreePort: nextFreePort }, + { nextFreePort: nextFreePort + 1 }, + ); + + res.json({ success: true }); + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +router.post("/deleteconf", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + const nginxConfigPath = "/etc/nginx/sites-available/default"; + const projectToRemove = `${theProject.name}.staticstorm.coderush.tech`; + + try { + // Read the content of the nginx configuration file + let nginxConfig = await fs.readFile(nginxConfigPath, "utf-8"); + // console.log(`Read nginx config: ${nginxConfig}`); + + // Find the two server blocks corresponding to the project to remove + const serverBlockRegex = new RegExp( + `\\s*server {\\s*listen 80;\\s*server_name ${projectToRemove};\\s*location / {\\s*proxy_pass http://localhost:${theProject.port};[^}]*}\\s*}`, + "g", + ); + const matches = nginxConfig.match(serverBlockRegex) || []; + console.log(`Found ${matches.length} matches: ${matches}`); + + const serverBlocksToRemove = matches.join(""); + + // Remove the server blocks from the nginx configuration file content + nginxConfig = nginxConfig.replace(serverBlocksToRemove, ""); + // console.log(`Modified nginx config: ${nginxConfig}`); + + // Write the modified content back to the nginx configuration file + await fs.writeFile(nginxConfigPath, nginxConfig); + // console.log(`Wrote modified nginx config to ${nginxConfigPath}`); + + console.log( + `Removed server blocks for project "${projectToRemove}" from nginx config.`, + ); + } catch (err) { + console.error( + `Error removing project "${projectToRemove}" from nginx config: ${err}`, + ); + return res.json({ success: false, err }); + } + + try { + // Read the content of the nginx configuration file + let nginxConfig = await fs.readFile(nginxConfigPath, "utf-8"); + + // Find the server block corresponding to the project to remove + const serverBlockRegex = new RegExp( + `\\s*server {\\s*listen ${theProject.port} default_server;\\s*listen \\[::\\]:${theProject.port} default_server;\\s*root /var/www/html/${theProject.name}/build;\\s*index index.html index.htm index.nginx-debian.html;\\s*server_name ${projectToRemove};\\s*location / {\\s*try_files \\$uri \\$uri/ /index.html;\\s*}\\s*}`, + "g", + ); + const matches = nginxConfig.match(serverBlockRegex) || []; + + // Remove the server block from the nginx configuration file content + const serverBlockToRemove = matches.join(""); + nginxConfig = nginxConfig.replace(serverBlockToRemove, ""); + + // Write the modified content back to the nginx configuration file + await fs.writeFile(nginxConfigPath, nginxConfig); + + console.log( + `Removed server block for project "${projectToRemove}" from nginx config.`, + ); + + return res.json({ success: true }); + } catch (err) { + console.error( + `Error removing project "${projectToRemove}" from nginx config: ${err}`, + ); + return res.json({ success: false, err }); + } +}); + +router.post("/deleteproject", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + + await fs.rmdir(`/projects/${theProject.name}`, { recursive: true }); + await fs.rmdir(`/var/www/html/${theProject.name}`, { recursive: true }); + + if (theProject) { + await theProject.delete(); + return res.json({ success: true }); + } + res.json({ success: false }); +}); + +router.post("/configurewebhook", async (req, res) => { + const theProject = await ProjectSchema.findById(req.body.projectId); + const theUser = await UserSchema.findOne({ githubId: theProject.ownerId }); + // Define the repository owner, repository name, and access token + const owner = theUser.githubLogin; + const repoId = theProject.githubRepoId; + const accessToken = req.body.accessToken; + + // Make the HTTP request to get the repository information + axios + .get(`https://api.github.com/repositories/${repoId}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "User-Agent": "axios", + }, + }) + .then((response) => { + const repoName = response.data.name; + + // Define the payload for the webhook creation request + const payload = { + name: "web", + config: { + url: "http://abcd.staticstorm.coderush.tech/api/deploy/triggerwebhook", + content_type: "json", + }, + events: ["push"], + active: true, + }; + + // Make the HTTP request to create the webhook + axios + .post( + `https://api.github.com/repos/${owner}/${repoName}/hooks`, + payload, + { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, + "User-Agent": "axios", + }, + }, + ) + .then((response) => { + console.log(`Webhook created: ${response.data.url}`); + res.json({ success: true }); + }) + .catch((error) => { + console.error(`Error creating webhook: ${error}`); + res.json({ success: false }); + }); + }) + .catch((error) => { + console.error(`Error getting repository information: ${error}`); + res.json({ success: false }); + }); +}); + +router.post("/triggerwebhook", async (req, res) => { + // Retrieve the payload data from the request body + const payload = req.body; + console.log(`Received webhook for ${payload.repository.full_name}`); + + const theProject = await ProjectSchema.findOne({ + githubRepoId: payload.repository.id, + }); + + process.chdir(`/projects/${theProject.name}/${payload.repository.name}`); + + const pull = spawn("git", ["pull"]); + + let logs1 = ""; + + pull.stdout.on("data", (data) => { + logs1 += data.toString(); + }); + + pull.stderr.on("data", (data) => { + logs1 += data.toString(); + }); + + pull.on("close", (code) => { + console.log(logs1); + if (code === 0) { + process.chdir(`/projects/${theProject.name}/${payload.repository.name}`); + + const install = spawn("npm", [ + "install", + "--legacy-peer-deps", + "-C", + theProject.rootDirectory, + ]); + + let logs2 = ""; + + install.stdout.on("data", (data) => { + logs2 += data.toString(); + }); + + install.stderr.on("data", (data) => { + logs2 += data.toString(); + }); + + install.on("close", (code) => { + console.log(logs2); + if (code === 0) { + process.chdir( + `/projects/${theProject.name}/${payload.repository.name}`, + ); + + const build = spawn("npm", [ + "run", + "build", + "-C", + theProject.rootDirectory, + ]); + + let logs4 = ""; + + build.stdout.on("data", (data) => { + logs4 += data.toString(); + }); + + build.stderr.on("data", (data) => { + logs4 += data.toString(); + }); + + build.on("close", (code) => { + console.log(logs4); + if (code === 0) { + const folderName = theProject.name; // name of the new folder + const path = "/var/www/html"; // path to the parent directory of the new folder + const folderPath = `${path}/${folderName}`; // full path to the new folder + const mkdir = spawn("mkdir", [folderPath]); // create the new folder + + mkdir.on("close", () => { + process.chdir( + `/projects/${theProject.name}/${payload.repository.name}`, + ); // change the current working directory to the new folder + process.chdir(theProject.rootDirectory); // change the current working directory to the new folder + const copyBuild = spawn("cp", ["-rf", "build", folderPath]); + + let logs3 = ""; + + copyBuild.stdout.on("data", (data) => { + logs3 += data.toString(); + }); + + copyBuild.stderr.on("data", (data) => { + logs3 += data.toString(); + }); + + copyBuild.on("close", (code) => { + if (code === 0) { + console.log(logs3); + + return res.status(200).json({ + logs3, + message: "Copied Successfully", + }); + } else { + return res.status(500).json({ + logs3, + message: "Failed to copy", + }); + } + }); + }); + } else { + return res.status(500).json({ + logs4, + message: "Build Failed", + }); + } + }); + } else { + return res.status(500).json({ + logs2, + message: "Failed to install dependencies", + }); + } + }); + } else { + console.log(logs1); + return res.status(500).json({ + logs1, + message: "Failed to pull new code", + }); + } + }); +}); + +router.post("/reloadnginx", async (req, res) => { + const reload = spawn("systemctl", ["restart", "nginx"]); + + res.status(200).json({ success: true }); +}); + +module.exports = router; diff --git a/server/routes/projects.js b/server/routes/projects.js new file mode 100644 index 0000000..30829bb --- /dev/null +++ b/server/routes/projects.js @@ -0,0 +1,40 @@ +const express = require("express"); +const router = express.Router(); +const UserSchema = require("../models/User"); +const ProjectSchema = require("../models/Project"); +// const { body, validationResult } = require('express-validator'); +const axios = require("axios"); + +// Get All Projects +router.post("/", async (req, res) => { + axios + .get("https://api.github.com/user", { + headers: { + Authorization: `Token ${req.body.accessToken}`, + }, + }) + .then(async (response) => { + // res.json(response.data); + const allProjects = await ProjectSchema.find({ + ownerId: response.data.id, + }); + return res.status(200).json(allProjects); + }) + .catch((error) => { + console.error(error); + res.json({ success: false }); + }); +}); + +router.post("/delete", async (req, res) => { + res.json({ success: false }); + //const theProject = await ProjectSchema.findById(req.body.projectId); + + //if (theProject) { + // await theProject.delete(); + // return res.json({ success: true }); + //} + //res.json({ success: false }); +}); + +module.exports = router;