Skip to content

Repository files navigation

hashing_encrypt

Small backend demo project: a FastAPI JSON API for one-time encrypted secrets. The service lets a client create a secret with a passphrase, receive a secret_key, and reveal the secret only once.

The project is based on a backend test-task idea and expanded with a cleaner layered architecture, async SQLAlchemy, encrypted storage, passphrase hashing, Docker Compose, and tests.

Features

  • Create one-time secrets with POST /generate.
  • Reveal secrets once with POST /secrets/{secret_key}.
  • Store secrets encrypted with Fernet.
  • Store passphrases as bcrypt hashes, never as plain text.
  • Validate request and response bodies with Pydantic schemas.
  • Use async SQLAlchemy with PostgreSQL.
  • Keep HTTP, service, repository, database, config, and security code separated.
  • Run API and PostgreSQL with Docker Compose.
  • Include pytest coverage for security helpers, schemas, and API flow.
  • Provide an optional static index.html page for manual API testing.

Tech Stack

  • Python 3.14
  • FastAPI
  • Uvicorn
  • SQLAlchemy async
  • asyncpg
  • PostgreSQL
  • Pydantic Settings
  • bcrypt
  • cryptography/Fernet
  • pytest
  • Docker Compose

Project Structure

app/
  main.py
  api/
    deps.py
    routes/
      secrets.py
  core/
    config.py
    exceptions.py
    security.py
  db/
    models.py
    session.py
  repositories/
    secrets.py
  schemas/
    secrets.py
  services/
    secrets.py
tests/
  test_api_secrets.py
  test_schemas.py
  test_security.py
Dockerfile
docker-compose.yaml
requirements.txt
requirements-dev.txt
index.html

Layer responsibilities:

  • api/routes handles HTTP endpoints and HTTP errors.
  • api/deps.py contains FastAPI dependencies.
  • schemas defines request and response models.
  • services contains business logic.
  • repositories contains direct database operations.
  • db contains SQLAlchemy models and session setup.
  • core contains settings, security helpers, and custom exceptions.

API

Create Secret

POST /generate
Content-Type: application/json

Request:

{
  "secret": "launch_discount=17",
  "passphrase": "qwerty"
}

Response:

{
  "secret_key": "generated-secret-key"
}

Reveal Secret

POST /secrets/{secret_key}
Content-Type: application/json

Request:

{
  "passphrase": "qwerty"
}

Response:

{
  "secret": "launch_discount=17"
}

The same secret cannot be revealed twice. A second reveal returns 404.

Wrong passphrase returns 403.

Run With Docker Compose

Start the API and PostgreSQL:

docker compose up --build

Open API docs:

http://127.0.0.1:8000/docs

Stop services:

docker compose down

Remove database data too:

docker compose down -v

Local Development

Create local .env from .env.example.

For local Python running outside Docker, keep:

DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:8291/postgres

Start PostgreSQL:

docker compose up -d postgres

Install runtime dependencies:

.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt

Install development dependencies:

pip install -r requirements-dev.txt

Run API locally:

uvicorn app.main:app --reload

Run tests:

.\.venv\Scripts\python.exe -m pytest

Optional Demo UI

The static index.html file is only a manual testing helper.

Start a local static server:

python -m http.server 63342

Open:

http://127.0.0.1:63342/index.html

Notes

  • Alembic is included as a dependency, but this demo currently uses SQLAlchemy create_all() on app startup for simplicity.
  • TTL cleanup is intentionally not implemented because the task does not require secret expiration.
  • Docker Compose uses postgres:5432 inside the Docker network and localhost:8291 for local host access.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages