create-flask-react 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. create_flask_react-0.1.0/LICENSE +21 -0
  2. create_flask_react-0.1.0/PKG-INFO +84 -0
  3. create_flask_react-0.1.0/README.md +65 -0
  4. create_flask_react-0.1.0/pyproject.toml +39 -0
  5. create_flask_react-0.1.0/setup.cfg +4 -0
  6. create_flask_react-0.1.0/src/create_flask_react/__init__.py +0 -0
  7. create_flask_react-0.1.0/src/create_flask_react/main.py +50 -0
  8. create_flask_react-0.1.0/src/create_flask_react/template/.env.example +10 -0
  9. create_flask_react-0.1.0/src/create_flask_react/template/.gitignore +23 -0
  10. create_flask_react-0.1.0/src/create_flask_react/template/Makefile +22 -0
  11. create_flask_react-0.1.0/src/create_flask_react/template/backend/.dockerignore +3 -0
  12. create_flask_react-0.1.0/src/create_flask_react/template/backend/Dockerfile +6 -0
  13. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/__init__.py +23 -0
  14. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/__pycache__/__init__.cpython-312.pyc +0 -0
  15. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/__pycache__/config.cpython-312.pyc +0 -0
  16. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/__pycache__/extensions.cpython-312.pyc +0 -0
  17. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/config.py +29 -0
  18. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/extensions.py +19 -0
  19. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/models/__init__.py +3 -0
  20. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/models/__pycache__/__init__.cpython-312.pyc +0 -0
  21. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/models/__pycache__/user.cpython-312.pyc +0 -0
  22. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/models/user.py +18 -0
  23. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/routes/__init__.py +16 -0
  24. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/routes/__pycache__/__init__.cpython-312.pyc +0 -0
  25. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/routes/__pycache__/auth.cpython-312.pyc +0 -0
  26. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/routes/__pycache__/main.cpython-312.pyc +0 -0
  27. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/routes/auth.py +107 -0
  28. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/routes/main.py +19 -0
  29. create_flask_react-0.1.0/src/create_flask_react/template/backend/app/static/.gitkeep +0 -0
  30. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/README +1 -0
  31. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/__pycache__/env.cpython-312.pyc +0 -0
  32. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/alembic.ini +40 -0
  33. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/env.py +69 -0
  34. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/script.py.mako +24 -0
  35. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/versions/001_create_user_table.py +32 -0
  36. create_flask_react-0.1.0/src/create_flask_react/template/backend/migrations/versions/__pycache__/001_create_user_table.cpython-312.pyc +0 -0
  37. create_flask_react-0.1.0/src/create_flask_react/template/backend/pyproject.toml +13 -0
  38. create_flask_react-0.1.0/src/create_flask_react/template/backend/tests/.gitkeep +0 -0
  39. create_flask_react-0.1.0/src/create_flask_react/template/backend/uv.lock +561 -0
  40. create_flask_react-0.1.0/src/create_flask_react/template/docker-compose.yml +45 -0
  41. create_flask_react-0.1.0/src/create_flask_react/template/frontend/Dockerfile +5 -0
  42. create_flask_react-0.1.0/src/create_flask_react/template/frontend/components.json +16 -0
  43. create_flask_react-0.1.0/src/create_flask_react/template/frontend/index.html +12 -0
  44. create_flask_react-0.1.0/src/create_flask_react/template/frontend/package.json +33 -0
  45. create_flask_react-0.1.0/src/create_flask_react/template/frontend/postcss.config.js +6 -0
  46. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/App.tsx +23 -0
  47. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/api/client.ts +43 -0
  48. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/components/ProtectedRoute.tsx +12 -0
  49. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/components/ui/alert.tsx +58 -0
  50. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/components/ui/button.tsx +55 -0
  51. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/components/ui/card.tsx +85 -0
  52. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/components/ui/input.tsx +24 -0
  53. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/components/ui/label.tsx +23 -0
  54. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/context/AuthContext.tsx +64 -0
  55. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/index.css +37 -0
  56. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/lib/utils.ts +6 -0
  57. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/main.tsx +16 -0
  58. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/pages/Dashboard.tsx +37 -0
  59. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/pages/Login.tsx +88 -0
  60. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/pages/Register.tsx +88 -0
  61. create_flask_react-0.1.0/src/create_flask_react/template/frontend/src/vite-env.d.ts +1 -0
  62. create_flask_react-0.1.0/src/create_flask_react/template/frontend/tailwind.config.ts +60 -0
  63. create_flask_react-0.1.0/src/create_flask_react/template/frontend/tsconfig.json +25 -0
  64. create_flask_react-0.1.0/src/create_flask_react/template/frontend/tsconfig.node.json +10 -0
  65. create_flask_react-0.1.0/src/create_flask_react/template/frontend/vite.config.ts +24 -0
  66. create_flask_react-0.1.0/src/create_flask_react.egg-info/PKG-INFO +84 -0
  67. create_flask_react-0.1.0/src/create_flask_react.egg-info/SOURCES.txt +68 -0
  68. create_flask_react-0.1.0/src/create_flask_react.egg-info/dependency_links.txt +1 -0
  69. create_flask_react-0.1.0/src/create_flask_react.egg-info/entry_points.txt +2 -0
  70. create_flask_react-0.1.0/src/create_flask_react.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joseph McGill
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: create-flask-react
3
+ Version: 0.1.0
4
+ Summary: Scaffold a full-stack Flask + React starter project with auth, PostgreSQL, and Docker
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/jsphfrntz/create-flask-react
7
+ Project-URL: Repository, https://github.com/jsphfrntz/create-flask-react
8
+ Project-URL: Issues, https://github.com/jsphfrntz/create-flask-react/issues
9
+ Keywords: flask,react,starter,scaffold,docker,fullstack
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Software Development :: Code Generators
14
+ Classifier: Framework :: Flask
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Dynamic: license-file
19
+
20
+ # create-flask-react
21
+
22
+ Scaffold a full-stack Flask + React starter project with auth, PostgreSQL, and Docker.
23
+
24
+ ## Quick Start
25
+
26
+ ```bash
27
+ uvx create-flask-react my-app
28
+ cd my-app
29
+ make dev
30
+ ```
31
+
32
+ Then open http://localhost:5173 — you'll see a login page, register an account, and land on an authenticated dashboard.
33
+
34
+ ## What You Get
35
+
36
+ - **Backend:** Flask with Flask-RESTX (auto-generated Swagger docs at `/api/docs`)
37
+ - **Auth:** Flask-Login with session-based authentication
38
+ - **Database:** PostgreSQL with Flask-SQLAlchemy + Flask-Migrate
39
+ - **Frontend:** React 18 + TypeScript + Vite
40
+ - **UI:** Tailwind CSS + shadcn/ui components
41
+ - **Docker:** Single `docker compose up` for the full stack
42
+
43
+ ## Project Structure
44
+
45
+ ```
46
+ my-app/
47
+ ├── docker-compose.yml # Dev: Flask + Vite + Postgres
48
+ ├── Makefile # Convenience commands
49
+ ├── backend/
50
+ │ ├── app/
51
+ │ │ ├── __init__.py # App factory
52
+ │ │ ├── config.py # Config from env vars
53
+ │ │ ├── extensions.py # SQLAlchemy, Migrate, LoginManager
54
+ │ │ ├── models/user.py # User model
55
+ │ │ └── routes/auth.py # Auth API (Flask-RESTX)
56
+ │ ├── migrations/ # Alembic migrations
57
+ │ └── pyproject.toml # Python deps (managed by uv)
58
+ └── frontend/
59
+ └── src/
60
+ ├── api/client.ts # Typed API client
61
+ ├── context/AuthContext # Auth state management
62
+ ├── components/ui/ # shadcn/ui components
63
+ └── pages/ # Login, Register, Dashboard
64
+ ```
65
+
66
+ ## Make Targets
67
+
68
+ | Command | Description |
69
+ |---------|-------------|
70
+ | `make dev` | Start dev environment |
71
+ | `make stop` | Stop containers |
72
+ | `make logs` | Tail container logs |
73
+ | `make db-migrate` | Generate a new migration |
74
+ | `make db-upgrade` | Apply migrations |
75
+ | `make shell` | Flask shell |
76
+ | `make clean` | Stop containers and remove volumes |
77
+
78
+ ## API Docs
79
+
80
+ Swagger UI is available at http://localhost:5000/api/docs when the backend is running.
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,65 @@
1
+ # create-flask-react
2
+
3
+ Scaffold a full-stack Flask + React starter project with auth, PostgreSQL, and Docker.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ uvx create-flask-react my-app
9
+ cd my-app
10
+ make dev
11
+ ```
12
+
13
+ Then open http://localhost:5173 — you'll see a login page, register an account, and land on an authenticated dashboard.
14
+
15
+ ## What You Get
16
+
17
+ - **Backend:** Flask with Flask-RESTX (auto-generated Swagger docs at `/api/docs`)
18
+ - **Auth:** Flask-Login with session-based authentication
19
+ - **Database:** PostgreSQL with Flask-SQLAlchemy + Flask-Migrate
20
+ - **Frontend:** React 18 + TypeScript + Vite
21
+ - **UI:** Tailwind CSS + shadcn/ui components
22
+ - **Docker:** Single `docker compose up` for the full stack
23
+
24
+ ## Project Structure
25
+
26
+ ```
27
+ my-app/
28
+ ├── docker-compose.yml # Dev: Flask + Vite + Postgres
29
+ ├── Makefile # Convenience commands
30
+ ├── backend/
31
+ │ ├── app/
32
+ │ │ ├── __init__.py # App factory
33
+ │ │ ├── config.py # Config from env vars
34
+ │ │ ├── extensions.py # SQLAlchemy, Migrate, LoginManager
35
+ │ │ ├── models/user.py # User model
36
+ │ │ └── routes/auth.py # Auth API (Flask-RESTX)
37
+ │ ├── migrations/ # Alembic migrations
38
+ │ └── pyproject.toml # Python deps (managed by uv)
39
+ └── frontend/
40
+ └── src/
41
+ ├── api/client.ts # Typed API client
42
+ ├── context/AuthContext # Auth state management
43
+ ├── components/ui/ # shadcn/ui components
44
+ └── pages/ # Login, Register, Dashboard
45
+ ```
46
+
47
+ ## Make Targets
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `make dev` | Start dev environment |
52
+ | `make stop` | Stop containers |
53
+ | `make logs` | Tail container logs |
54
+ | `make db-migrate` | Generate a new migration |
55
+ | `make db-upgrade` | Apply migrations |
56
+ | `make shell` | Flask shell |
57
+ | `make clean` | Stop containers and remove volumes |
58
+
59
+ ## API Docs
60
+
61
+ Swagger UI is available at http://localhost:5000/api/docs when the backend is running.
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "create-flask-react"
7
+ version = "0.1.0"
8
+ description = "Scaffold a full-stack Flask + React starter project with auth, PostgreSQL, and Docker"
9
+ requires-python = ">=3.10"
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ keywords = ["flask", "react", "starter", "scaffold", "docker", "fullstack"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3",
17
+ "Topic :: Software Development :: Code Generators",
18
+ "Framework :: Flask",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/jsphfrntz/create-flask-react"
23
+ Repository = "https://github.com/jsphfrntz/create-flask-react"
24
+ Issues = "https://github.com/jsphfrntz/create-flask-react/issues"
25
+
26
+ [project.scripts]
27
+ create-flask-react = "create_flask_react.main:main"
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
31
+
32
+ [tool.setuptools.package-data]
33
+ create_flask_react = [
34
+ "template/**/*",
35
+ "template/**/.gitkeep",
36
+ "template/**/.gitignore",
37
+ "template/**/.dockerignore",
38
+ "template/.env.example",
39
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,50 @@
1
+ """CLI entry point for create-flask-react."""
2
+
3
+ import argparse
4
+ import shutil
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ TEMPLATE_DIR = Path(__file__).parent / "template"
9
+ IGNORE = shutil.ignore_patterns(
10
+ ".venv", "node_modules", "__pycache__", "*.pyc", ".env",
11
+ )
12
+
13
+
14
+ def main():
15
+ parser = argparse.ArgumentParser(
16
+ description="Create a new Flask + React project"
17
+ )
18
+ parser.add_argument(
19
+ "project_name",
20
+ help="Name of the project directory to create (use '.' for current directory)",
21
+ )
22
+ args = parser.parse_args()
23
+
24
+ if args.project_name == ".":
25
+ dest = Path.cwd()
26
+ else:
27
+ dest = Path.cwd() / args.project_name
28
+
29
+ if dest.exists() and args.project_name != ".":
30
+ if any(dest.iterdir()):
31
+ print(f"Error: {dest} already exists and is not empty.", file=sys.stderr)
32
+ sys.exit(1)
33
+
34
+ dest.mkdir(parents=True, exist_ok=True)
35
+
36
+ shutil.copytree(TEMPLATE_DIR, dest, dirs_exist_ok=True, ignore=IGNORE)
37
+
38
+ # Create .env from .env.example
39
+ env_example = dest / ".env.example"
40
+ env_file = dest / ".env"
41
+ if env_example.exists() and not env_file.exists():
42
+ shutil.copy2(env_example, env_file)
43
+
44
+ print(f"Created Flask + React project in {dest.resolve()}")
45
+ print()
46
+ print("Next steps:")
47
+ print(f" cd {args.project_name}")
48
+ print(" make dev")
49
+ print()
50
+ print("Then open http://localhost:5173 in your browser.")
@@ -0,0 +1,10 @@
1
+ # Database
2
+ POSTGRES_USER=app
3
+ POSTGRES_PASSWORD=changeme
4
+ POSTGRES_DB=app
5
+ DATABASE_URL=postgresql://app:changeme@db:5432/app
6
+
7
+ # Flask
8
+ SECRET_KEY=change-this-to-a-random-secret
9
+ FLASK_ENV=development
10
+ FLASK_DEBUG=1
@@ -0,0 +1,23 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+
7
+ # Node
8
+ node_modules/
9
+ dist/
10
+
11
+ # Environment
12
+ .env
13
+
14
+ # IDE
15
+ .vscode/
16
+ .idea/
17
+
18
+ # OS
19
+ .DS_Store
20
+ Thumbs.db
21
+
22
+ # Docker
23
+ *.log
@@ -0,0 +1,22 @@
1
+ .PHONY: dev stop logs db-migrate db-upgrade shell clean
2
+
3
+ dev:
4
+ docker compose up --build
5
+
6
+ stop:
7
+ docker compose down
8
+
9
+ logs:
10
+ docker compose logs -f
11
+
12
+ db-migrate:
13
+ docker compose exec backend uv run flask db migrate
14
+
15
+ db-upgrade:
16
+ docker compose exec backend uv run flask db upgrade
17
+
18
+ shell:
19
+ docker compose exec backend uv run flask shell
20
+
21
+ clean:
22
+ docker compose down -v
@@ -0,0 +1,3 @@
1
+ .venv
2
+ __pycache__
3
+ *.pyc
@@ -0,0 +1,6 @@
1
+ FROM python:3.12-slim
2
+ WORKDIR /app
3
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
4
+ COPY pyproject.toml uv.lock* ./
5
+ RUN uv sync
6
+ COPY . .
@@ -0,0 +1,23 @@
1
+ import os
2
+ from flask import Flask
3
+ from .config import config
4
+ from .extensions import db, migrate, login_manager
5
+
6
+
7
+ def create_app():
8
+ app = Flask(__name__, static_folder="static", static_url_path="/")
9
+
10
+ env = os.getenv("FLASK_ENV", "development")
11
+ app.config.from_object(config[env])
12
+
13
+ db.init_app(app)
14
+ migrate.init_app(app, db)
15
+ login_manager.init_app(app)
16
+
17
+ from .routes import api_bp
18
+ from .routes.main import main_bp
19
+
20
+ app.register_blueprint(api_bp)
21
+ app.register_blueprint(main_bp)
22
+
23
+ return app
@@ -0,0 +1,29 @@
1
+ import os
2
+
3
+
4
+ class BaseConfig:
5
+ SECRET_KEY = os.getenv("SECRET_KEY", "change-this-to-a-random-secret")
6
+ SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL")
7
+ SQLALCHEMY_TRACK_MODIFICATIONS = False
8
+ SESSION_COOKIE_SAMESITE = "Lax"
9
+ SESSION_COOKIE_HTTPONLY = True
10
+
11
+
12
+ class DevelopmentConfig(BaseConfig):
13
+ DEBUG = True
14
+
15
+
16
+ class ProductionConfig(BaseConfig):
17
+ DEBUG = False
18
+
19
+
20
+ class TestConfig(BaseConfig):
21
+ TESTING = True
22
+ SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
23
+
24
+
25
+ config = {
26
+ "development": DevelopmentConfig,
27
+ "production": ProductionConfig,
28
+ "testing": TestConfig,
29
+ }
@@ -0,0 +1,19 @@
1
+ from flask import jsonify
2
+ from flask_login import LoginManager
3
+ from flask_migrate import Migrate
4
+ from flask_sqlalchemy import SQLAlchemy
5
+
6
+ db = SQLAlchemy()
7
+ migrate = Migrate()
8
+ login_manager = LoginManager()
9
+
10
+
11
+ @login_manager.user_loader
12
+ def load_user(user_id):
13
+ from .models.user import User
14
+ return db.session.get(User, int(user_id))
15
+
16
+
17
+ @login_manager.unauthorized_handler
18
+ def unauthorized():
19
+ return jsonify({"error": "Authentication required"}), 401
@@ -0,0 +1,3 @@
1
+ from .user import User
2
+
3
+ __all__ = ["User"]
@@ -0,0 +1,18 @@
1
+ from datetime import datetime, timezone
2
+ from flask_login import UserMixin
3
+ from werkzeug.security import generate_password_hash, check_password_hash
4
+ from ..extensions import db
5
+
6
+
7
+ class User(UserMixin, db.Model):
8
+ id = db.Column(db.Integer, primary_key=True)
9
+ email = db.Column(db.String(255), unique=True, nullable=False, index=True)
10
+ password_hash = db.Column(db.String(256), nullable=False)
11
+ is_active = db.Column(db.Boolean, default=True)
12
+ created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
13
+
14
+ def set_password(self, password):
15
+ self.password_hash = generate_password_hash(password)
16
+
17
+ def check_password(self, password):
18
+ return check_password_hash(self.password_hash, password)
@@ -0,0 +1,16 @@
1
+ from flask import Blueprint
2
+ from flask_restx import Api
3
+
4
+ api_bp = Blueprint("api", __name__, url_prefix="/api")
5
+
6
+ api = Api(
7
+ api_bp,
8
+ version="1.0",
9
+ title="API",
10
+ description="Flask + React Starter API",
11
+ doc="/docs",
12
+ )
13
+
14
+ from .auth import ns as auth_ns # noqa: E402
15
+
16
+ api.add_namespace(auth_ns)
@@ -0,0 +1,107 @@
1
+ from flask_login import current_user, login_required, login_user, logout_user
2
+ from flask_restx import Namespace, Resource, fields
3
+ from ..extensions import db
4
+ from ..models.user import User
5
+
6
+ ns = Namespace("auth", description="Authentication")
7
+
8
+ credentials_model = ns.model("Credentials", {
9
+ "email": fields.String(required=True, description="User email"),
10
+ "password": fields.String(required=True, description="User password"),
11
+ })
12
+
13
+ user_model = ns.model("User", {
14
+ "id": fields.Integer(description="User ID"),
15
+ "email": fields.String(description="User email"),
16
+ "created_at": fields.DateTime(description="Account creation date"),
17
+ })
18
+
19
+ error_model = ns.model("Error", {
20
+ "error": fields.String(description="Error message"),
21
+ })
22
+
23
+ message_model = ns.model("Message", {
24
+ "message": fields.String(description="Status message"),
25
+ })
26
+
27
+
28
+ @ns.route("/register")
29
+ class Register(Resource):
30
+ @ns.expect(credentials_model)
31
+ @ns.response(201, "User created", user_model)
32
+ @ns.response(400, "Validation error", error_model)
33
+ @ns.response(409, "Email already registered", error_model)
34
+ def post(self):
35
+ """Register a new user."""
36
+ data = ns.payload
37
+ email = data.get("email")
38
+ password = data.get("password")
39
+
40
+ if not email or not password:
41
+ return {"error": "Email and password are required"}, 400
42
+
43
+ if User.query.filter_by(email=email).first():
44
+ return {"error": "Email already registered"}, 409
45
+
46
+ user = User(email=email)
47
+ user.set_password(password)
48
+ db.session.add(user)
49
+ db.session.commit()
50
+
51
+ login_user(user)
52
+
53
+ return {
54
+ "id": user.id,
55
+ "email": user.email,
56
+ "created_at": user.created_at.isoformat(),
57
+ }, 201
58
+
59
+
60
+ @ns.route("/login")
61
+ class Login(Resource):
62
+ @ns.expect(credentials_model)
63
+ @ns.response(200, "Login successful", user_model)
64
+ @ns.response(401, "Invalid credentials", error_model)
65
+ def post(self):
66
+ """Log in with email and password."""
67
+ data = ns.payload
68
+ email = data.get("email")
69
+ password = data.get("password")
70
+
71
+ user = User.query.filter_by(email=email).first()
72
+
73
+ if user is None or not user.check_password(password):
74
+ return {"error": "Invalid email or password"}, 401
75
+
76
+ login_user(user)
77
+
78
+ return {
79
+ "id": user.id,
80
+ "email": user.email,
81
+ "created_at": user.created_at.isoformat(),
82
+ }, 200
83
+
84
+
85
+ @ns.route("/logout")
86
+ class Logout(Resource):
87
+ @login_required
88
+ @ns.response(200, "Logged out", message_model)
89
+ @ns.response(401, "Not authenticated", error_model)
90
+ def post(self):
91
+ """Log out the current user."""
92
+ logout_user()
93
+ return {"message": "Logged out"}, 200
94
+
95
+
96
+ @ns.route("/me")
97
+ class Me(Resource):
98
+ @login_required
99
+ @ns.response(200, "Current user", user_model)
100
+ @ns.response(401, "Not authenticated", error_model)
101
+ def get(self):
102
+ """Get the current authenticated user."""
103
+ return {
104
+ "id": current_user.id,
105
+ "email": current_user.email,
106
+ "created_at": current_user.created_at.isoformat(),
107
+ }, 200
@@ -0,0 +1,19 @@
1
+ import os
2
+ from flask import Blueprint, send_from_directory, current_app
3
+
4
+ main_bp = Blueprint("main", __name__)
5
+
6
+
7
+ @main_bp.route("/", defaults={"path": ""})
8
+ @main_bp.route("/<path:path>")
9
+ def serve(path):
10
+ static_folder = os.path.join(current_app.root_path, "static")
11
+
12
+ if path and os.path.exists(os.path.join(static_folder, path)):
13
+ return send_from_directory(static_folder, path)
14
+
15
+ index_path = os.path.join(static_folder, "index.html")
16
+ if os.path.exists(index_path):
17
+ return send_from_directory(static_folder, "index.html")
18
+
19
+ return "Frontend not built. Run the Vite dev server or build the frontend.", 404
@@ -0,0 +1 @@
1
+ Single-database configuration for Flask.
@@ -0,0 +1,40 @@
1
+ [alembic]
2
+
3
+ [loggers]
4
+ keys = root,sqlalchemy,alembic,flask_migrate
5
+
6
+ [handlers]
7
+ keys = console
8
+
9
+ [formatters]
10
+ keys = generic
11
+
12
+ [logger_root]
13
+ level = WARN
14
+ handlers = console
15
+ qualname =
16
+
17
+ [logger_sqlalchemy]
18
+ level = WARN
19
+ handlers =
20
+ qualname = sqlalchemy.engine
21
+
22
+ [logger_alembic]
23
+ level = INFO
24
+ handlers =
25
+ qualname = alembic
26
+
27
+ [logger_flask_migrate]
28
+ level = INFO
29
+ handlers =
30
+ qualname = flask_migrate
31
+
32
+ [handler_console]
33
+ class = StreamHandler
34
+ args = (sys.stderr,)
35
+ level = NOTSET
36
+ formatter = generic
37
+
38
+ [formatter_generic]
39
+ format = %(levelname)-5.5s [%(name)s] %(message)s
40
+ datefmt = %H:%M:%S