Skip to content

Commit 0ad9370

Browse files
authored
Merge pull request #64 from stenwire/whatsapp-intg-v1
Whatsapp intg v1
2 parents f9ad0cc + dd4250e commit 0ad9370

20 files changed

Lines changed: 548 additions & 79 deletions

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ POSTGRES_CONTAINER ?= $(PROJECT_PREFIX)_postgres
1212
BACKEND_CONTAINER ?= $(PROJECT_PREFIX)_backend
1313
FRONTEND_CONTAINER ?= $(PROJECT_PREFIX)_frontend
1414

15-
.PHONY: start start-d stop build logs migrate migrate-generate db-shell db-backup db-restore clean ps db-reset db-truncate logs-backend logs-frontend lint-be lint-be-fix lint-fe lint-fe-fix test-be test-be-unit test-be-api test-be-integration test-fe install-hooks setup
15+
.PHONY: start start-d stop build logs migrate migrate-generate db-shell db-backup db-restore clean ps db-reset db-truncate logs-backend logs-frontend lint-be lint-be-fix lint-fe lint-fe-fix test-be test-be-unit test-be-api test-be-integration test-fe install-hooks setup create-admin
1616

1717
# Start all services (foreground)
1818
start:
@@ -150,6 +150,18 @@ setup: install-hooks
150150
cd frontend && npm ci
151151
@echo "Setup complete."
152152

153+
# Create or promote admin user
154+
# Promote existing user: make create-admin EMAIL=user@example.com
155+
# Create new admin: make create-admin EMAIL=admin@example.com PASSWORD=securepass NAME="Admin"
156+
create-admin:
157+
@if [ -z "$(EMAIL)" ]; then \
158+
echo "Error: EMAIL is required."; \
159+
echo " Promote existing user: make create-admin EMAIL=user@example.com"; \
160+
echo " Create new admin: make create-admin EMAIL=admin@example.com PASSWORD=securepass"; \
161+
exit 1; \
162+
fi
163+
docker-compose exec backend uv run python -m scripts.create_admin --email $(EMAIL) $(if $(PASSWORD),--password $(PASSWORD)) $(if $(NAME),--name "$(NAME)")
164+
153165
# Show running containers
154166
ps:
155167
docker-compose ps

backend/alembic/env.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@
99
from alembic import context
1010

1111
from app.db.base import Base
12-
# Import all your models here so they are registered with Base.metadata
12+
# Import all models so they are registered with Base.metadata
13+
from app.models.user import User # noqa: F401
14+
from app.models.business import Business # noqa: F401
15+
from app.models.plan import Plan # noqa: F401
16+
from app.models.payment import PaymentTransaction # noqa: F401
17+
from app.models.chat_session import ChatSession # noqa: F401
18+
from app.models.widget import WidgetSettings, GuestUser, GuestMessage # noqa: F401
19+
from app.models.escalation import Escalation # noqa: F401
20+
from app.models.document import Document # noqa: F401
21+
from app.models.analytics import AnalyticsDailySummary # noqa: F401
1322

1423

1524
# Alembic Config object
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""add_is_admin_to_users
2+
3+
Revision ID: 266444da8a8b
4+
Revises: b1c2d3e4f5a6
5+
Create Date: 2026-04-06 20:40:41.699752
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = '266444da8a8b'
16+
down_revision: Union[str, Sequence[str], None] = 'b1c2d3e4f5a6'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
op.add_column(
23+
"users",
24+
sa.Column("is_admin", sa.Boolean(), nullable=False, server_default=sa.text("false")),
25+
)
26+
27+
28+
def downgrade() -> None:
29+
op.drop_column("users", "is_admin")

backend/app/admin/__init__.py

Whitespace-only changes.

backend/app/admin/auth.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from sqladmin.authentication import AuthenticationBackend
2+
from starlette.requests import Request
3+
4+
from app.db.session import SessionLocal
5+
from app.models.user import User
6+
from app.core.security import verify_password
7+
8+
9+
class AdminAuth(AuthenticationBackend):
10+
async def login(self, request: Request) -> bool:
11+
form = await request.form()
12+
email = form.get("username")
13+
password = form.get("password")
14+
15+
if not email or not password:
16+
return False
17+
18+
db = SessionLocal()
19+
try:
20+
user = db.query(User).filter(User.email == email).first()
21+
if not user or not user.hashed_password:
22+
return False
23+
if not verify_password(password, user.hashed_password):
24+
return False
25+
if not user.is_admin:
26+
return False
27+
28+
request.session.update({"admin_user_id": user.id})
29+
return True
30+
finally:
31+
db.close()
32+
33+
async def logout(self, request: Request) -> bool:
34+
request.session.clear()
35+
return True
36+
37+
async def authenticate(self, request: Request) -> bool:
38+
admin_user_id = request.session.get("admin_user_id")
39+
if not admin_user_id:
40+
return False
41+
42+
db = SessionLocal()
43+
try:
44+
user = db.query(User).filter(User.id == admin_user_id).first()
45+
if not user or not user.is_admin:
46+
request.session.clear()
47+
return False
48+
return True
49+
finally:
50+
db.close()

0 commit comments

Comments
 (0)