Skip to content

Commit

Permalink
Merge branch 'main' of github.com:hotosm/Drone-TM into feat/create-pr…
Browse files Browse the repository at this point in the history
…oject-form
  • Loading branch information
Prajwalism committed Jul 2, 2024
2 parents c6cc35f + 1bd1716 commit da5c7c6
Show file tree
Hide file tree
Showing 28 changed files with 1,600 additions and 1,275 deletions.
29 changes: 29 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# For Database and Backend
POSTGRES_USER=dtm
POSTGRES_DB=dtm_db
POSTGRES_PASSWORD=dtm
POSTGRES_HOST=db

# For MinIO and Backend
S3_BUCKET_NAME=dtm-bucket
S3_ACCESS_KEY=SAMPLEACCESSKEYFORMINIOROOT
S3_SECRET_KEY=SAMPLESECRETACCESSKEYFORMINIOROOT

GOOGLE_CLIENT_ID="xxxxxxxxx-xxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="GOOGLE_CLIENT_SECRET"
SECRET_KEY=SUPERSECRETKEY

EXTRA_CORS_ORIGINS=["http://localhost:3040"]

# For Frontend
SITE_NAME="DTM-Drone Tasking Manager"

# BACKEND URL USE
BASE_URL=http://localhost:8000/api
API_URL_V1=http://localhost:8000/api

# Pattern goes as <MINIO_HOST>:<MINIO_PORT>/<MINIO_BUCKET> or any object storage path
STATIC_BASE_URL=http://localhost:9000/frontendstatic/

# use development for frontend as dev else live ["development", "live"]
DOCKER_TARGET=development
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
name: Build and Deploy Drone Tasking Manager
name: Build and Deploy Drone Tasking Manager Backend

on:
push:
branches:
- main
paths:
- src/backend/**
workflow_dispatch:

permissions:
Expand Down
78 changes: 78 additions & 0 deletions .github/workflows/build_and_deploy_DTM_frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Build and Deploy Drone Tasking Manager Frontend

on:
push:
branches:
- main
paths:
- src/frontend/**
workflow_dispatch:

permissions:
id-token: write
contents: read

env:
AWS_REGION: ap-south-1
S3_BUCKET: dronetm.naxa.com.np

jobs:
build:
name: Build JavaScript assets
runs-on: ubuntu-latest
environment:
name: ${{ github.ref_name }}
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
ref: ${{ github.ref_name }}

- name: Use Node.js 19.x
uses: actions/setup-node@v1
with:
node-version: 19.x

- name: Install yarn
working-directory: ./src/frontend/
run: npm install -g yarn

- name: Cache node_modules
uses: actions/cache@v2
with:
path: ./src/frontend/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Write Environment Variables
id: write_env
working-directory: ./src/frontend/
run: |
echo ${{ vars.FRONTEND_ENV_VARS }} > .env
- name: Install dependencies
working-directory: ./src/frontend/
run: yarn

- name: Generate build
working-directory: ./src/frontend/
run: |
yarn build
- name: Setup AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
audience: sts.amazonaws.com
aws-region: ${{ env.AWS_REGION }}
role-session-name: GH-Actions-${{ github.run_id }}-${{ github.run_attempt }}
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}

- name: Copy Static Files to S3
working-directory: ./src/frontend/
run: |
pwd
ls -alh
aws s3 cp --recursive ./dist s3://${{ env.S3_BUCKET }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,6 @@ chart/charts

#Docker
DockerData/

#Backend Template
src/backend/templates/*
33 changes: 24 additions & 9 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,44 @@
version: "3"

volumes:
dtm_db_data:

services:
web:
backend:
build:
context: .
dockerfile: src/backend/Dockerfile
restart: always
depends_on:
- db
- minio
ports:
- 8002:8000
- ${BACKEND_WEB_APP_PORT:-8000}:8000
volumes:
- ./src/backend:/project/src/backend
env_file: .env
networks:
- dtm-network
container_name: api

frontend:
build:
context: .
dockerfile: src/frontend/Dockerfile
target: ${DOCKER_TARGET:-development}
ports:
- ${FRONTEND_WEB_APP_PORT:-3040}:3040
depends_on:
- minio
env_file: .env
networks:
- dtm-network
volumes:
- ./src/frontend:/app
- /var/run/docker.sock:/var/run/docker.sock
- ./src/frontend/docker-entrypoint.sh:/docker-entrypoint.sh

db:
image: postgis/postgis:14-3.4-alpine
restart: always
volumes:
- dtm_db_data:/var/lib/postgresql/data/
- ./DockerData/dtm_db_data:/var/lib/postgresql/data/
env_file: .env
networks:
- dtm-network
Expand All @@ -44,7 +60,6 @@ services:
MINIO_ROOT_USER: ${S3_ACCESS_KEY:-dtm_user}
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY:-somelongpassword}
MINIO_CONSOLE_ADDRESS: ":9090"
MINIO_ENDPOINT: ${S3_ENDPOINT:-http://s3:9000}
ports:
- 9000:9000
- 9090:9090
Expand All @@ -70,7 +85,7 @@ services:
volumes:
- ./src/backend:/project/src/backend
depends_on:
- web
- backend
- db
env_file:
- .env
Expand Down
4 changes: 0 additions & 4 deletions env_sample.txt

This file was deleted.

5 changes: 3 additions & 2 deletions src/backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def assemble_cors_origins(
default_origins += val
return default_origins

API_PREFIX: str = "/"
API_PREFIX: str = "/api"
SECRET_KEY: str = secrets.token_urlsafe(32)

POSTGRES_HOST: Optional[str] = "db"
Expand Down Expand Up @@ -78,12 +78,13 @@ def assemble_db_connection(cls, v: Optional[str], info: ValidationInfo) -> Any:
S3_BUCKET_NAME: str = "dtm-data"
S3_DOWNLOAD_ROOT: Optional[str] = None

ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 1 # 1 day
REFRESH_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 # 8 day

GOOGLE_CLIENT_ID: str
GOOGLE_CLIENT_SECRET: str
GOOGLE_LOGIN_REDIRECT_URI: str = "http://localhost:8002"
GOOGLE_LOGIN_REDIRECT_URI: str = "http://localhost:8000"


@lru_cache
Expand Down
61 changes: 47 additions & 14 deletions src/backend/app/db/database.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
"""Config for the DTM database connection."""

from databases import Database
from app.config import settings
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

from app.config import settings
Base = declarative_base()

engine = create_engine(
settings.DTM_DB_URL.unicode_string(),
pool_size=20,
max_overflow=-1,
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()
DtmMetadata = Base.metadata
class DatabaseConnection:
"""Manages database connection (sqlalchemy & encode databases)"""

def __init__(self):
self.database = Database(
settings.DTM_DB_URL.unicode_string(), min_size=5, max_size=20
)
# self.database = Database(settings.DTM_DB_URL.unicode_string())
self.engine = create_engine(
settings.DTM_DB_URL.unicode_string(),
pool_size=20,
max_overflow=-1,
)
self.SessionLocal = sessionmaker(
autocommit=False, autoflush=False, bind=self.engine
)

async def connect(self):
"""Connect to the database."""
await self.database.connect()

async def disconnect(self):
"""Disconnect from the database."""
await self.database.disconnect()

def create_db_session(self):
"""Create a new SQLAlchemy DB session."""
db = self.SessionLocal()
try:
return db
finally:
db.close()


db_connection = DatabaseConnection() # Create a single instance


def get_db():
"""Create SQLAlchemy DB session."""
db = SessionLocal()
"""Yield a new database session."""
return db_connection.create_db_session()


async def encode_db():
"""Get the encode database connection"""
try:
yield db
await db_connection.connect()
yield db_connection.database
finally:
db.close()
await db_connection.disconnect()
7 changes: 4 additions & 3 deletions src/backend/app/db/db_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
ProjectVisibility,
MappingLevel,
ProjectPriority,
UserRole,
)
from sqlalchemy.orm import (
object_session,
Expand All @@ -36,16 +37,16 @@
class DbUser(Base):
__tablename__ = "users"

id = cast(int, Column(BigInteger, primary_key=True))
id = cast(str, Column(String, primary_key=True))
username = cast(str, Column(String, nullable=False, unique=True))
password = cast(str, Column(String))
is_active = cast(bool, Column(Boolean, default=False))
is_superuser = cast(bool, Column(Boolean, default=False))
profile_img = cast(str, Column(String, nullable=True))
name = cast(str, Column(String))
city = cast(str, Column(String))
country = cast(str, Column(String))
email_address = cast(str, Column(String, nullable=False, unique=True))
role = cast(UserRole, Column(Enum(UserRole), default=UserRole.DRONE_PILOT))
date_registered = cast(datetime, Column(DateTime, default=timestamp))


class DbOrganisation(Base):
Expand Down
47 changes: 42 additions & 5 deletions src/backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import os
import logging
import sys
from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
from app.projects import project_routes
from app.waypoints import waypoint_routes
from fastapi.responses import RedirectResponse
from fastapi.responses import RedirectResponse, JSONResponse
from app.users import oauth_routes
from app.users import user_routes
from loguru import logger as log
from fastapi.templating import Jinja2Templates


root = os.path.dirname(os.path.abspath(__file__))
templates = Jinja2Templates(directory="templates")


class InterceptHandler(logging.Handler):
Expand Down Expand Up @@ -68,6 +74,9 @@ def get_application() -> FastAPI:
"url": "https://raw.githubusercontent.com/hotosm/fmtm/main/LICENSE.md",
},
debug=settings.DEBUG,
docs_url="/api/docs",
openapi_url="/api/openapi.json",
redoc_url="/api/redoc",
)

# Set custom logger
Expand All @@ -94,6 +103,34 @@ def get_application() -> FastAPI:


@api.get("/")
async def home():
"""Redirect home to docs."""
return RedirectResponse("/docs")
async def home(request: Request):
try:
"""Return Frontend HTML"""
return templates.TemplateResponse(
name="index.html", context={"request": request}
)
except Exception:
"""Fall back if tempalate missing. Redirect home to docs."""
return RedirectResponse(f"{settings.API_PREFIX}/docs")


known_browsers = ["Mozilla", "Chrome", "Safari", "Opera", "Edge", "Firefox"]


@api.exception_handler(404)
async def custom_404_handler(request: Request, _):
"""Return Frontend HTML or throw 404 Response on 404 requests."""
try:
query_params = dict(request.query_params)
user_agent = request.headers.get("User-Agent", "")
format = query_params.get("format")
is_browser = any(browser in user_agent for browser in known_browsers)
if format == "json" or not is_browser:
return JSONResponse(status_code=404, content={"detail": "Not found"})
return templates.TemplateResponse(
name="index.html", context={"request": request}
)

except Exception:
"""Fall back if tempalate missing. Redirect home to docs."""
return JSONResponse(status_code=404, content={"detail": "Not found"})
Loading

0 comments on commit da5c7c6

Please sign in to comment.