fixed comments
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,6 @@
|
||||
# core/celery_app.py
|
||||
from celery import Celery
|
||||
from core.config import settings # Import your settings
|
||||
|
||||
from core.config import settings
|
||||
celery_app = Celery(
|
||||
"worker",
|
||||
broker=settings.REDIS_URL,
|
||||
@@ -9,9 +8,5 @@ celery_app = Celery(
|
||||
include=[
|
||||
"modules.auth.tasks",
|
||||
"modules.admin.tasks",
|
||||
], # Add paths to modules containing tasks
|
||||
# Add other modules with tasks here, e.g., "modules.some_other_module.tasks"
|
||||
)
|
||||
|
||||
# Optional: Update Celery configuration directly if needed
|
||||
# celery_app.conf.update(task_track_started=True)
|
||||
],
|
||||
)
|
||||
@@ -20,7 +20,7 @@ class Settings(BaseSettings):
|
||||
JWT_SECRET_KEY: str
|
||||
|
||||
# Other settings
|
||||
GOOGLE_API_KEY: str = "" # Example with a default
|
||||
GOOGLE_API_KEY: str
|
||||
|
||||
class Config:
|
||||
# Tell pydantic-settings to load variables from a .env file
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# core/database.py
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, Session, declarative_base
|
||||
from typing import Generator
|
||||
@@ -13,7 +12,7 @@ _SessionLocal = None
|
||||
|
||||
def get_engine():
|
||||
global _engine
|
||||
if _engine is None:
|
||||
if (_engine is None):
|
||||
if not settings.DB_URL:
|
||||
raise ValueError("DB_URL is not set in Settings.")
|
||||
print(f"Connecting to database at {settings.DB_URL}")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# main.py
|
||||
from contextlib import _AsyncGeneratorContextManager, asynccontextmanager
|
||||
from typing import Any, Callable
|
||||
from fastapi import FastAPI
|
||||
@@ -7,14 +6,9 @@ from core.database import get_engine, Base
|
||||
from modules import router
|
||||
import logging
|
||||
|
||||
|
||||
# import all models to ensure they are registered before create_all
|
||||
|
||||
|
||||
logging.getLogger("passlib").setLevel(logging.ERROR) # fix bc package logging is broken
|
||||
|
||||
|
||||
# Create DB tables (remove in production; use migrations instead)
|
||||
def lifespan_factory() -> Callable[[FastAPI], _AsyncGeneratorContextManager[Any]]:
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -29,16 +23,11 @@ def lifespan_factory() -> Callable[[FastAPI], _AsyncGeneratorContextManager[Any]
|
||||
lifespan = lifespan_factory()
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
# Include module router
|
||||
app.include_router(router)
|
||||
|
||||
# CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=[
|
||||
"http://localhost:8081", # Keep for web testing if needed
|
||||
"http://192.168.1.9:8081", # Add your mobile device/emulator origin (adjust port if needed)
|
||||
"http://192.168.255.221:8081",
|
||||
"https://maia.depaoli.id.au",
|
||||
],
|
||||
allow_credentials=True,
|
||||
@@ -47,7 +36,6 @@ app.add_middleware(
|
||||
)
|
||||
|
||||
|
||||
# Health endpoint
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
# modules/admin/api.py
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends # Import Body
|
||||
from pydantic import BaseModel # Import BaseModel
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
from core.database import get_db
|
||||
from modules.auth.dependencies import admin_only
|
||||
@@ -10,7 +10,6 @@ from .tasks import cleardb
|
||||
router = APIRouter(prefix="/admin", tags=["admin"], dependencies=[Depends(admin_only)])
|
||||
|
||||
|
||||
# Define a Pydantic model for the request body
|
||||
class ClearDbRequest(BaseModel):
|
||||
hard: bool
|
||||
|
||||
@@ -20,7 +19,6 @@ def read_admin():
|
||||
return {"message": "Admin route"}
|
||||
|
||||
|
||||
# Change to POST and use the request body model
|
||||
@router.post("/cleardb")
|
||||
def clear_db(payload: ClearDbRequest, db: Annotated[Session, Depends(get_db)]):
|
||||
"""
|
||||
@@ -28,6 +26,6 @@ def clear_db(payload: ClearDbRequest, db: Annotated[Session, Depends(get_db)]):
|
||||
'hard'=True: Drop and recreate all tables.
|
||||
'hard'=False: Delete data from tables except users.
|
||||
"""
|
||||
hard = payload.hard # Get 'hard' from the payload
|
||||
hard = payload.hard
|
||||
cleardb.delay(hard)
|
||||
return {"message": "Clearing database in the background", "hard": hard}
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
# modules/admin/services.py
|
||||
|
||||
|
||||
## temp
|
||||
|
||||
@@ -18,16 +18,13 @@ def cleardb(hard: bool):
|
||||
db = SessionLocal()
|
||||
|
||||
if hard:
|
||||
# Drop and recreate all tables
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
Base.metadata.create_all(bind=engine)
|
||||
db.commit()
|
||||
return {"message": "Database reset (HARD)"}
|
||||
else:
|
||||
# Delete data from tables except users
|
||||
tables = Base.metadata.tables.keys()
|
||||
for table_name in tables:
|
||||
# delete all tables that isn't the users table
|
||||
if table_name != "users":
|
||||
table = Base.metadata.tables[table_name]
|
||||
print(f"Deleting table: {table_name}")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,3 @@
|
||||
# modules/auth/api.py
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from jose import JWTError
|
||||
@@ -25,7 +24,7 @@ from sqlalchemy.orm import Session
|
||||
from typing import Annotated
|
||||
from core.database import get_db
|
||||
from datetime import timedelta
|
||||
from core.config import settings # Assuming settings is defined in core.config
|
||||
from core.config import settings
|
||||
from core.exceptions import unauthorized_exception
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# modules/auth/dependencies.py
|
||||
from fastapi import Depends
|
||||
from modules.auth.security import get_current_user
|
||||
from modules.auth.schemas import UserRole
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# modules/auth/schemas.py
|
||||
from enum import Enum as PyEnum
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ password_hasher = PasswordHasher()
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""Hash a password with Argon2 (and optional pepper)."""
|
||||
peppered_password = password + settings.PEPPER # Prepend/append pepper
|
||||
peppered_password = password + settings.PEPPER
|
||||
return password_hasher.hash(peppered_password)
|
||||
|
||||
|
||||
@@ -47,10 +47,8 @@ def authenticate_user(username: str, password: str, db: Session) -> User | None:
|
||||
Authenticate a user by checking username/password against the database.
|
||||
Returns User object if valid, None otherwise.
|
||||
"""
|
||||
# Get user from database
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
|
||||
# If user not found or password doesn't match
|
||||
if not user or not verify_password(password, user.hashed_password):
|
||||
return None
|
||||
|
||||
@@ -65,7 +63,6 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None):
|
||||
expire = datetime.now(timezone.utc) + timedelta(
|
||||
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
)
|
||||
# expire = datetime.now(timezone.utc) + timedelta(seconds=5)
|
||||
to_encode.update({"exp": expire, "token_type": TokenType.ACCESS})
|
||||
return jwt.encode(
|
||||
to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM
|
||||
@@ -89,22 +86,6 @@ def create_refresh_token(data: dict, expires_delta: timedelta | None = None):
|
||||
def verify_token(
|
||||
token: str, expected_token_type: TokenType, db: Session
|
||||
) -> TokenData | None:
|
||||
"""Verify a JWT token and return TokenData if valid.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
token: str
|
||||
The JWT token to be verified.
|
||||
expected_token_type: TokenType
|
||||
The expected type of token (access or refresh)
|
||||
db: Session
|
||||
Database session to fetch user data.
|
||||
|
||||
Returns
|
||||
-------
|
||||
TokenData | None
|
||||
TokenData instance if the token is valid, None otherwise.
|
||||
"""
|
||||
is_blacklisted = (
|
||||
db.query(TokenBlacklist).filter(TokenBlacklist.token == token).first()
|
||||
is not None
|
||||
@@ -137,7 +118,6 @@ def get_current_user(
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Check if the token is blacklisted
|
||||
is_blacklisted = (
|
||||
db.query(TokenBlacklist).filter(TokenBlacklist.token == token).first()
|
||||
is not None
|
||||
@@ -178,7 +158,6 @@ def blacklist_tokens(access_token: str, refresh_token: str, db: Session) -> None
|
||||
)
|
||||
expires_at = datetime.fromtimestamp(payload.get("exp"))
|
||||
|
||||
# Add the token to the blacklist
|
||||
blacklisted_token = TokenBlacklist(token=token, expires_at=expires_at)
|
||||
db.add(blacklisted_token)
|
||||
|
||||
@@ -191,7 +170,6 @@ def blacklist_token(token: str, db: Session) -> None:
|
||||
)
|
||||
expires_at = datetime.fromtimestamp(payload.get("exp"))
|
||||
|
||||
# Add the token to the blacklist
|
||||
blacklisted_token = TokenBlacklist(token=token, expires_at=expires_at)
|
||||
db.add(blacklisted_token)
|
||||
db.commit()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# modules/auth/services.py
|
||||
from sqlalchemy.orm import Session
|
||||
from modules.auth.models import User
|
||||
from modules.auth.schemas import UserResponse
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,3 @@
|
||||
# modules/calendar/api.py
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from sqlalchemy.orm import Session
|
||||
from datetime import datetime
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# modules/calendar/schemas.py
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, field_validator # Add field_validator
|
||||
from typing import List, Optional # Add List and Optional
|
||||
from pydantic import BaseModel, field_validator
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
# Base schema for common fields, including tags
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,3 @@
|
||||
# modules/nlp/api.py
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
@@ -8,7 +7,6 @@ from core.database import get_db
|
||||
from modules.auth.dependencies import get_current_user
|
||||
from modules.auth.models import User
|
||||
|
||||
# Import the new service functions and Enum
|
||||
from modules.nlp.service import (
|
||||
process_request,
|
||||
ask_ai,
|
||||
@@ -17,7 +15,6 @@ from modules.nlp.service import (
|
||||
MessageSender,
|
||||
)
|
||||
|
||||
# Import the response schema and the new ChatMessage model for response type hinting
|
||||
from modules.nlp.schemas import ProcessCommandRequest, ProcessCommandResponse
|
||||
from modules.calendar.service import (
|
||||
create_calendar_event,
|
||||
@@ -28,7 +25,6 @@ from modules.calendar.service import (
|
||||
from modules.calendar.models import CalendarEvent
|
||||
from modules.calendar.schemas import CalendarEventCreate, CalendarEventUpdate
|
||||
|
||||
# Import TODO services, schemas, and model
|
||||
from modules.todo import service as todo_service
|
||||
from modules.todo.models import Todo
|
||||
from modules.todo.schemas import TodoCreate, TodoUpdate
|
||||
@@ -38,24 +34,22 @@ from datetime import datetime
|
||||
|
||||
class ChatMessageResponse(BaseModel):
|
||||
id: int
|
||||
sender: MessageSender # Use the enum directly
|
||||
sender: MessageSender
|
||||
text: str
|
||||
timestamp: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True # Allow Pydantic to work with ORM models
|
||||
from_attributes = True
|
||||
|
||||
|
||||
router = APIRouter(prefix="/nlp", tags=["nlp"])
|
||||
|
||||
|
||||
# Helper to format calendar events (expects list of CalendarEvent models)
|
||||
def format_calendar_events(events: List[CalendarEvent]) -> List[str]:
|
||||
if not events:
|
||||
return ["You have no events matching that criteria."]
|
||||
formatted = ["Here are the events:"]
|
||||
for event in events:
|
||||
# Access attributes directly from the model instance
|
||||
start_str = (
|
||||
event.start.strftime("%Y-%m-%d %H:%M") if event.start else "No start time"
|
||||
)
|
||||
@@ -65,7 +59,6 @@ def format_calendar_events(events: List[CalendarEvent]) -> List[str]:
|
||||
return formatted
|
||||
|
||||
|
||||
# Helper to format TODO items (expects list of Todo models)
|
||||
def format_todos(todos: List[Todo]) -> List[str]:
|
||||
if not todos:
|
||||
return ["Your TODO list is empty."]
|
||||
@@ -80,7 +73,6 @@ def format_todos(todos: List[Todo]) -> List[str]:
|
||||
return formatted
|
||||
|
||||
|
||||
# Update the response model for the endpoint
|
||||
@router.post("/process-command", response_model=ProcessCommandResponse)
|
||||
def process_command(
|
||||
request_data: ProcessCommandRequest,
|
||||
@@ -92,34 +84,25 @@ def process_command(
|
||||
"""
|
||||
user_input = request_data.user_input
|
||||
|
||||
# --- Save User Message ---
|
||||
save_chat_message(
|
||||
db, user_id=current_user.id, sender=MessageSender.USER, text=user_input
|
||||
)
|
||||
# ------------------------
|
||||
|
||||
command_data = process_request(user_input)
|
||||
intent = command_data["intent"]
|
||||
params = command_data["params"]
|
||||
response_text = command_data["response_text"]
|
||||
|
||||
responses = [response_text] # Start with the initial response
|
||||
responses = [response_text]
|
||||
|
||||
# --- Save Initial AI Response ---
|
||||
# Save the first response generated by process_request
|
||||
save_chat_message(
|
||||
db, user_id=current_user.id, sender=MessageSender.AI, text=response_text
|
||||
)
|
||||
# -----------------------------
|
||||
|
||||
if intent == "error":
|
||||
# Don't raise HTTPException here if we want to save the error message
|
||||
# Instead, return the error response directly
|
||||
# save_chat_message(db, user_id=current_user.id, sender=MessageSender.AI, text=response_text) # Already saved above
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
if intent == "clarification_needed" or intent == "unknown":
|
||||
# save_chat_message(db, user_id=current_user.id, sender=MessageSender.AI, text=response_text) # Already saved above
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
try:
|
||||
@@ -127,11 +110,9 @@ def process_command(
|
||||
case "ask_ai":
|
||||
ai_answer = ask_ai(**params)
|
||||
responses.append(ai_answer)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db, user_id=current_user.id, sender=MessageSender.AI, text=ai_answer
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "get_calendar_events":
|
||||
@@ -140,12 +121,10 @@ def process_command(
|
||||
)
|
||||
formatted_responses = format_calendar_events(events)
|
||||
responses.extend(formatted_responses)
|
||||
# --- Save Additional AI Responses ---
|
||||
for resp in formatted_responses:
|
||||
save_chat_message(
|
||||
db, user_id=current_user.id, sender=MessageSender.AI, text=resp
|
||||
)
|
||||
# ----------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "add_calendar_event":
|
||||
@@ -159,20 +138,17 @@ def process_command(
|
||||
title = created_event.title or "Untitled Event"
|
||||
add_response = f"Added: {title} starting at {start_str}."
|
||||
responses.append(add_response)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
sender=MessageSender.AI,
|
||||
text=add_response,
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "update_calendar_event":
|
||||
event_id = params.pop("event_id", None)
|
||||
if event_id is None:
|
||||
# Save the error message before raising
|
||||
error_msg = "Event ID is required for update."
|
||||
save_chat_message(
|
||||
db,
|
||||
@@ -188,20 +164,17 @@ def process_command(
|
||||
title = updated_event.title or "Untitled Event"
|
||||
update_response = f"Updated event ID {updated_event.id}: {title}."
|
||||
responses.append(update_response)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
sender=MessageSender.AI,
|
||||
text=update_response,
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "delete_calendar_event":
|
||||
event_id = params.get("event_id")
|
||||
if event_id is None:
|
||||
# Save the error message before raising
|
||||
error_msg = "Event ID is required for delete."
|
||||
save_chat_message(
|
||||
db,
|
||||
@@ -213,29 +186,24 @@ def process_command(
|
||||
delete_calendar_event(db, current_user.id, event_id)
|
||||
delete_response = f"Deleted event ID {event_id}."
|
||||
responses.append(delete_response)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
sender=MessageSender.AI,
|
||||
text=delete_response,
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
# --- Add TODO Cases ---
|
||||
case "get_todos":
|
||||
todos: List[Todo] = todo_service.get_todos(
|
||||
db, user=current_user, **params
|
||||
)
|
||||
formatted_responses = format_todos(todos)
|
||||
responses.extend(formatted_responses)
|
||||
# --- Save Additional AI Responses ---
|
||||
for resp in formatted_responses:
|
||||
save_chat_message(
|
||||
db, user_id=current_user.id, sender=MessageSender.AI, text=resp
|
||||
)
|
||||
# ----------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "add_todo":
|
||||
@@ -247,14 +215,12 @@ def process_command(
|
||||
f"Added TODO: '{created_todo.task}' (ID: {created_todo.id})."
|
||||
)
|
||||
responses.append(add_response)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
sender=MessageSender.AI,
|
||||
text=add_response,
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "update_todo":
|
||||
@@ -279,14 +245,12 @@ def process_command(
|
||||
status = "complete" if params["complete"] else "incomplete"
|
||||
update_response += f" Marked as {status}."
|
||||
responses.append(update_response)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
sender=MessageSender.AI,
|
||||
text=update_response,
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
case "delete_todo":
|
||||
@@ -307,26 +271,21 @@ def process_command(
|
||||
f"Deleted TODO ID {deleted_todo.id}: '{deleted_todo.task}'."
|
||||
)
|
||||
responses.append(delete_response)
|
||||
# --- Save Additional AI Response ---
|
||||
save_chat_message(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
sender=MessageSender.AI,
|
||||
text=delete_response,
|
||||
)
|
||||
# ---------------------------------
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
# --- End TODO Cases ---
|
||||
|
||||
case _:
|
||||
print(
|
||||
f"Warning: Unhandled intent '{intent}' reached api.py match statement."
|
||||
)
|
||||
# The initial response_text was already saved
|
||||
return ProcessCommandResponse(responses=responses)
|
||||
|
||||
except HTTPException as http_exc:
|
||||
# Don't save again if already saved before raising
|
||||
if http_exc.status_code != 400 or ("event_id" not in http_exc.detail.lower()):
|
||||
save_chat_message(
|
||||
db,
|
||||
@@ -340,11 +299,9 @@ def process_command(
|
||||
error_response = (
|
||||
"Sorry, I encountered an error while trying to perform that action."
|
||||
)
|
||||
# --- Save Final Error AI Response ---
|
||||
save_chat_message(
|
||||
db, user_id=current_user.id, sender=MessageSender.AI, text=error_response
|
||||
)
|
||||
# ----------------------------------
|
||||
return ProcessCommandResponse(responses=[error_response])
|
||||
|
||||
|
||||
@@ -355,6 +312,3 @@ def read_chat_history(
|
||||
"""Retrieves the last 50 chat messages for the current user."""
|
||||
history = get_chat_history(db, user_id=current_user.id, limit=50)
|
||||
return history
|
||||
|
||||
|
||||
# -------------------------------------
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# /home/cdp/code/MAIA/backend/modules/nlp/models.py
|
||||
from sqlalchemy import Column, Integer, Text, DateTime, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# modules/nlp/schemas.py
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
|
||||
@@ -9,5 +8,4 @@ class ProcessCommandRequest(BaseModel):
|
||||
|
||||
class ProcessCommandResponse(BaseModel):
|
||||
responses: List[str]
|
||||
# Optional: Keep details if needed for specific frontend logic beyond display
|
||||
# details: dict | None = None
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# modules/nlp/service.py
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import desc # Import desc for ordering
|
||||
from google import genai
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
# backend/modules/todo/__init__.py
|
||||
# This file makes the 'todo' directory a Python package.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,17 +1,16 @@
|
||||
# backend/modules/todo/api.py
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
|
||||
from . import service, schemas
|
||||
from core.database import get_db
|
||||
from modules.auth.dependencies import get_current_user # Corrected import
|
||||
from modules.auth.models import User # Assuming User model is in auth.models
|
||||
from modules.auth.dependencies import get_current_user
|
||||
from modules.auth.models import User
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/todos",
|
||||
tags=["todos"],
|
||||
dependencies=[Depends(get_current_user)], # Corrected dependency
|
||||
dependencies=[Depends(get_current_user)],
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
@@ -20,7 +19,7 @@ router = APIRouter(
|
||||
def create_todo_endpoint(
|
||||
todo: schemas.TodoCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user), # Corrected dependency
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
return service.create_todo(db=db, todo=todo, user=current_user)
|
||||
|
||||
@@ -30,7 +29,7 @@ def read_todos_endpoint(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user), # Corrected dependency
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
todos = service.get_todos(db=db, user=current_user, skip=skip, limit=limit)
|
||||
return todos
|
||||
@@ -40,7 +39,7 @@ def read_todos_endpoint(
|
||||
def read_todo_endpoint(
|
||||
todo_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user), # Corrected dependency
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
db_todo = service.get_todo(db=db, todo_id=todo_id, user=current_user)
|
||||
if db_todo is None:
|
||||
@@ -53,7 +52,7 @@ def update_todo_endpoint(
|
||||
todo_id: int,
|
||||
todo_update: schemas.TodoUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user), # Corrected dependency
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
return service.update_todo(
|
||||
db=db, todo_id=todo_id, todo_update=todo_update, user=current_user
|
||||
@@ -64,6 +63,6 @@ def update_todo_endpoint(
|
||||
def delete_todo_endpoint(
|
||||
todo_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user), # Corrected dependency
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
return service.delete_todo(db=db, todo_id=todo_id, user=current_user)
|
||||
|
||||
@@ -16,4 +16,4 @@ class Todo(Base):
|
||||
|
||||
owner = relationship(
|
||||
"User"
|
||||
) # Add relationship if needed, assuming User model exists in auth.models
|
||||
)
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,3 @@
|
||||
# modules/user/api.py
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"softwareKeyboardLayoutMode": "resize"
|
||||
"softwareKeyboardLayoutMode": "resize",
|
||||
"package": "com.seedeep.maia"
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
@@ -28,6 +29,12 @@
|
||||
"plugins": [
|
||||
"expo-secure-store",
|
||||
"expo-font"
|
||||
]
|
||||
],
|
||||
"extra": {
|
||||
"eas": {
|
||||
"projectId": "4d7d70ce-a4d8-4307-8827-8ef713b95b78"
|
||||
}
|
||||
},
|
||||
"owner": "cdp202"
|
||||
}
|
||||
}
|
||||
|
||||
21
interfaces/nativeapp/eas.json
Normal file
21
interfaces/nativeapp/eas.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"cli": {
|
||||
"version": ">= 16.3.2",
|
||||
"appVersionSource": "remote"
|
||||
},
|
||||
"build": {
|
||||
"development": {
|
||||
"developmentClient": true,
|
||||
"distribution": "internal"
|
||||
},
|
||||
"preview": {
|
||||
"distribution": "internal"
|
||||
},
|
||||
"production": {
|
||||
"autoIncrement": true
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"production": {}
|
||||
}
|
||||
}
|
||||
102
interfaces/nativeapp/package-lock.json
generated
102
interfaces/nativeapp/package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"axios": "^1.8.4",
|
||||
"date-fns": "^4.1.0",
|
||||
"expo": "^52.0.46",
|
||||
"expo-dev-client": "~5.0.20",
|
||||
"expo-font": "~13.0.4",
|
||||
"expo-secure-store": "~14.0.1",
|
||||
"expo-splash-screen": "~0.29.24",
|
||||
@@ -3746,6 +3747,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/anser": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz",
|
||||
@@ -5307,6 +5323,54 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-client": {
|
||||
"version": "5.0.20",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.0.20.tgz",
|
||||
"integrity": "sha512-bLNkHdU7V3I4UefgJbJnIDUBUL0LxIal/xYEx9BbgDd3B7wgQKY//+BpPIxBOKCQ22lkyiHY8y9tLhO903sAgg==",
|
||||
"dependencies": {
|
||||
"expo-dev-launcher": "5.0.35",
|
||||
"expo-dev-menu": "6.0.25",
|
||||
"expo-dev-menu-interface": "1.9.3",
|
||||
"expo-manifests": "~0.15.8",
|
||||
"expo-updates-interface": "~1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-launcher": {
|
||||
"version": "5.0.35",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-5.0.35.tgz",
|
||||
"integrity": "sha512-hEQr0ZREnUMxZ6wtQgfK1lzYnbb0zar3HqYZhmANzXmE6UEPbQ4GByLzhpfz/d+xxdBVQZsrHdtiV28KPG2sog==",
|
||||
"dependencies": {
|
||||
"ajv": "8.11.0",
|
||||
"expo-dev-menu": "6.0.25",
|
||||
"expo-manifests": "~0.15.8",
|
||||
"resolve-from": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-menu": {
|
||||
"version": "6.0.25",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-6.0.25.tgz",
|
||||
"integrity": "sha512-K2m4z/I+CPWbMtHlDzU68lHaQs52De0v5gbsjAmA5ig8FrYh4MKZvPxSVANaiKENzgmtglu8qaFh7ua9Gt2TfA==",
|
||||
"dependencies": {
|
||||
"expo-dev-menu-interface": "1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-menu-interface": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.9.3.tgz",
|
||||
"integrity": "sha512-KY/dWTBE1l47i9V366JN5rC6YIdOc9hz8yAmZzkl5DrPia5l3M2WIjtnpHC9zUkNjiSiG2urYoOAq4H/uLdmyg==",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-file-system": {
|
||||
"version": "18.0.12",
|
||||
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz",
|
||||
@@ -5331,6 +5395,11 @@
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-json-utils": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.14.0.tgz",
|
||||
"integrity": "sha512-xjGfK9dL0B1wLnOqNkX0jM9p48Y0I5xEPzHude28LY67UmamUyAACkqhZGaPClyPNfdzczk7Ej6WaRMT3HfXvw=="
|
||||
},
|
||||
"node_modules/expo-keep-awake": {
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.0.3.tgz",
|
||||
@@ -5340,6 +5409,18 @@
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-manifests": {
|
||||
"version": "0.15.8",
|
||||
"resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.8.tgz",
|
||||
"integrity": "sha512-VuIyaMfRfLZeETNsRohqhy1l7iZ7I+HKMPfZXVL2Yn17TT0WkOhZoq1DzYwPbOHPgp1Uk6phNa86EyaHrD2DLw==",
|
||||
"dependencies": {
|
||||
"@expo/config": "~10.0.11",
|
||||
"expo-json-utils": "~0.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-modules-autolinking": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.8.tgz",
|
||||
@@ -5427,6 +5508,14 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-updates-interface": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-1.0.0.tgz",
|
||||
"integrity": "sha512-93oWtvULJOj+Pp+N/lpTcFfuREX1wNeHtp7Lwn8EbzYYmdn37MvZU3TPW2tYYCZuhzmKEXnUblYcruYoDu7IrQ==",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/exponential-backoff": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
|
||||
@@ -6596,6 +6685,11 @@
|
||||
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
@@ -10308,6 +10402,14 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-latest-callback": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz",
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
"react-native-safe-area-context": "4.12.0",
|
||||
"react-native-screens": "~4.4.0",
|
||||
"react-native-vector-icons": "^10.2.0",
|
||||
"react-native-web": "~0.19.13"
|
||||
"react-native-web": "~0.19.13",
|
||||
"expo-dev-client": "~5.0.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
// src/api/client.ts
|
||||
import axios, { AxiosError } from 'axios'; // Import AxiosError
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import { Platform } from 'react-native';
|
||||
import * as SecureStore from 'expo-secure-store';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
|
||||
// const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'http://192.168.255.221:8000/api'; // Use your machine's IP
|
||||
// const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'http://192.168.1.9:8000/api'; // Use your machine's IP
|
||||
const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'https://maia.depaoli.id.au/api'; // Use your machine's IP
|
||||
const ACCESS_TOKEN_KEY = 'maia_access_token'; // Renamed for clarity
|
||||
const REFRESH_TOKEN_KEY = 'maia_refresh_token'; // Key for refresh token
|
||||
const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'https://maia.depaoli.id.au/api';
|
||||
const ACCESS_TOKEN_KEY = 'maia_access_token';
|
||||
const REFRESH_TOKEN_KEY = 'maia_refresh_token';
|
||||
|
||||
console.log("Using API Base URL:", API_BASE_URL);
|
||||
|
||||
@@ -34,7 +32,7 @@ const deleteToken = async (key: string): Promise<void> => {
|
||||
if (Platform.OS === 'web') {
|
||||
await AsyncStorage.removeItem(key);
|
||||
} else {
|
||||
await SecureStore.deleteItemAsync(key).catch(() => {}); // Ignore delete error
|
||||
await SecureStore.deleteItemAsync(key).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const AuthNavigator = () => {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Login" component={LoginScreen} />
|
||||
<Stack.Screen name="Register" component={RegisterScreen} /> {/* Add Register screen */}
|
||||
<Stack.Screen name="Register" component={RegisterScreen} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -35,11 +35,7 @@ const RegisterScreen: React.FC<RegisterScreenProps> = ({ navigation }) => {
|
||||
await register(username, password, name);
|
||||
console.log("[RegisterScreen] handleRegister: Registration successful (from context perspective).");
|
||||
// Show success message and navigate back to Login
|
||||
Alert.alert(
|
||||
'Registration Successful',
|
||||
'Your account has been created. Please log in.',
|
||||
[{ text: 'OK', onPress: () => navigation.navigate('Login') }]
|
||||
);
|
||||
navigation.navigate('Login');
|
||||
} catch (err: any) {
|
||||
console.log("[RegisterScreen] handleRegister: Caught error from context register.");
|
||||
const errorMessage = err.response?.data?.detail ||
|
||||
|
||||
Reference in New Issue
Block a user