{"id":30133768,"url":"https://github.com/lftobs/penfolio","last_synced_at":"2025-08-10T20:11:50.092Z","repository":{"id":301750918,"uuid":"1009143255","full_name":"Lftobs/penfolio","owner":"Lftobs","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-28T16:00:59.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-28T16:33:20.583Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/Lftobs.png","metadata":{"files":{"readme":"README.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-06-26T16:39:23.000Z","updated_at":"2025-06-28T16:01:02.000Z","dependencies_parsed_at":"2025-06-28T16:33:43.138Z","dependency_job_id":"6703e9ad-d0ef-4ad7-aac4-44117e8d14d4","html_url":"https://github.com/Lftobs/penfolio","commit_stats":null,"previous_names":["lftobs/penfolio"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Lftobs/penfolio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lftobs%2Fpenfolio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lftobs%2Fpenfolio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lftobs%2Fpenfolio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lftobs%2Fpenfolio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lftobs","download_url":"https://codeload.github.com/Lftobs/penfolio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lftobs%2Fpenfolio/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269780617,"owners_count":24474686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-08-10T20:11:46.243Z","updated_at":"2025-08-10T20:11:50.073Z","avatar_url":"https://github.com/Lftobs.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Penfolio API\n\nA secure, feature-rich backend API for a personal journaling application. Built with **Django** and **Django Ninja**, it provides a robust set of endpoints for creating, managing, and securing journal entries.\n\nThis API is designed to be consumed by a separate frontend client (e.g., a web app built with React/Vue, or a mobile application). It features JWT-based authentication, PIN-protected \"Covert\" journals, gamified journaling streaks, and social media integration.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)\n[![Django Version](https://img.shields.io/badge/django-4.x-green.svg)](https://www.djangoproject.com/)\n[![Django Ninja](https://img.shields.io/badge/api-django--ninja-lightgrey.svg)](https://django-ninja.rest-framework.com/)\n\n---\n\n## ✨ Core Features\n\n*   **Secure User Authentication**:\n    *   Register with email/username and password.\n    *   JWT (JSON Web Token) based login for stateless sessions.\n    *   Twitter OAuth2 for social login.\n    *   Endpoints for email verification and password reset (requires email backend setup).\n*   **Complete Journal Management (CRUD)**:\n    *   Create, Read, Update, and Delete journal entries.\n    *   Paginated lists for efficient data retrieval.\n*   **Mood Tagging**: Categorize journals as 'Merry', 'Gloomy', or 'Covert'.\n*   **PIN-Protected \"Covert\" Journals**: A standout privacy feature.\n    *   Users must set a 4-digit PIN on their profile.\n    *   A valid PIN is required to list all covert journals.\n    *   A valid PIN is required to view the content of a single covert journal.\n*   **Gamification with Journaling Streaks**:\n    *   Automatically tracks `current_streak` and `longest_streak` for consecutive days of journaling to encourage user engagement.\n*   **Powerful Search**: Full-text search across journal titles and content.\n*   **Social Media Integration**:\n    *   Generate a tweet based on a journal's content.\n    *   Optionally provide a Twitter handle to \"inspire\" the tone of the generated tweet.\n*   **Rich Content**:\n    *   Support for Markdown in journal content.\n    *   Image uploads directly to **Cloudinary**.\n*   **Automatic API Documentation**: Interactive Swagger UI and ReDoc documentation generated automatically by Django Ninja.\n\n## 🛠️ Technology Stack\n\n*   **Backend**: Django 4.x\n*   **API Framework**: Django Ninja\n*   **Authentication**: Django Ninja JWT (for token-based auth), Python Social Auth (for Twitter OAuth)\n*   **Database**: PostgreSQL (recommended), SQLite3 (for development)\n*   **Image Storage**: Cloudinary\n*   **Data Validation**: Pydantic (via Django Ninja)\n\n## 🚀 Getting Started\n\nFollow these instructions to get the project up and running on your local machine for development and testing.\n\n### 1. Prerequisites\n\n*   Python 3.9+\n*   Poetry or Pip for package management\n*   A Cloudinary account for image uploads\n*   An active email service (like SendGrid, Mailgun, or Gmail for development) for password resets.\n\n### 2. Installation \u0026 Setup\n\n1.  **Clone the repository:**\n    ```bash\n    git clone https://github.com/your-username/myjournal-api.git\n    cd myjournal-api\n    ```\n\n2.  **Create and activate a virtual environment:**\n    ```bash\n    python -m venv venv\n    source venv/bin/activate  # On Windows, use `venv\\Scripts\\activate`\n    ```\n\n3.  **Install dependencies:**\n    ```bash\n    uv sync\n    ```\n\n4.  **Set up environment variables:**\n    Create a `.env` file in the project root directory. You can copy the example below and fill in your own credentials.\n    ```ini\n    # .env\n\n    # Django Settings\n    SECRET_KEY='your-strong-django-secret-key'\n    DEBUG=True\n\n    # Database Settings (PostgreSQL example)\n    DB_NAME='myjournal_db'\n    DB_USER='your_db_user'\n    DB_PASSWORD='your_db_password'\n    DB_HOST='localhost'\n    DB_PORT='5432'\n\n    # Cloudinary Credentials for image uploads\n    CLOUDINARY_CLOUD_NAME='your-cloud-name'\n    CLOUDINARY_API_KEY='your-api-key'\n    CLOUDINARY_API_SECRET='your-api-secret'\n\n    # Email Settings (Example for Gmail)\n    EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'\n    EMAIL_HOST='smtp.gmail.com'\n    EMAIL_PORT=587\n    EMAIL_USE_TLS=True\n    EMAIL_HOST_USER='your-email@gmail.com'\n    EMAIL_HOST_PASSWORD='your-gmail-app-password' # Use an App Password, not your regular password\n    ```\n\n5.  **Run database migrations:**\n    ```bash\n    python manage.py makemigrations\n    python manage.py migrate\n    ```\n\n6.  **Create a superuser (optional):**\n    ```bash\n    python manage.py createsuperuser\n    ```\n\n### 3. Running the Development Server\n\n1.  **Start the server:**\n    ```bash\n    python manage.py runserver\n    ```\n2.  **Access the API documentation:**\n    The server will be running at `http://127.0.0.1:8000`. Navigate to the following URLs in your browser to see the interactive API documentation:\n    *   **Swagger UI:** `http://127.0.0.1:8000/api/docs`\n    *   **ReDoc:** `http://127.0.0.1:8000/api/redoc`\n\n---\n\n## 🔑 API Endpoints Overview\n\nAll endpoints are prefixed with `/api`.\n\n### Users \u0026 Authentication (`/users/`)\n\n| Method | Endpoint                    | Authentication | Description                                      |\n| :----- | :-------------------------- | :------------- | :----------------------------------------------- |\n| `POST` | `/register`                 | Public         | Creates a new user account.                      |\n| `POST` | `/login`                    | Public         | Authenticates and returns JWT access/refresh tokens. |\n| `GET`  | `/profile`                  | JWT Required   | Retrieves the authenticated user's profile.      |\n| `POST` | `/profile/set-pin`          | JWT Required   | Sets or updates the user's secret PIN.           |\n| `GET`  | `/auth/twitter`             | Public         | Initiates the Twitter OAuth2 login flow.         |\n| `POST` | `/password/reset`           | Public         | Sends a password reset link to the user's email. |\n| `POST` | `/password/reset/confirm`   | Public         | Resets the password using a token from email.    |\n\n### Journals (`/journals/`)\n\n| Method   | Endpoint                  | Authentication | Description                                                  |\n| :------- | :------------------------ | :------------- | :----------------------------------------------------------- |\n| `GET`    | `/`                       | JWT Required   | Lists all non-covert journals for the authenticated user.    |\n| `POST`   | `/covert`                 | JWT Required   | Lists all covert journals (requires PIN in request body).    |\n| `POST`   | `/`                       | JWT Required   | Creates a new journal entry.                                 |\n| `GET`    | `/{journal_id}`           | JWT Required   | Retrieves a single journal. Hides content if covert.         |\n| `POST`   | `/{journal_id}/reveal`    | JWT Required   | Reveals a covert journal's content (requires PIN).           |\n| `PUT`    | `/{journal_id}`           | JWT Required   | Updates a journal entry.                                     |\n| `DELETE` | `/{journal_id}`           | JWT Required   | Deletes a journal entry.                                     |\n| `GET`    | `/search?q=\u003cquery\u003e`       | JWT Required   | Searches journals by title and content.                      |\n| `POST`   | `/{journal_id}/tweet`     | JWT Required   | Generates a Twitter intent URL from the journal's content.   |\n| `POST`   | `/upload-image`           | JWT Required   | Uploads an image to Cloudinary and returns the URL.          |\n\n## 📄 License\n\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flftobs%2Fpenfolio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flftobs%2Fpenfolio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flftobs%2Fpenfolio/lists"}