chore(01-01): add FERNET_KEY config and email tables migration
- Add FERNET_KEY to .env.example with generation instructions - Register email.models in aerich_config.py and app.py - Register email blueprint in app.py - Create database migration for email_accounts, email_sync_status, emails - Migration includes proper foreign keys and indexes
This commit is contained in:
11
.env.example
11
.env.example
@@ -54,3 +54,14 @@ OIDC_USE_DISCOVERY=true
|
|||||||
YNAB_ACCESS_TOKEN=your-ynab-personal-access-token
|
YNAB_ACCESS_TOKEN=your-ynab-personal-access-token
|
||||||
# Optional: Specify a budget ID, or leave empty to use the default/first budget
|
# Optional: Specify a budget ID, or leave empty to use the default/first budget
|
||||||
YNAB_BUDGET_ID=
|
YNAB_BUDGET_ID=
|
||||||
|
|
||||||
|
# Mealie Configuration
|
||||||
|
# Base URL for your Mealie instance (e.g., http://192.168.1.5:9000 or https://mealie.example.com)
|
||||||
|
MEALIE_BASE_URL=http://192.168.1.5:9000
|
||||||
|
# Get your API token from Mealie's user settings page
|
||||||
|
MEALIE_API_TOKEN=your-mealie-api-token
|
||||||
|
|
||||||
|
# Email Integration
|
||||||
|
# Email Encryption Key (32-byte URL-safe base64)
|
||||||
|
# Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
||||||
|
FERNET_KEY=your-fernet-key-here
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ TORTOISE_ORM = {
|
|||||||
"models": [
|
"models": [
|
||||||
"blueprints.conversation.models",
|
"blueprints.conversation.models",
|
||||||
"blueprints.users.models",
|
"blueprints.users.models",
|
||||||
|
"blueprints.email.models",
|
||||||
"aerich.models",
|
"aerich.models",
|
||||||
],
|
],
|
||||||
"default_connection": "default",
|
"default_connection": "default",
|
||||||
|
|||||||
16
app.py
16
app.py
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from quart import Quart, jsonify, render_template, request, send_from_directory
|
from quart import Quart, jsonify, render_template, request, send_from_directory
|
||||||
@@ -7,6 +8,7 @@ from tortoise.contrib.quart import register_tortoise
|
|||||||
|
|
||||||
import blueprints.conversation
|
import blueprints.conversation
|
||||||
import blueprints.conversation.logic
|
import blueprints.conversation.logic
|
||||||
|
import blueprints.email
|
||||||
import blueprints.rag
|
import blueprints.rag
|
||||||
import blueprints.users
|
import blueprints.users
|
||||||
import blueprints.users.models
|
import blueprints.users.models
|
||||||
@@ -15,6 +17,18 @@ from main import consult_simba_oracle
|
|||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
handlers=[logging.StreamHandler()],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure YNAB and Mealie loggers are visible
|
||||||
|
logging.getLogger("utils.ynab_service").setLevel(logging.INFO)
|
||||||
|
logging.getLogger("utils.mealie_service").setLevel(logging.INFO)
|
||||||
|
logging.getLogger("blueprints.conversation.agents").setLevel(logging.INFO)
|
||||||
|
|
||||||
app = Quart(
|
app = Quart(
|
||||||
__name__,
|
__name__,
|
||||||
static_folder="raggr-frontend/dist/static",
|
static_folder="raggr-frontend/dist/static",
|
||||||
@@ -27,6 +41,7 @@ jwt = JWTManager(app)
|
|||||||
# Register blueprints
|
# Register blueprints
|
||||||
app.register_blueprint(blueprints.users.user_blueprint)
|
app.register_blueprint(blueprints.users.user_blueprint)
|
||||||
app.register_blueprint(blueprints.conversation.conversation_blueprint)
|
app.register_blueprint(blueprints.conversation.conversation_blueprint)
|
||||||
|
app.register_blueprint(blueprints.email.email_blueprint)
|
||||||
app.register_blueprint(blueprints.rag.rag_blueprint)
|
app.register_blueprint(blueprints.rag.rag_blueprint)
|
||||||
|
|
||||||
|
|
||||||
@@ -42,6 +57,7 @@ TORTOISE_CONFIG = {
|
|||||||
"models": [
|
"models": [
|
||||||
"blueprints.conversation.models",
|
"blueprints.conversation.models",
|
||||||
"blueprints.users.models",
|
"blueprints.users.models",
|
||||||
|
"blueprints.email.models",
|
||||||
"aerich.models",
|
"aerich.models",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
56
migrations/models/2_20260208091453_add_email_tables.py
Normal file
56
migrations/models/2_20260208091453_add_email_tables.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from tortoise import BaseDBAsyncClient
|
||||||
|
|
||||||
|
RUN_IN_TRANSACTION = True
|
||||||
|
|
||||||
|
|
||||||
|
async def upgrade(db: BaseDBAsyncClient) -> str:
|
||||||
|
return """
|
||||||
|
CREATE TABLE IF NOT EXISTS "email_accounts" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"email_address" VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
"display_name" VARCHAR(255),
|
||||||
|
"imap_host" VARCHAR(255) NOT NULL,
|
||||||
|
"imap_port" INT NOT NULL DEFAULT 993,
|
||||||
|
"imap_username" VARCHAR(255) NOT NULL,
|
||||||
|
"imap_password" TEXT NOT NULL,
|
||||||
|
"is_active" BOOL NOT NULL DEFAULT TRUE,
|
||||||
|
"last_error" TEXT,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"user_id" UUID NOT NULL REFERENCES "users" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "email_sync_status" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"last_sync_date" TIMESTAMPTZ,
|
||||||
|
"last_message_uid" INT NOT NULL DEFAULT 0,
|
||||||
|
"message_count" INT NOT NULL DEFAULT 0,
|
||||||
|
"consecutive_failures" INT NOT NULL DEFAULT 0,
|
||||||
|
"last_failure_date" TIMESTAMPTZ,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"account_id" UUID NOT NULL UNIQUE REFERENCES "email_accounts" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "emails" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"message_id" VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
"subject" VARCHAR(500) NOT NULL,
|
||||||
|
"from_address" VARCHAR(255) NOT NULL,
|
||||||
|
"to_address" TEXT NOT NULL,
|
||||||
|
"date" TIMESTAMPTZ NOT NULL,
|
||||||
|
"body_text" TEXT,
|
||||||
|
"body_html" TEXT,
|
||||||
|
"chromadb_doc_id" VARCHAR(255),
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"expires_at" TIMESTAMPTZ NOT NULL,
|
||||||
|
"account_id" UUID NOT NULL REFERENCES "email_accounts" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_emails_message_9e3c0c" ON "emails" ("message_id");"""
|
||||||
|
|
||||||
|
|
||||||
|
async def downgrade(db: BaseDBAsyncClient) -> str:
|
||||||
|
return """
|
||||||
|
DROP TABLE IF EXISTS "emails";
|
||||||
|
DROP TABLE IF EXISTS "email_sync_status";
|
||||||
|
DROP TABLE IF EXISTS "email_accounts";"""
|
||||||
|
|
||||||
|
|
||||||
|
MODELS_STATE = ""
|
||||||
Reference in New Issue
Block a user