Skip to content

Commit

Permalink
feat: implement admin panel and docker support (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhijna-Raghavendra authored May 15, 2024
2 parents f8bdb0c + 86675a2 commit 1d31983
Show file tree
Hide file tree
Showing 13 changed files with 94 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/github-actions-ec2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
username: ${{ secrets.USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd ~/project-domain-forge/domain-forge/docker
cd ~/domain-forge/docker
docker compose down
git pull origin master
docker compose up -d --build
6 changes: 3 additions & 3 deletions docs/admin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This step involves the configuration of three `.env` files:
SENTRY_DSN=...
```
- Frontend env:
As suggested in the `.env.sample` present at the `src/frontend` directory, create a file named `.env` and copy the contents as shown, replacing *"..."* with the respective values for your registered github oauth application's **VITE_APP_GITHUB_OAUTH_CLIENT_ID** and **VITE_APP_GITHUB_OAUTH_CLIENT_SECRET**. For the **VITE_APP_GITHUB_OAUTH_REDIRECT_URL** enter the public url of the application's `/login` route. (Example: `http://df.mdgspace.org/login`). Also, add the port at which your backend is running in place of *"XXXX"*.
As suggested in the `.env.sample` present at the `src/frontend` directory, create a file named `.env` and copy the contents as shown, replacing *"..."* with the respective values for your registered github oauth application's **VITE_APP_GITHUB_OAUTH_CLIENT_ID** and **VITE_APP_GITHUB_OAUTH_CLIENT_SECRET**. For the **VITE_APP_GITHUB_OAUTH_REDIRECT_URL** enter the public url of the application's `/login` route. (Example: `https://domains.mdgspace.org/login`). Also, add the port at which your backend is running in place of *"XXXX"*.
```
VITE_APP_GITHUB_OAUTH_CLIENT_ID=...
VITE_APP_GITHUB_OAUTH_CLIENT_SECRET=...
Expand Down Expand Up @@ -61,6 +61,6 @@ Refer to [this](https://www.digitalocean.com/community/tutorials/how-to-set-up-n

### 6. Adding the DNS Records and Issue SSL Certificates

Refer to [this](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/) to add DNS records for *df.yourorgname.com* and also a wildcard DNS record for **.df.yourorgname.com*
Refer to [this](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/) to add DNS records for *domains.yourorgname.com* and also a wildcard DNS record for **.domains.yourorgname.com*

You can use [letsencrypt](https://letsencrypt.org/) to issue SSL certificates for *df.yourorgname.com* and a wildcard SSL certificate for **.df.yourorgname.com*.
You can use [letsencrypt](https://letsencrypt.org/) to issue SSL certificates for *domains.yourorgname.com* and a wildcard SSL certificate for **.domains.yourorgname.com*.
4 changes: 3 additions & 1 deletion src/backend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ GITLAB_OAUTH_CLIENT_SECRET=...
MONGO_API_KEY=...
MONGO_APP_ID=...
SENTRY_DSN=...
FRONTEND=...
FRONTEND=...
ADMIN_LIST=admin1|admin2
MEMORY_LIMIT=500m
13 changes: 9 additions & 4 deletions src/backend/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ async function checkUser(accessToken: string, provider: string) {
}

// Get all content maps corresponding to user
async function getMaps(author: string) {
async function getMaps(author: string, ADMIN_LIST: string[]) {
const filter = ADMIN_LIST?.includes(author) ? {} : { "author": author };
const query = {
collection: "content_maps",
database: DATABASE,
dataSource: DATA_SOURCE,
filter: { "author": author },
filter: filter,
};
options.body = JSON.stringify(query);
const resp = await fetch(MONGO_URLs.find.toString(), options);
Expand Down Expand Up @@ -96,12 +97,16 @@ async function addMaps(document: DfContentMap) {
}

// Delete content maps
async function deleteMaps(document: DfContentMap) {
async function deleteMaps(document: DfContentMap, ADMIN_LIST: string[]) {
const filter = JSON.parse(JSON.stringify(document));
if (ADMIN_LIST.includes(document.author)) {
delete filter.author;
}
const query = {
collection: "content_maps",
database: DATABASE,
dataSource: DATA_SOURCE,
filter: document,
filter: filter,
};
options.body = JSON.stringify(query);

Expand Down
9 changes: 7 additions & 2 deletions src/backend/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { addScript, deleteScript } from "./scripts.ts";
import { checkJWT } from "./utils/jwt.ts";
import { addMaps, deleteMaps, getMaps } from "./db.ts";

const ADMIN_LIST = Deno.env.get("ADMIN_LIST")?.split("|");

async function getSubdomains(ctx: Context) {
const author = ctx.request.url.searchParams.get("user");
const token = ctx.request.url.searchParams.get("token");
const provider = ctx.request.url.searchParams.get("provider");
if (author != await checkJWT(provider!, token!)) {
ctx.throw(401);
}
const data = await getMaps(author);
const data = await getMaps(author, ADMIN_LIST!);
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
ctx.response.body = data.documents;
}
Expand All @@ -33,9 +35,11 @@ async function addSubdomain(ctx: Context) {
delete document.provider;
delete document.port;
delete document.build_cmds;
delete document.dockerfile_present;
delete document.stack;
delete document.env_content;
delete document.static_content;

if (document.author != await checkJWT(provider, token)) {
ctx.throw(401);
}
Expand All @@ -47,6 +51,7 @@ async function addSubdomain(ctx: Context) {
document,
copy.env_content,
copy.static_content,
copy.dockerfile_present,
copy.stack,
copy.port,
copy.build_cmds,
Expand Down Expand Up @@ -80,7 +85,7 @@ async function deleteSubdomain(ctx: Context) {
if (author != await checkJWT(provider, token)) {
ctx.throw(401);
}
const data = await deleteMaps(document);
const data = await deleteMaps(document, ADMIN_LIST!);
if (data.deletedCount) {
deleteScript(document);
Sentry.captureMessage(
Expand Down
17 changes: 14 additions & 3 deletions src/backend/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { exec } from "./dependencies.ts";
import dockerize from "./utils/container.ts";
import DfContentMap from "./types/maps_interface.ts";

const MEMORY_LIMIT = Deno.env.get("MEMORY_LIMIT");

async function addScript(
document: DfContentMap,
env_content: string,
static_content: string,
dockerfile_present:string,
stack: string,
port: string,
build_cmds: string,
Expand All @@ -21,15 +24,23 @@ async function addScript(
} else if (document.resource_type === "GITHUB" && static_content == "Yes") {
Deno.writeTextFile(`/hostpipe/.env`, env_content);
await exec(
`bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -s ${document.subdomain} ${document.resource}' > /hostpipe/pipe"`,
`bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -s ${document.subdomain} ${document.resource} 80 ${MEMORY_LIMIT}' > /hostpipe/pipe"`,
);
} else if (document.resource_type === "GITHUB" && static_content == "No") {
const dockerfile = dockerize(stack, port, build_cmds);
if(dockerfile_present === 'No'){
const dockerfile = dockerize(stack, port, build_cmds);
Deno.writeTextFile(`/hostpipe/Dockerfile`, dockerfile);
Deno.writeTextFile(`/hostpipe/.env`, env_content);
await exec(
`bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -g ${document.subdomain} ${document.resource} ${port}' > /hostpipe/pipe"`,
`bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -g ${document.subdomain} ${document.resource} ${port} ${MEMORY_LIMIT}' > /hostpipe/pipe"`,
);
}else if(dockerfile_present === 'Yes'){

await exec(
`bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -d ${document.subdomain} ${document.resource} ${port} ${MEMORY_LIMIT}' > /hostpipe/pipe"`,
);
}

}
}

Expand Down
10 changes: 5 additions & 5 deletions src/backend/shell_scripts/automate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ if [ "$arg1" = "-u" ]; then
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
listen [::]:443 ssl;
server_name $arg3;
location / {
return 307 $arg2;
}
charset utf-8;
client_max_body_size 20M;
ssl_certificate /etc/letsencrypt/live/df.mdgspace.org-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/df.mdgspace.org-0001/privkey.pem;
ssl_certificate /etc/letsencrypt/live/domains.mdgspace.org-0002/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domains.mdgspace.org-0002/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}" > /etc/nginx/sites-available/$arg3.conf;
Expand Down Expand Up @@ -62,8 +62,8 @@ elif [ "$arg1" = "-p" ]; then
}
charset utf-8;
client_max_body_size 20M;
ssl_certificate /etc/letsencrypt/live/df.mdgspace.org-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/df.mdgspace.org-0001/privkey.pem;
ssl_certificate /etc/letsencrypt/live/domains.mdgspace.org-0002/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domains.mdgspace.org-0002/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}" > /etc/nginx/sites-available/$arg3.conf;
Expand Down
92 changes: 27 additions & 65 deletions src/backend/shell_scripts/container.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ PORT_MAX=8099
flag=$1
name=$2
resource=$3
exp_port=$4
exp_port=$4
max_mem=$5

available_ports=()

Expand All @@ -15,23 +16,29 @@ done

echo "Available ports: ${available_ports[56]}"
AVAILABLE=0
echo "Creating subdomain $name"
git clone $resource $name
sudo cp .env $name/
cd $name

if [ $flag = "-g" ]; then
echo "Creating subdomain $name"
git clone $resource $name
sudo cp Dockerfile $name/
sudo cp .env $name/
cd $name
sudo docker build -t $name .
echo ${available_ports[$AVAILABLE]};
sudo docker run --name=$name -d -p ${available_ports[$AVAILABLE]}:$exp_port $2
cd ..
sudo rm -rf $name
sudo rm Dockerfile
sudo rm .env
sudo cp ../Dockerfile ./
elif [ $flag = "-s" ]; then
sudo echo "
FROM nginx:alpine
COPY . /usr/share/nginx/html
" > Dockerfile
fi

sudo touch /etc/nginx/sites-available/$2.conf
sudo chmod 666 /etc/nginx/sites-available/$2.conf
sudo echo "# Virtual Host configuration for example.com
sudo docker build -t $name .
sudo docker run --memory=$max_mem --name=$name -d -p ${available_ports[$AVAILABLE]}:$exp_port $2
cd ..
sudo rm -rf $name
sudo rm Dockerfile
sudo rm .env
sudo touch /etc/nginx/sites-available/$2.conf
sudo chmod 666 /etc/nginx/sites-available/$2.conf
sudo echo "# Virtual Host configuration for $2
server {
listen 80;
listen [::]:80;
Expand All @@ -48,55 +55,10 @@ if [ $flag = "-g" ]; then
}
charset utf-8;
client_max_body_size 20M;
ssl_certificate /etc/letsencrypt/live/df.mdgspace.org-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/df.mdgspace.org-0001/privkey.pem;
ssl_certificate /etc/letsencrypt/live/domains.mdgspace.org-0002/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domains.mdgspace.org-0002/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}" > /etc/nginx/sites-available/$2.conf
sudo ln -s /etc/nginx/sites-available/$2.conf /etc/nginx/sites-enabled/$2.conf
sudo systemctl reload nginx

else
echo "Creating subdomain $name"
git clone $resource $name
sudo cp .env $name/
cd $name
sudo echo "
FROM nginx:alpine
COPY . /usr/share/nginx/html
" > Dockerfile
sudo docker build -t $name .
sudo docker run --name=$name -d -p ${available_ports[$AVAILABLE]}:80 $name
cd ..
sudo rm .env
sudo rm -rf $name
sudo touch /etc/nginx/sites-available/$2.conf
sudo chmod 666 /etc/nginx/sites-available/$2.conf
sudo echo "# Virtual Host configuration for example.com
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name $2;
location / {
proxy_pass http://localhost:${available_ports[$AVAILABLE]};
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_cache_bypass \$http_upgrade;
}
charset utf-8;
client_max_body_size 20M;
ssl_certificate /etc/letsencrypt/live/df.mdgspace.org-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/df.mdgspace.org-0001/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}" > /etc/nginx/sites-available/$2.conf;
sudo ln -s /etc/nginx/sites-available/$2.conf /etc/nginx/sites-enabled/$2.conf;
sudo systemctl reload nginx;
fi



sudo ln -s /etc/nginx/sites-available/$2.conf /etc/nginx/sites-enabled/$2.conf
sudo systemctl reload nginx
7 changes: 4 additions & 3 deletions src/backend/utils/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ export default function dockerize(
build_cmds: string,
) {
let dockerfile = "";
build_cmds = build_cmds.replace(/\r?\n$/, '');
const run_cmd = build_cmds.split("\n");
const execute_cmd = "CMD " + JSON.stringify(run_cmd.pop()?.split(" "));
const build_cmds_mapped = run_cmd.map((elem) => {
return "RUN " + elem;
}).join("\n");
if (stack == "Python") {
dockerfile =
"FROM python:3.11 \nWORKDIR /app \nCOPY requirements.txt . \nRUN pip install --no-cache-dir -r requirements.txt \nCOPY . ." +
"FROM python:latest \nWORKDIR /app \nCOPY requirements.txt . \nRUN pip install --no-cache-dir -r requirements.txt \nCOPY . ." +
build_cmds_mapped + `\nEXPOSE ${port}\n` + execute_cmd;
} else if (stack == "NodeJS") {
dockerfile =
"FROM node:latest \n WORKDIR /app \n COPY ./package*.json . \n RUN npm install \n COPY . ." +
build_cmds_mapped + `\n EXPOSE ${port} \n` + execute_cmd;
"FROM node:latest \nWORKDIR /app \nCOPY ./package*.json . \nRUN npm install \nCOPY . ." +
build_cmds_mapped + `\nEXPOSE ${port} \n` + execute_cmd;
}
return dockerfile.toString();
}
3 changes: 2 additions & 1 deletion src/frontend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ VITE_APP_GITHUB_OAUTH_REDIRECT_URL=.../login
VITE_APP_GITLAB_OAUTH_CLIENT_ID=...
VITE_APP_GITLAB_OAUTH_CLIENT_SECRET=...
VITE_APP_GITLAB_OAUTH_REDIRECT_URL=.../login
VITE_APP_BACKEND=...
VITE_APP_BACKEND=...
VITE_APP_DOMAIN=your-domain.com
17 changes: 15 additions & 2 deletions src/frontend/src/components/modal.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script setup>
const domain = import.meta.env.VITE_APP_DOMAIN
</script>
<template>
<div class="modal-overlay">
<div class="modal">
<div class="close">
<button class="close-button" @click="closeModal">X</button>
</div>
<h5>Enter the details for creating your subdomain:</h5>
<p>Subdomain:<br><input class="input-field" v-model="subdomain" />.df.mdgspace.org</p>
<p>Subdomain:<br><input class="input-field" v-model="subdomain" />.{{ domain }}</p>
<p>Resource Type:<br>
<select class="dropdown" v-model="resource_type">
<option v-for="option in resourceTypes" :key="option">{{ option }}</option>
Expand All @@ -21,12 +24,21 @@
<input name="radio" type="radio" value="No" v-model="static_content"> No
</div>
<div v-if="static_content === 'No'" class="stack-section">
<div class="docker-content">
<label for="dockerfile-content">Do you have dockerfile in your repo ?</label><br>
<input name="radio" type="radio" value="Yes" v-model="dockerfile_present"> Yes
<input name="radio" type="radio" value="No" v-model="dockerfile_present"> No
</div>
<div v-if="dockerfile_present === 'No'" class="dockerfile-section">
<p>Stack:</p>
<select class="dropdown" v-model="stack">
<option v-for="option in stacks" :key="option">{{ option }}</option>
</select>
</div>
<p>Port:<br><input class="input-field" v-model="port" /></p>
<div v-if="dockerfile_present === 'No'" class="dockerfile-section">
<p>Build Commands:<br><textarea class="textarea-field" cols="50" rows="10" v-model="build_cmds"></textarea></p>
</div>
</div>
</div>
<div class="button-container">
Expand All @@ -48,6 +60,7 @@ export default {
resource: '',
env_content: 'key1 = value1', // Default prompt text
static_content: 'No',
dockerfile_present :'No',
port: '',
stack: '',
build_cmds: '',
Expand All @@ -58,7 +71,7 @@ export default {
methods: {
submitForm() {
console.log(this.subdomain, this.resource_type, this.resource);
create(this.subdomain, this.resource_type, this.resource, this.env_content, this.static_content, this.port, this.stack, this.build_cmds)
create(this.subdomain, this.resource_type, this.resource, this.env_content, this.static_content,this.dockerfile_present,this.port, this.stack, this.build_cmds)
.then((res) => {
console.log(res);
if (res === 'Submitted') {
Expand Down
Loading

0 comments on commit 1d31983

Please sign in to comment.