Skip to content

Commit

Permalink
feat: add token access to instanciator
Browse files Browse the repository at this point in the history
  • Loading branch information
alberto-abarzua committed Nov 19, 2023
1 parent f21b192 commit 73b4bbf
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 16 deletions.
39 changes: 39 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"react-dom": "18.2.0",
"react-dropzone": "^14.2.3",
"react-redux": "^8.1.2",
"react-router-dom": "^6.19.0",
"redux-saga": "^1.2.3",
"shadcn-ui": "^0.4.1",
"tailwind-merge": "^1.14.0",
Expand Down
52 changes: 45 additions & 7 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@ import instanciatorApi from '@/utils/instanciator_api';
import MainView from '@/views/MainView';
import { Loader2 } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';

function isMobileDevice() {
return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

function App() {
const [loading, setLoading] = useState(true);
const [searchParams] = useSearchParams();
const [denied, setDenied] = useState(true);

useEffect(() => {
console.log('useEffect triggered');
if (isMobileDevice()) {
setDenied(true);
setLoading(false);
return;
}

const getBackendInfo = async () => {
console.log('getBackendInfo function called');
Expand All @@ -17,24 +29,39 @@ function App() {

if (envBackendUrl === undefined || envBackendUrl === 'no_backend') {
console.log('Fetching backend URL from API');
const response = await instanciatorApi.get('/backend_url/');
console.log('Response from API:', response);
const { backend_port } = response.data;
const access_token = searchParams.get('access_token');
try {
const response = await instanciatorApi.get('/backend_url/', {
params: { token: access_token },
});
console.log('Response from API:', response);
const { backend_port } = response.data;

const backend_url = `${import.meta.env.VITE_INSTANCIATOR_URL}/s${backend_port}`;
window.localStorage.setItem('backendUrl', backend_url);
console.log('Backend URL saved to local storage from API:', backend_url);
const backend_url = `${import.meta.env.VITE_INSTANCIATOR_URL}/s${backend_port}`;
window.localStorage.setItem('backendUrl', backend_url);
console.log('Backend URL saved to local storage from API:', backend_url);
if (backend_port === undefined) {
console.log('Backend port is undefined');
setDenied(true);
}
setDenied(false);
} catch (error) {
console.log('Error fetching backend URL from API');
setDenied(true);
console.log(error);
}
} else {
console.log('Saving env variable to local storage:', envBackendUrl);
window.localStorage.setItem('backendUrl', envBackendUrl);
setDenied(false);
}
// sleep to make the loading viual
await new Promise(resolve => setTimeout(resolve, 1000));
setLoading(false);
};

getBackendInfo().catch(console.error);
}, []);
}, [searchParams]);

if (loading) {
return (
Expand All @@ -44,6 +71,17 @@ function App() {
</div>
);
}
if (denied) {
return (
<div className="flex h-screen flex-col items-center justify-center">
<p className="text-2xl text-gray-800">Access denied!</p>
<p className="text-center italic text-gray-500">
You do not have permision or are using a mobile device (This is intended for PC
use only)
</p>
</div>
);
}

return <MainView />;
}
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import App from './App.jsx';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

import { BrowserRouter } from 'react-router-dom';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
3 changes: 3 additions & 0 deletions instanciator/.env.temlate
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
HOST=https://api.demo.ribot.dev
ORIGIN=https://demo.ribot.dev
ACCESS_TOKENS=access_token_1,access_token_2
14 changes: 14 additions & 0 deletions instanciator/backend/src/instance_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def __init__(self) -> None:
self.stop_event = Event()
self.instances = []
self.min_instances = 5
self.max_instances = 20
self.check_interval = 20
self.prune_interval = 60 * 60
self.start_instance_checker()
Expand Down Expand Up @@ -219,6 +220,19 @@ def instance_checker_target_fun(self) -> None:
instance.stop()
self.instances.remove(instance)

num_instances = len(self.instances)
if num_instances > self.max_instances:
free_instances = [instance for instance in self.instances if instance.free]
if len(free_instances) == 0:
print("no free instances, can't stop")
oldest_instance = self.instances[-1]
else:
oldest_instance = min(free_instances, key=lambda instance: instance.time_created)


oldest_instance.stop()
self.instances.remove(oldest_instance)

num_free = sum(1 for instance in self.instances if instance.free)

if num_free < self.min_instances:
Expand Down
19 changes: 15 additions & 4 deletions instanciator/backend/src/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import os
import uuid
from typing import Any, Dict, List

from fastapi import FastAPI, Request, Response
from fastapi import FastAPI, Request, Response, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware

from src.instance_generator import InstanceGenerator
Expand All @@ -13,6 +12,19 @@

ORIGIN = os.environ.get("ORIGIN", "http://localhost:3000")


ACCESS_TOKENS = os.environ.get("ACCESS_TOKENS", "").split(",")


def verify_token(token: str):
if token not in ACCESS_TOKENS:
raise HTTPException(status_code=400, detail="Invalid token")
return True


token_dependency = Depends(verify_token)


app = FastAPI()

app.add_middleware(
Expand Down Expand Up @@ -42,9 +54,8 @@ async def destroy_all() -> Dict[str, Any]:
return {"status": "ok"}



@app.get("/backend_url/")
async def get_backend_port(request: Request, response: Response) -> Dict[str, Any]:
async def get_backend_port(request: Request, response: Response, _: str = token_dependency) -> Dict[str, Any]:
instance_id_cookie = request.cookies.get("instance_id")
instance = None
instance_id = None
Expand Down
36 changes: 35 additions & 1 deletion instanciator/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@

from pathlib import Path

SYSTEMD_SERVICE_TEMPLATE = """
[Unit]
Description=Instanciator service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/env python3 {script_command}
WorkingDirectory={working_directory}
Restart=on-failure
[Install]
WantedBy=multi-user.target
"""


CURRENT_FILE_PATH = Path(__file__).parent
Expand Down Expand Up @@ -35,6 +50,22 @@ def source_env(self, file_path):
except Exception as e:
print(f"An error occurred while processing the environment file: {e}")

def create_systemd_service(self):
script_command = f"{CURRENT_FILE_PATH}/deploy.py start"

service_content = SYSTEMD_SERVICE_TEMPLATE.format(script_command,
working_directory=str(CURRENT_FILE_PATH)
)

service_file_path = '/etc/systemd/system/instanciator.service'

with open(service_file_path, 'w') as file:
file.write(service_content)

subprocess.run(['systemctl', 'daemon-reload'])
subprocess.run(['systemctl', 'enable', 'mycustomservice'])
subprocess.run(['systemctl', 'start', 'mycustomservice'])

def parse_and_execute(self):
parser = argparse.ArgumentParser(description='Deploy services')
parser.add_argument('action', choices=['start', 'stop', 'restart', 'status'])
Expand All @@ -44,7 +75,10 @@ def parse_and_execute(self):

if args.action == 'start':
subprocess.run(['docker', 'compose', 'up', '-d'])
subprocess.check_call(['pdm', 'run', 'start'], env=os.environ,cwd='backend')
subprocess.check_call(['pdm', 'run', 'start'], env=os.environ, cwd='backend')
elif args.action == 'setup':
subprocess.run(['docker', 'compose', 'up', '-d'])
subprocess.check_call(['pdm', 'install'], env=os.environ, cwd='backend')

elif args.action == 'stop':
subprocess.run(['docker', 'compose', 'down', '--remove-orphans'])
Expand Down

0 comments on commit 73b4bbf

Please sign in to comment.