Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions BuildDash/backend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
AM_HOST=IP_OF_DB_1
AM_PORT=Port_of_DB_1
AM_USER_NAME=username_1
AM_PASS=password_1
AM_DB_NAME=db_name_1

DB_HOST=IP_OF_DB_2
DB_PORT=Port_of_DB_2
DB_USER_NAME=username_2
DB_PASS=password_2
DATABASE_NAME=db_name_2
CONNECTIONS_POOL=10

Empty file.
27 changes: 27 additions & 0 deletions BuildDash/backend/app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from pydantic_settings import BaseSettings
from pydantic import Field
from dotenv import load_dotenv

load_dotenv()

class Settings(BaseSettings):
AM_HOST: str = Field(..., env='AM_HOST')
AM_PORT: int = Field(..., env='AM_PORT')
AM_USER_NAME: str = Field(..., env='AM_USER_NAME')
AM_PASS: str = Field(..., env='AM_PASS')
AM_DB_NAME: str = Field(..., env='AM_DB_NAME')

DB_HOST: str = Field(..., env='DB_HOST')
DB_PORT: int = Field(..., env='DB_PORT')
DB_USER_NAME: str = Field(..., env='DB_USER_NAME')
DB_PASS: str = Field(..., env='DB_PASS')
DATABASE_NAME: str = Field(..., env='DATABASE_NAME')
CONNECTIONS_POOL: int = Field(..., env='CONNECTIONS_POOL')

# SQLite settings
SQLITE_DB_PATH: str = "layouts.db"

class Config:
env_file = ".env"

settings = Settings()
41 changes: 41 additions & 0 deletions BuildDash/backend/app/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from sqlalchemy import create_engine, inspect
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from .config import settings
from .models.layout import Base

# Create connection strings
am_db_url = f"postgresql://{settings.AM_USER_NAME}:{settings.AM_PASS}@{settings.AM_HOST}:{settings.AM_PORT}/{settings.AM_DB_NAME}"
landing_db_url = f"postgresql://{settings.DB_USER_NAME}:{settings.DB_PASS}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DATABASE_NAME}"

# Create engines
am_engine = create_engine(am_db_url, pool_size=settings.CONNECTIONS_POOL)
landing_engine = create_engine(landing_db_url, pool_size=settings.CONNECTIONS_POOL)

# SQLite setup
SQLITE_URL = f"sqlite:///{settings.SQLITE_DB_PATH}"
ASYNC_SQLITE_URL = f"sqlite+aiosqlite:///{settings.SQLITE_DB_PATH}"

sqlite_engine = create_engine(SQLITE_URL)
async_sqlite_engine = create_async_engine(ASYNC_SQLITE_URL)
AsyncSessionLocal = sessionmaker(async_sqlite_engine, class_=AsyncSession, expire_on_commit=False)

# Create tables
Base.metadata.create_all(bind=sqlite_engine)

async def get_db():
async with AsyncSessionLocal() as session:
yield session

def get_all_tables():
tables = {}

# Get tables from asset_manager
inspector = inspect(am_engine)
tables['asset_manager'] = inspector.get_table_names()

# Get tables from landing_tables
inspector = inspect(landing_engine)
tables['landing_tables'] = inspector.get_table_names()

return tables
16 changes: 16 additions & 0 deletions BuildDash/backend/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .routes import tables, layouts

app = FastAPI()

app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

app.include_router(tables.router, prefix="/api")
app.include_router(layouts.router, prefix="/api")
21 changes: 21 additions & 0 deletions BuildDash/backend/app/models/layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from sqlalchemy import Column, Integer, String, JSON, DateTime
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_base()

class Layout(Base):
__tablename__ = "layouts"

id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, index=True)
layout_config = Column(JSON)
widgets_config = Column(JSON)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class Config:
orm_mode = True

def __repr__(self):
return f"<Layout {self.name}>"
140 changes: 140 additions & 0 deletions BuildDash/backend/app/routes/layouts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, insert
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
from ..database import get_db
from ..models.layout import Layout

router = APIRouter()

class LayoutConfig(BaseModel):
i: str
x: int
y: int
w: int
h: int

class LayoutCreate(BaseModel):
name: str
layout_config: List[dict]

class LayoutResponse(BaseModel):
id: int
name: str
layout_config: List[dict]
created_at: datetime
updated_at: datetime

class Config:
from_attributes = True

@router.post("/layouts", response_model=LayoutResponse)
async def create_layout(layout: LayoutCreate, db: AsyncSession = Depends(get_db)):
try:
# Check if layout with this name already exists
existing = await db.execute(select(Layout).filter(Layout.name == layout.name))
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="Layout with this name already exists")

now = datetime.utcnow()
new_layout = Layout(
name=layout.name,
layout_config=layout.layout_config,
created_at=now,
updated_at=now
)

db.add(new_layout)
await db.commit()
await db.refresh(new_layout)

return new_layout
except HTTPException as he:
await db.rollback()
raise he
except Exception as e:
await db.rollback()
raise HTTPException(status_code=500, detail=str(e))

@router.get("/layouts", response_model=List[LayoutResponse])
async def get_layouts(db: AsyncSession = Depends(get_db)):
try:
result = await db.execute(select(Layout))
layouts = result.scalars().all()
return layouts
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@router.get("/layouts/{layout_id}", response_model=LayoutResponse)
async def get_layout(layout_id: int, db: AsyncSession = Depends(get_db)):
try:
result = await db.execute(select(Layout).filter(Layout.id == layout_id))
layout = result.scalar_one_or_none()

if layout is None:
raise HTTPException(status_code=404, detail="Layout not found")

return layout
except ValueError:
# If layout_id cannot be converted to int, try to find by name
try:
result = await db.execute(select(Layout).filter(Layout.name == layout_id))
layout = result.scalar_one_or_none()
if layout is None:
raise HTTPException(status_code=404, detail="Layout not found")
return layout
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@router.get("/layouts/name/{layout_name}", response_model=LayoutResponse)
async def get_layout_by_name(layout_name: str, db: AsyncSession = Depends(get_db)):
try:
result = await db.execute(select(Layout).filter(Layout.name == layout_name))
layout = result.scalar_one_or_none()

if layout is None:
raise HTTPException(status_code=404, detail="Layout not found")

return layout
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@router.put("/layouts/{layout_id}", response_model=LayoutResponse)
async def update_layout(
layout_id: int,
layout_update: LayoutCreate,
db: AsyncSession = Depends(get_db)
):
try:
layout = await db.get(Layout, layout_id)
if layout is None:
raise HTTPException(status_code=404, detail="Layout not found")

layout.name = layout_update.name
layout.layout_config = layout_update.layout_config
layout.updated_at = datetime.utcnow()

await db.commit()
await db.refresh(layout)
return layout
except Exception as e:
await db.rollback()
raise HTTPException(status_code=500, detail=str(e))

@router.delete("/layouts/{layout_id}")
async def delete_layout(layout_id: int, db: AsyncSession = Depends(get_db)):
try:
layout = await db.get(Layout, layout_id)
if layout is None:
raise HTTPException(status_code=404, detail="Layout not found")

await db.delete(layout)
await db.commit()
return {"message": "Layout deleted successfully"}
except Exception as e:
await db.rollback()
raise HTTPException(status_code=500, detail=str(e))
Loading