{"id":30174084,"url":"https://github.com/id-andyyy/url-alias-api","last_synced_at":"2026-04-08T18:02:35.294Z","repository":{"id":297307149,"uuid":"993692211","full_name":"id-andyyy/url-alias-api","owner":"id-andyyy","description":"🔗🌐 Сервис для создания коротких URL с возможностью просмотра статистики","archived":false,"fork":false,"pushed_at":"2025-07-01T14:18:04.000Z","size":109,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-12T00:22:52.815Z","etag":null,"topics":["alembic","docker","docker-compose","fastapi","postgresql","pydantic","pytest","python","shorturl","sqlalchemy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/id-andyyy.png","metadata":{"files":{"readme":"README-en.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-31T10:07:34.000Z","updated_at":"2025-07-01T14:18:08.000Z","dependencies_parsed_at":"2025-06-05T02:21:47.735Z","dependency_job_id":"638a5281-1a24-410b-a100-f515d26b7360","html_url":"https://github.com/id-andyyy/url-alias-api","commit_stats":null,"previous_names":["id-andyyy/yadro-url-alias-intern","id-andyyy/url-alias-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/id-andyyy/url-alias-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/id-andyyy%2Furl-alias-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/id-andyyy%2Furl-alias-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/id-andyyy%2Furl-alias-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/id-andyyy%2Furl-alias-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/id-andyyy","download_url":"https://codeload.github.com/id-andyyy/url-alias-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/id-andyyy%2Furl-alias-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31567227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alembic","docker","docker-compose","fastapi","postgresql","pydantic","pytest","python","shorturl","sqlalchemy"],"created_at":"2025-08-12T00:15:53.475Z","updated_at":"2026-04-08T18:02:35.277Z","avatar_url":"https://github.com/id-andyyy.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Art](https://i.postimg.cc/XYf5YtLW/art.png)\n\n![GitHub Created At](https://img.shields.io/github/created-at/id-andyyy/url-alias-api?style=flat\u0026color=3C0081)\n![](https://tokei.rs/b1/github/id-andyyy/url-alias-api?style=flat\u0026category=code\u0026color=3FDFFF)\n![Top Language](https://img.shields.io/github/languages/top/id-andyyy/url-alias-api?style=flat)\n![Pet Project](https://img.shields.io/badge/pet-project-8400FF)\n\n# URL Alias Service\n\nA service for converting long URLs into short unique URLs, with the ability to view statistics\u0026nbsp;\u0026#128279;.\n\n## \u0026#128268;\u0026nbsp;API Endpoints\n\nThe following endpoints are implemented:\n\n- \u0026#129517;\u0026nbsp;`GET /{short_id}/` - redirect to the original URL using the short link. Each redirect is tracked in the statistics.\n- \u0026#128279;\u0026nbsp;`POST /api/links/` - create a short link. You can specify the number of seconds after which the link will become invalid. Authorization required\u0026nbsp;\u0026#128274;.\n- \u0026#128203;\u0026nbsp;`GET /api/links/` - get information about your created links. You can filter by inactive and expired links. Pagination is available. Authorization required\u0026nbsp;\u0026#128274;.\n- \u0026#128202;\u0026nbsp;`GET /api/stats/` - get statistics on your most visited links in the last hour, last day, or all time. You can configure sorting and the number of links displayed. Authorization required\u0026nbsp;\u0026#128274;.\n- \u0026#128200;\u0026nbsp;`GET /api/stats/{short_id}/` - get statistics for a specific link. Authorization required\u0026nbsp;\u0026#128274;.\n- \u0026#128161;\u0026nbsp;`GET /health/` - service health check.\n\n## \u0026#128218;\u0026nbsp;Technologies and Tools\n\n![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge\u0026logo=python\u0026logoColor=ffffff)\n![FastAPI](https://img.shields.io/badge/FastAPI-005571?style=for-the-badge\u0026logo=fastapi\u0026color=009485\u0026logoColor=white)\n![Postgres](https://img.shields.io/badge/postgres-%23316192.svg?style=for-the-badge\u0026logo=postgresql\u0026logoColor=white)\n![SQLite](https://img.shields.io/badge/sqlite-%2307405e.svg?style=for-the-badge\u0026logo=sqlite\u0026logoColor=white)\n![SQLAlchemy](https://img.shields.io/badge/sqlalchemy-%ff2f2e.svg?style=for-the-badge\u0026logo=sqlalchemy\u0026logoColor=white\u0026color=ff2f2e)\n![alembic](https://img.shields.io/badge/alembic-%230db7ed.svg?style=for-the-badge\u0026logo=alembic\u0026logoColor=white\u0026color=black)\n![Pytest](https://img.shields.io/badge/pytest-%23ffffff.svg?style=for-the-badge\u0026logo=pytest\u0026logoColor=2f9fe3)\n![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge\u0026logo=docker\u0026logoColor=white)\n\n- Python 3.11\n- REST API with FastAPI\n- PostgreSQL 13 on the production stack and in Docker Compose\n- In-memory SQLite for tests\n- SQLAlchemy for database operations\n- Alembic for migrations\n- Basic Authentication\n- Password hashing with bcrypt\n- Pytest, TestClient, and Monkeypatch for testing\n- Docker and Docker Compose\n\n## \u0026#128161;\u0026nbsp;Technical Decisions\n\n- Data Validation and Schema\n    - Pydantic v2 + pydantic-settings (description of input/output JSON models)\n- ORM and Database Operations\n    - SQLAlchemy (Declarative Base + `Mapped`/`mapped_column`)\n- Schema Migrations\n    - Alembic (provides the ability to scale the database without losing existing data)\n    - In Docker, `alembic upgrade head` is always executed on container startup to keep the data up to date\n- Link Shortening\n    - Generation of random short identifiers (short_id) from letters and numbers\n    - Uniqueness check of short_id before saving\n    - Configurable link lifetime (expire_seconds)\n    - Tracking of link click statistics\n- Authentication\n    - Basic Authentication\n    - Secure password storage using bcrypt (via passlib)\n    - Scripts for manual user creation (create_user.py)\n    - Automatic creation of a default user when the service starts\n- Containerization\n    - Docker Compose\n        - `db` service (Postgres 13 + volume for persistence)\n        - `web` service (Healthcheck `pg_isready`, `depends_on: condition: service_healthy` dependency)\n    - `entrypoint.sh` script for automatic execution of migrations, user creation, and application launch\n    - `.env` file (standard variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` for compatibility with the Postgres Docker image, and `DEFAULT_USER_USERNAME` and `DEFAULT_USER_PASSWORD` for automatic user creation at startup)\n- Testing\n    - \u003e98% code coverage\n    - TestClient (for end-to-end API tests without running an external server)\n    - In-memory SQLite (a lightweight, isolated database to speed up tests)\n    - Fixtures with transaction rollback (each `db_session` rolls back changes after the test, keeping the state clean)\n- Error Handling\n    - Explicit input data checks\n    - Global `exception_handler` for IntegrityError\n- Layer Separation\n    - `routers/` - HTTP + validation\n    - `crud/` - database operations\n    - `utils/` - utility functions\n\n## \u0026#9997;\u0026nbsp;Data Models\n\n- User\u0026nbsp;\u0026#128104;\u0026#8205;\u0026#128187; - user model with hashed passwords\n- Link\u0026nbsp;\u0026#128279; - model for storing original and short URLs\n- Click\u0026nbsp;\u0026#128070; - model for registering link clicks and collecting statistics\n\n## \u0026#128640;\u0026nbsp;How to run the service\n\n1. Clone the repository and navigate to the project folder:\n    ```bash\n    git clone https://github.com/id-andyyy/url-alias-api.git\n    cd url-alias-api\n    ```\n\n2. Create an environment file based on `.env.example`:\n    ```bash\n    cp .env.example .env\n    ```\n\n3. If necessary, fill in the `DEFAULT_USER_USERNAME` and `DEFAULT_USER_PASSWORD` variables in the `.env` file to automatically create a user when the server starts. By default, a user `admin` with the password `admin` is created.\n\n4. Start Docker Compose (don't forget to start the Docker daemon first):\n    ```bash\n    docker-compose up --build\n    ```\n    Wait for the process to finish.\n\n5. Check the service's health via the terminal:\n    ```bash\n    curl http://0.0.0.0:8080/api/health\n    ```\n    \n    Expected response:\n\n    ```bash\n    {\"status\":\"ok\"}\n    ```\n\n    Or via the Swagger UI in your browser:\n\n    ```bash\n    http://127.0.0.1:8080/docs\n    ```\n\n6. To create another user:\n    \n    1. Go to the console:\n        ```bash\n        docker-compose exec web sh\n        ```\n    \n    2. In the console, execute the command (replace `new_user` and `secret_password` with your desired values):\n        ```bash\n        python3 create_user.py -u new_user -p secret_password\n        ```\n    \n    3. If the user is created successfully, after a message about version incompatibility (which can be ignored), you will receive a message:\n        ```bash\n        New user created: username='new_user', id=2\n        ```\n\n    4. To exit the console, execute:\n        ```bash\n        exit\n        ```\n\n## \u0026#129514;\u0026nbsp;How to run tests\n\n1. Create and activate a virtual environment:\n    ```bash\n    python3 -m venv .venv\n\n    source .venv/bin/activate       # On macOS / Linux\n    .\\.venv\\Scripts\\Activate.ps1    # On Windows\n    ```\n\n2. Install the dependencies:\n    ```bash\n    pip install -r requirements.txt\n    ```\n\n3. Run the tests with the command:\n    ```bash\n    pytest\n    ```\n\n## \u0026#128221;\u0026nbsp;Project Structure\n\n```\nalembic/\n│   ├── versions/       # Migration scripts\n│   └── env.py          # Alembic configuration\n\napp/\n├── api/\n│   ├── routes/         # Endpoints and their logic\n│   └── deps.py         # FastAPI dependencies\n├── core/\n│   ├── config.py       # Reading .env\n├── crud/               # Database operation functions\n├── db/\n│   ├── base.py         # Base class for DeclarativeBase\n│   └── session.py      # Engine and SessionLocal setup\n├── models/             # Declarative models \n├── schemas/            # Request and response models\n├── utils/              # Utility functions        \n└── main.py             # Application creation\n\ntests/\n├── api/                # API tests via TestClient\n├── crud/               # Unit tests for CRUD functions\n├── fixtures/           # Additional fixtures\n├── utils/              # Unit tests for utility functions\n└── conftest.py         # Common fixtures       \n\n.dockerignore           # Files ignored by Docker\n.env                    # Local environment variables\n.env.example            # Example .env content\n.gitignore              # Files ignored by Git\nalembic.ini             # Alembic configuration\ncreate_default_user.py  # Script for automatic user creation at startup\ncreate_user.py          # Script for manual user creation\ndocker-compose.yaml     # Docker services description\nDockerfile              # Docker image build instructions\nentrypoint.sh           # web container startup script\nrequirements.txt        # List of dependencies\n```\n\n## \u0026#128232;\u0026nbsp;Feedback\n\nI would appreciate it if you give it a star\u0026nbsp;\u0026#11088;. If you find a bug or have suggestions for improvement, please use the [Issues](https://github.com/id-andyyy/url-alias-api/issues) section.\n\nЧитать на [русском\u0026nbsp;🇷🇺](README.md)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fid-andyyy%2Furl-alias-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fid-andyyy%2Furl-alias-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fid-andyyy%2Furl-alias-api/lists"}