diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index b75b2b4..23983da 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -6,27 +6,33 @@ labels: bug
assignees: ''
---
-**Describe the bug**
+# Describe the bug
A clear and concise description of what the bug is.
-**To Reproduce**
+# To Reproduce
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
-**Expected behavior**
+# Expected behavior
A clear and concise description of what you expected to happen.
-**Screenshots or logs**
+# Screenshots or logs
If applicable, add screenshots or terminal logs to help explain your problem.
-**Environment (please complete the following information):**
+# Environment (please complete the following information):
- OS: [e.g. Android 14 / iOS 17 / Ubuntu 22.04]
- App version: [e.g. Android v2.0 / iOS 2.0]
- Node version: [e.g. 20]
- Device/emulator: [e.g. Pixel 6 / iPhone 12]
-**Additional context**
+# Additional context
Add any other context about the problem here.
+
+# Guidelines
+- If you want to work on this bug, please comment on this issue first.
+- Once someone has been assigned or has claimed the issue, no one else should start working on it.
+- If the issue is not completed, you must provide a progress update within 2 days.
+- If no update is provided within 2 days, the issue will be considered available for others to take.
diff --git a/BizBuch API.yaml b/BizBuch API.yaml
deleted file mode 100644
index 7cb180b..0000000
--- a/BizBuch API.yaml
+++ /dev/null
@@ -1,2233 +0,0 @@
-openapi: 3.0.3
-info:
- title: BizBuch API
- version: 1.0.0
- description: API Documentation for BizBuch
-paths:
- /activity/{id}/read/:
- post:
- operationId: activity_read_create
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - activity
- security:
- - jwtAuth: []
- responses:
- '200':
- description: No response body
- /activity/log/:
- get:
- operationId: activity_log_list
- description: Activity log of the authenticated user (actor-based).
- tags:
- - activity
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/ActivityLog'
- description: ''
- /activity/notifications/:
- get:
- operationId: activity_notifications_list
- tags:
- - activity
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/Activity'
- description: ''
- /activity/read-all/:
- post:
- operationId: activity_read_all_create
- tags:
- - activity
- security:
- - jwtAuth: []
- responses:
- '200':
- description: No response body
- /auth/forgot-password/:
- post:
- operationId: auth_forgot_password_create
- tags:
- - auth
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ForgotPasswordRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/ForgotPasswordRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/ForgotPasswordRequest'
- required: true
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ForgotPassword'
- description: ''
- /auth/login/:
- post:
- operationId: auth_login_create
- tags:
- - auth
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/LoginRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/LoginRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/LoginRequest'
- required: true
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Login'
- description: ''
- /auth/register/:
- post:
- operationId: auth_register_create
- tags:
- - auth
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/RegisterRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/RegisterRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/RegisterRequest'
- required: true
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Register'
- description: ''
- /auth/resend-otp/:
- post:
- operationId: auth_resend_otp_create
- tags:
- - auth
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ResendOTPRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/ResendOTPRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/ResendOTPRequest'
- required: true
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ResendOTP'
- description: ''
- /auth/reset-password/:
- post:
- operationId: auth_reset_password_create
- tags:
- - auth
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ResetPasswordRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/ResetPasswordRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/ResetPasswordRequest'
- required: true
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ResetPassword'
- description: ''
- /auth/validate-token/:
- post:
- operationId: auth_validate_token_create
- tags:
- - auth
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- description: No response body
- /auth/verify-otp/:
- post:
- operationId: auth_verify_otp_create
- tags:
- - auth
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/VerifyOTPRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/VerifyOTPRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/VerifyOTPRequest'
- required: true
- security:
- - jwtAuth: []
- - {}
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/VerifyOTP'
- description: ''
- /chat/conversations/:
- get:
- operationId: chat_conversations_list
- description: Get all conversations for the authenticated user with preview of
- last message
- summary: List user's conversations
- tags:
- - Chat
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/ConversationList'
- description: ''
- /chat/conversations/{conversation_id}/:
- get:
- operationId: chat_conversations_retrieve
- description: Get detailed view of a conversation with all messages
- summary: Get conversation details
- parameters:
- - in: path
- name: conversation_id
- schema:
- type: integer
- required: true
- tags:
- - Chat
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Conversation'
- description: ''
- /chat/conversations/{conversation_id}/messages/:
- get:
- operationId: chat_conversations_messages_list
- description: Get paginated list of messages in a conversation. Each message
- includes a 'type' field indicating 'sent' or 'received'.
- summary: List messages in conversation
- parameters:
- - in: path
- name: conversation_id
- schema:
- type: integer
- required: true
- - in: query
- name: page
- schema:
- type: integer
- description: Page number
- - in: query
- name: page_size
- schema:
- type: integer
- description: 'Number of messages per page (default: 50)'
- tags:
- - Chat
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/Message'
- description: ''
- /chat/conversations/{conversation_id}/read/:
- post:
- operationId: chat_conversations_read_create
- description: Mark specific messages or all messages in a conversation as read
- summary: Mark messages as read
- parameters:
- - in: path
- name: conversation_id
- schema:
- type: integer
- required: true
- tags:
- - Chat
- requestBody:
- content:
- application/json:
- schema:
- type: object
- properties:
- message_ids:
- type: array
- items:
- type: integer
- description: List of message IDs to mark as read (optional)
- mark_all:
- type: boolean
- description: Mark all messages in conversation as read
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- description: Messages marked as read
- description: ''
- '400':
- content:
- application/json:
- schema:
- description: Bad request
- description: ''
- '404':
- content:
- application/json:
- schema:
- description: Conversation not found
- description: ''
- /chat/conversations/start/:
- post:
- operationId: chat_conversations_start_create
- description: Start a new conversation with a user or get the existing one
- summary: Start or get conversation
- tags:
- - Chat
- requestBody:
- content:
- application/json:
- schema:
- type: object
- properties:
- user_id:
- type: integer
- description: ID of the user to start conversation with
- required:
- - user_id
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Conversation'
- description: ''
- '201':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Conversation'
- description: ''
- '400':
- content:
- application/json:
- schema:
- description: Bad request
- description: ''
- '404':
- content:
- application/json:
- schema:
- description: User not found
- description: ''
- /chat/send/:
- post:
- operationId: chat_send_create
- description: Send a message to another user. Creates conversation if it doesn't
- exist.
- summary: Send a message
- tags:
- - Chat
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/MessageCreateRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/MessageCreateRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/MessageCreateRequest'
- required: true
- security:
- - jwtAuth: []
- responses:
- '201':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Message'
- description: ''
- '400':
- content:
- application/json:
- schema:
- description: Bad request
- description: ''
- '404':
- content:
- application/json:
- schema:
- description: User not found
- description: ''
- /onboarding/status/:
- get:
- operationId: onboarding_status_retrieve
- tags:
- - onboarding
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/OnboardingStatus'
- description: ''
- /onboarding/topics/:
- get:
- operationId: onboarding_topics_retrieve
- tags:
- - onboarding
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Topic'
- description: ''
- /onboarding/topics/submit/:
- post:
- operationId: onboarding_topics_submit_create
- tags:
- - onboarding
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/TopicSelectionRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/TopicSelectionRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/TopicSelectionRequest'
- required: true
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/TopicSelection'
- description: ''
- /posts/:
- get:
- operationId: posts_list
- tags:
- - posts
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/PostModel'
- description: ''
- post:
- operationId: posts_create
- tags:
- - posts
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostModelRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PostModelRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PostModelRequest'
- security:
- - jwtAuth: []
- responses:
- '201':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostModel'
- description: ''
- /posts/{id}/:
- get:
- operationId: posts_retrieve
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - posts
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostModel'
- description: ''
- put:
- operationId: posts_update
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - posts
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostModelRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PostModelRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PostModelRequest'
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostModel'
- description: ''
- patch:
- operationId: posts_partial_update
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - posts
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PatchedPostModelRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PatchedPostModelRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PatchedPostModelRequest'
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostModel'
- description: ''
- delete:
- operationId: posts_destroy
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - posts
- security:
- - jwtAuth: []
- responses:
- '204':
- description: No response body
- /posts/{post_id}/comments/:
- get:
- operationId: posts_comments_list
- parameters:
- - in: path
- name: post_id
- schema:
- type: integer
- required: true
- tags:
- - Comments
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/PostComment'
- description: ''
- post:
- operationId: posts_comments_create
- parameters:
- - in: path
- name: post_id
- schema:
- type: integer
- required: true
- tags:
- - Comments
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostCommentRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PostCommentRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PostCommentRequest'
- required: true
- security:
- - jwtAuth: []
- responses:
- '201':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostComment'
- description: ''
- /posts/{post_id}/likes/:
- get:
- operationId: posts_likes_list
- parameters:
- - in: path
- name: post_id
- schema:
- type: integer
- required: true
- tags:
- - Likes
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/PostLike'
- description: ''
- post:
- operationId: posts_likes_create
- parameters:
- - in: path
- name: post_id
- schema:
- type: integer
- required: true
- tags:
- - Likes
- security:
- - jwtAuth: []
- responses:
- '201':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostLike'
- description: ''
- /posts/{post_id}/report/:
- post:
- operationId: posts_report_create
- parameters:
- - in: path
- name: post_id
- schema:
- type: integer
- required: true
- tags:
- - Reports
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostReportRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PostReportRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PostReportRequest'
- required: true
- security:
- - jwtAuth: []
- responses:
- '201':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostReport'
- description: ''
- /posts/comments/{id}/:
- get:
- operationId: posts_comments_retrieve
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Comments
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostComment'
- description: ''
- put:
- operationId: posts_comments_update
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Comments
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostCommentRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PostCommentRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PostCommentRequest'
- required: true
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostComment'
- description: ''
- patch:
- operationId: posts_comments_partial_update
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Comments
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PatchedPostCommentRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PatchedPostCommentRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PatchedPostCommentRequest'
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PostComment'
- description: ''
- delete:
- operationId: posts_comments_destroy
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Comments
- security:
- - jwtAuth: []
- responses:
- '204':
- description: No response body
- /posts/likes/{id}/:
- delete:
- operationId: posts_likes_destroy
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Likes
- security:
- - jwtAuth: []
- responses:
- '204':
- description: No response body
- /profiles/:
- get:
- operationId: profiles_list
- parameters:
- - name: search
- required: false
- in: query
- description: A search term.
- schema:
- type: string
- tags:
- - profiles
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/ProfileCompact'
- description: ''
- /profiles/{id}/follow/:
- post:
- operationId: profiles_follow_create
- description: Follow a user profile
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Follows
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/FollowResponse'
- description: ''
- /profiles/{id}/followers/:
- get:
- operationId: profiles_followers_list
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Follows
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/ProfileCompact'
- description: ''
- /profiles/{id}/following/:
- get:
- operationId: profiles_following_list
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Follows
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/ProfileCompact'
- description: ''
- /profiles/{id}/unfollow/:
- post:
- operationId: profiles_unfollow_create
- description: Unfollow a user profile
- parameters:
- - in: path
- name: id
- schema:
- type: integer
- required: true
- tags:
- - Follows
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/FollowResponse'
- description: ''
- /profiles/{user_id}/:
- get:
- operationId: profiles_retrieve
- parameters:
- - in: path
- name: user_id
- schema:
- type: integer
- required: true
- tags:
- - profiles
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Profile'
- description: ''
- /profiles/connections/:
- get:
- operationId: profiles_connections_list
- description: |-
- Get the authenticated user's connections (followers + following).
-
- Returns a list of profiles that the user is connected with.
- tags:
- - profiles
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/ProfileCompact'
- description: ''
- /profiles/me/:
- get:
- operationId: profiles_me_retrieve
- tags:
- - profiles
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Profile'
- description: ''
- patch:
- operationId: profiles_me_partial_update
- tags:
- - profiles
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PatchedProfileUpdateRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PatchedProfileUpdateRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PatchedProfileUpdateRequest'
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ProfileUpdate'
- description: ''
- /uploads/presign/:
- post:
- operationId: uploads_presign_create
- tags:
- - uploads
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PresignUploadRequestRequest'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/PresignUploadRequestRequest'
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/PresignUploadRequestRequest'
- required: true
- security:
- - jwtAuth: []
- responses:
- '200':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PresignUploadRequest'
- description: ''
-components:
- schemas:
- AccountTypeEnum:
- enum:
- - personal
- - business
- - creator
- type: string
- description: |-
- * `personal` - personal
- * `business` - business
- * `creator` - creator
- Activity:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- actor_username:
- type: string
- readOnly: true
- verb:
- type: string
- maxLength: 50
- is_read:
- type: boolean
- created_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - actor_username
- - created_at
- - id
- - verb
- ActivityLog:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- verb:
- type: string
- maxLength: 50
- target_type:
- type: string
- nullable: true
- readOnly: true
- target_id:
- type: integer
- nullable: true
- readOnly: true
- created_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - target_id
- - target_type
- - verb
- BlankEnum:
- enum:
- - ''
- Conversation:
- type: object
- description: Detailed serializer for a single conversation.
- properties:
- id:
- type: integer
- readOnly: true
- participants:
- type: array
- items:
- $ref: '#/components/schemas/ConversationParticipant'
- readOnly: true
- messages:
- type: array
- items:
- $ref: '#/components/schemas/Message'
- readOnly: true
- created_at:
- type: string
- format: date-time
- readOnly: true
- updated_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - messages
- - participants
- - updated_at
- ConversationList:
- type: object
- description: Serializer for listing conversations with preview.
- properties:
- id:
- type: integer
- readOnly: true
- other_participant:
- type: string
- readOnly: true
- last_message:
- type: string
- readOnly: true
- unread_count:
- type: string
- readOnly: true
- created_at:
- type: string
- format: date-time
- readOnly: true
- updated_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - last_message
- - other_participant
- - unread_count
- - updated_at
- ConversationParticipant:
- type: object
- description: Serializer for conversation participants.
- properties:
- id:
- type: integer
- username:
- type: string
- first_name:
- type: string
- last_name:
- type: string
- headline:
- type: string
- nullable: true
- required:
- - first_name
- - headline
- - id
- - last_name
- - username
- EmploymentTypeEnum:
- enum:
- - full-time
- - part-time
- - self-employed
- - freelance
- - contract
- - internship
- - apprenticeship
- - seasonal
- type: string
- description: |-
- * `full-time` - Full-time
- * `part-time` - Part-time
- * `self-employed` - Self-employed
- * `freelance` - Freelance
- * `contract` - Contract
- * `internship` - Internship
- * `apprenticeship` - Apprenticeship
- * `seasonal` - Seasonal
- EndMonthEnum:
- enum:
- - 1
- - 2
- - 3
- - 4
- - 5
- - 6
- - 7
- - 8
- - 9
- - 10
- - 11
- - 12
- type: integer
- description: |-
- * `1` - 1
- * `2` - 2
- * `3` - 3
- * `4` - 4
- * `5` - 5
- * `6` - 6
- * `7` - 7
- * `8` - 8
- * `9` - 9
- * `10` - 10
- * `11` - 11
- * `12` - 12
- FollowResponse:
- type: object
- properties:
- detail:
- type: string
- required:
- - detail
- ForgotPassword:
- type: object
- properties:
- email:
- type: string
- format: email
- required:
- - email
- ForgotPasswordRequest:
- type: object
- properties:
- email:
- type: string
- format: email
- minLength: 1
- required:
- - email
- LocationTypeEnum:
- enum:
- - current
- - previous
- - hometown
- - other
- type: string
- description: |-
- * `current` - Current
- * `previous` - Previous
- * `hometown` - Hometown
- * `other` - Other
- Login:
- type: object
- properties:
- username:
- type: string
- required:
- - username
- LoginRequest:
- type: object
- properties:
- username:
- type: string
- minLength: 1
- password:
- type: string
- writeOnly: true
- minLength: 1
- required:
- - password
- - username
- Message:
- type: object
- description: Serializer for Message model.
- properties:
- id:
- type: integer
- readOnly: true
- conversation:
- type: integer
- sender_id:
- type: integer
- readOnly: true
- sender_username:
- type: string
- readOnly: true
- sender_first_name:
- type: string
- readOnly: true
- sender_last_name:
- type: string
- readOnly: true
- content:
- type: string
- timestamp:
- type: string
- format: date-time
- readOnly: true
- is_read:
- type: boolean
- readOnly: true
- read_at:
- type: string
- format: date-time
- readOnly: true
- nullable: true
- required:
- - content
- - conversation
- - id
- - is_read
- - read_at
- - sender_first_name
- - sender_id
- - sender_last_name
- - sender_username
- - timestamp
- MessageCreateRequest:
- type: object
- description: Serializer for creating a new message.
- properties:
- recipient_id:
- type: integer
- content:
- type: string
- minLength: 1
- maxLength: 5000
- required:
- - content
- - recipient_id
- NullEnum:
- enum:
- - null
- OnboardingStatus:
- type: object
- properties:
- completed:
- type: boolean
- required:
- - completed
- PatchedPostCommentRequest:
- type: object
- properties:
- content:
- type: string
- minLength: 1
- PatchedPostModelRequest:
- type: object
- properties:
- content:
- type: string
- poll:
- nullable: true
- location:
- type: string
- maxLength: 255
- feeling:
- type: string
- maxLength: 100
- privacy:
- type: string
- minLength: 1
- maxLength: 50
- PatchedProfileUpdateRequest:
- type: object
- description: |-
- Serializer for updating profile and user information.
- Separates read and write operations - follows Single Responsibility Principle.
- properties:
- display_name:
- type: string
- minLength: 1
- maxLength: 150
- bio:
- type: string
- avatar:
- type: string
- nullable: true
- minLength: 1
- headline:
- type: string
- maxLength: 255
- current_position:
- type: string
- maxLength: 255
- company:
- type: string
- maxLength: 255
- industry:
- type: string
- maxLength: 100
- company_logo:
- type: string
- nullable: true
- minLength: 1
- cover_image:
- type: string
- nullable: true
- minLength: 1
- phone:
- type: string
- maxLength: 20
- website:
- type: string
- format: uri
- linkedin_url:
- type: string
- format: uri
- twitter_url:
- type: string
- format: uri
- account_type:
- $ref: '#/components/schemas/AccountTypeEnum'
- open_to_work:
- type: boolean
- open_to_hire:
- type: boolean
- is_public:
- type: boolean
- PostComment:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- post:
- type: integer
- readOnly: true
- user:
- type: string
- description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
- only.
- readOnly: true
- content:
- type: string
- created_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - content
- - created_at
- - id
- - post
- - user
- PostCommentRequest:
- type: object
- properties:
- content:
- type: string
- minLength: 1
- required:
- - content
- PostLike:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- user:
- type: string
- description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
- only.
- readOnly: true
- post:
- type: integer
- readOnly: true
- created_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - post
- - user
- PostModel:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- author:
- type: string
- description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
- only.
- readOnly: true
- image_url:
- type: string
- readOnly: true
- content:
- type: string
- poll:
- nullable: true
- location:
- type: string
- maxLength: 255
- feeling:
- type: string
- maxLength: 100
- privacy:
- type: string
- maxLength: 50
- created_at:
- type: string
- format: date-time
- readOnly: true
- likes_count:
- type: integer
- readOnly: true
- comments_count:
- type: integer
- readOnly: true
- shares_count:
- type: integer
- readOnly: true
- required:
- - author
- - comments_count
- - created_at
- - id
- - image_url
- - likes_count
- - shares_count
- PostModelRequest:
- type: object
- properties:
- content:
- type: string
- poll:
- nullable: true
- location:
- type: string
- maxLength: 255
- feeling:
- type: string
- maxLength: 100
- privacy:
- type: string
- minLength: 1
- maxLength: 50
- PostReport:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- reported_by:
- type: string
- description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
- only.
- readOnly: true
- post:
- type: integer
- readOnly: true
- reason:
- type: string
- created_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - post
- - reason
- - reported_by
- PostReportRequest:
- type: object
- properties:
- reason:
- type: string
- minLength: 1
- required:
- - reason
- PresignUploadRequest:
- type: object
- properties:
- contentType:
- type: string
- maxLength: 100
- required:
- - contentType
- PresignUploadRequestRequest:
- type: object
- properties:
- contentType:
- type: string
- minLength: 1
- maxLength: 100
- required:
- - contentType
- Profile:
- type: object
- description: |-
- Main profile response serializer with key fields.
- Follows Dependency Inversion Principle - depends on ProfileStatsService.
- properties:
- id:
- type: integer
- display_name:
- type: string
- username:
- type: string
- bio:
- type: string
- avatar:
- type: string
- readOnly: true
- headline:
- type: string
- nullable: true
- current_position:
- type: string
- nullable: true
- company:
- type: string
- nullable: true
- company_logo:
- type: string
- readOnly: true
- industry:
- type: string
- nullable: true
- locations:
- type: array
- items:
- $ref: '#/components/schemas/ProfileLocation'
- readOnly: true
- phone:
- type: string
- nullable: true
- website:
- type: string
- format: uri
- nullable: true
- linkedin_url:
- type: string
- format: uri
- nullable: true
- twitter_url:
- type: string
- format: uri
- nullable: true
- connections_count:
- type: string
- readOnly: true
- followers_count:
- type: string
- readOnly: true
- following_count:
- type: string
- readOnly: true
- posts_count:
- type: string
- readOnly: true
- is_connected:
- type: string
- readOnly: true
- is_following:
- type: string
- readOnly: true
- mutual_connections_count:
- type: string
- readOnly: true
- is_verified:
- type: boolean
- is_premium:
- type: boolean
- account_type:
- type: string
- cover_image:
- type: string
- readOnly: true
- joined_date:
- type: string
- format: date-time
- skills:
- type: array
- items:
- type: string
- educations:
- type: array
- items:
- $ref: '#/components/schemas/ProfileEducation'
- readOnly: true
- work_experiences:
- type: array
- items:
- $ref: '#/components/schemas/ProfileWorkExperience'
- readOnly: true
- open_to_work:
- type: boolean
- open_to_hire:
- type: boolean
- required:
- - account_type
- - avatar
- - bio
- - company
- - company_logo
- - connections_count
- - cover_image
- - current_position
- - display_name
- - educations
- - followers_count
- - following_count
- - headline
- - id
- - industry
- - is_connected
- - is_following
- - is_premium
- - is_verified
- - joined_date
- - linkedin_url
- - locations
- - mutual_connections_count
- - open_to_hire
- - open_to_work
- - phone
- - posts_count
- - skills
- - twitter_url
- - username
- - website
- - work_experiences
- ProfileCompact:
- type: object
- description: |-
- Lightweight profile serializer for list views and embeds.
- Follows Single Responsibility - only essential fields for performance.
- properties:
- id:
- type: integer
- display_name:
- type: string
- username:
- type: string
- avatar:
- type: string
- readOnly: true
- headline:
- type: string
- nullable: true
- company:
- type: string
- nullable: true
- is_verified:
- type: boolean
- followers_count:
- type: string
- readOnly: true
- required:
- - avatar
- - company
- - display_name
- - followers_count
- - headline
- - id
- - is_verified
- - username
- ProfileEducation:
- type: object
- description: Serializer for user profile education
- properties:
- id:
- type: integer
- readOnly: true
- name:
- type: string
- maxLength: 255
- degrees: {}
- duration:
- type: string
- nullable: true
- maxLength: 100
- start_year:
- type: integer
- maximum: 9223372036854775807
- minimum: -9223372036854775808
- format: int64
- nullable: true
- end_year:
- type: integer
- maximum: 9223372036854775807
- minimum: -9223372036854775808
- format: int64
- nullable: true
- is_current:
- type: boolean
- school_logo:
- type: string
- readOnly: true
- description:
- type: string
- created_at:
- type: string
- format: date-time
- readOnly: true
- updated_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - name
- - school_logo
- - updated_at
- ProfileLocation:
- type: object
- description: Serializer for user profile locations
- properties:
- id:
- type: integer
- readOnly: true
- location:
- type: string
- maxLength: 255
- is_primary:
- type: boolean
- location_type:
- $ref: '#/components/schemas/LocationTypeEnum'
- created_at:
- type: string
- format: date-time
- readOnly: true
- updated_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - created_at
- - id
- - location
- - updated_at
- ProfileUpdate:
- type: object
- description: |-
- Serializer for updating profile and user information.
- Separates read and write operations - follows Single Responsibility Principle.
- properties:
- display_name:
- type: string
- maxLength: 150
- bio:
- type: string
- avatar:
- type: string
- nullable: true
- headline:
- type: string
- maxLength: 255
- current_position:
- type: string
- maxLength: 255
- company:
- type: string
- maxLength: 255
- industry:
- type: string
- maxLength: 100
- company_logo:
- type: string
- nullable: true
- cover_image:
- type: string
- nullable: true
- phone:
- type: string
- maxLength: 20
- website:
- type: string
- format: uri
- linkedin_url:
- type: string
- format: uri
- twitter_url:
- type: string
- format: uri
- account_type:
- $ref: '#/components/schemas/AccountTypeEnum'
- open_to_work:
- type: boolean
- open_to_hire:
- type: boolean
- is_public:
- type: boolean
- ProfileWorkExperience:
- type: object
- description: Serializer for user profile work experience
- properties:
- id:
- type: integer
- readOnly: true
- company_name:
- type: string
- maxLength: 255
- job_title:
- type: string
- maxLength: 255
- location:
- type: string
- nullable: true
- maxLength: 255
- employment_type:
- nullable: true
- oneOf:
- - $ref: '#/components/schemas/EmploymentTypeEnum'
- - $ref: '#/components/schemas/BlankEnum'
- - $ref: '#/components/schemas/NullEnum'
- start_year:
- type: integer
- maximum: 9223372036854775807
- minimum: -9223372036854775808
- format: int64
- start_month:
- nullable: true
- minimum: -9223372036854775808
- maximum: 9223372036854775807
- oneOf:
- - $ref: '#/components/schemas/StartMonthEnum'
- - $ref: '#/components/schemas/NullEnum'
- end_year:
- type: integer
- maximum: 9223372036854775807
- minimum: -9223372036854775808
- format: int64
- nullable: true
- end_month:
- nullable: true
- minimum: -9223372036854775808
- maximum: 9223372036854775807
- oneOf:
- - $ref: '#/components/schemas/EndMonthEnum'
- - $ref: '#/components/schemas/NullEnum'
- is_current:
- type: boolean
- company_logo:
- type: string
- readOnly: true
- description:
- type: string
- skills: {}
- created_at:
- type: string
- format: date-time
- readOnly: true
- updated_at:
- type: string
- format: date-time
- readOnly: true
- required:
- - company_logo
- - company_name
- - created_at
- - id
- - job_title
- - start_year
- - updated_at
- Register:
- type: object
- properties:
- username:
- type: string
- description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
- only.
- maxLength: 150
- email:
- type: string
- format: email
- first_name:
- type: string
- maxLength: 150
- last_name:
- type: string
- maxLength: 150
- required:
- - email
- - username
- RegisterRequest:
- type: object
- properties:
- username:
- type: string
- minLength: 1
- description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
- only.
- maxLength: 150
- email:
- type: string
- format: email
- minLength: 1
- password:
- type: string
- writeOnly: true
- minLength: 1
- description: Use a strong password
- confirm_password:
- type: string
- writeOnly: true
- minLength: 1
- description: Retype the password
- first_name:
- type: string
- minLength: 1
- maxLength: 150
- last_name:
- type: string
- minLength: 1
- maxLength: 150
- recaptcha_token:
- type: string
- writeOnly: true
- minLength: 1
- required:
- - confirm_password
- - email
- - password
- - username
- ResendOTP:
- type: object
- properties:
- email:
- type: string
- format: email
- required:
- - email
- ResendOTPRequest:
- type: object
- properties:
- email:
- type: string
- format: email
- minLength: 1
- required:
- - email
- ResetPassword:
- type: object
- properties:
- email:
- type: string
- format: email
- otp:
- type: string
- maxLength: 6
- required:
- - email
- - otp
- ResetPasswordRequest:
- type: object
- properties:
- email:
- type: string
- format: email
- minLength: 1
- otp:
- type: string
- minLength: 1
- maxLength: 6
- new_password:
- type: string
- writeOnly: true
- minLength: 1
- confirm_password:
- type: string
- writeOnly: true
- minLength: 1
- required:
- - confirm_password
- - email
- - new_password
- - otp
- StartMonthEnum:
- enum:
- - 1
- - 2
- - 3
- - 4
- - 5
- - 6
- - 7
- - 8
- - 9
- - 10
- - 11
- - 12
- type: integer
- description: |-
- * `1` - 1
- * `2` - 2
- * `3` - 3
- * `4` - 4
- * `5` - 5
- * `6` - 6
- * `7` - 7
- * `8` - 8
- * `9` - 9
- * `10` - 10
- * `11` - 11
- * `12` - 12
- Topic:
- type: object
- properties:
- id:
- type: integer
- readOnly: true
- name:
- type: string
- maxLength: 100
- required:
- - id
- - name
- TopicSelection:
- type: object
- properties:
- topic_ids:
- type: array
- items:
- type: integer
- required:
- - topic_ids
- TopicSelectionRequest:
- type: object
- properties:
- topic_ids:
- type: array
- items:
- type: integer
- required:
- - topic_ids
- VerifyOTP:
- type: object
- properties:
- email:
- type: string
- format: email
- otp:
- type: string
- maxLength: 6
- required:
- - email
- - otp
- VerifyOTPRequest:
- type: object
- properties:
- email:
- type: string
- format: email
- minLength: 1
- otp:
- type: string
- minLength: 1
- maxLength: 6
- required:
- - email
- - otp
- securitySchemes:
- jwtAuth:
- type: http
- scheme: bearer
- bearerFormat: JWT
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a605c86..2043bb7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -46,6 +46,76 @@ pod install
npm test
```
+## Backend setup (Docker)
+
+The backend services run via Docker Compose. This includes the Django API, PostgreSQL, Redis, and MinIO (S3-compatible storage).
+
+### Prerequisites
+- Docker and Docker Compose installed
+
+### Quick start
+
+1. Start all services:
+
+```bash
+docker compose up -d
+```
+
+2. Check logs:
+
+```bash
+docker compose logs -f web
+```
+
+3. Stop services:
+
+```bash
+docker compose down
+```
+
+### Available services
+
+| Service | URL | Description |
+|---------------|------------------------------|--------------------------------|
+| Backend API | http://localhost:8000 | Django REST API |
+| pgAdmin | http://localhost:5050 | PostgreSQL admin UI |
+| MinIO Console | http://localhost:9001 | S3-compatible storage UI |
+| RedisInsight | http://localhost:5540 | Redis management UI |
+
+### Environment variables
+
+Key environment variables in `docker-compose.yml`:
+
+| Variable | Default | Description |
+|---------------------------|---------|------------------------------------------|
+| `DEBUG` | `True` | Enable Django debug mode |
+| `OTP_VERIFICATION_ENABLED`| `False` | Set to `True` to require email OTP |
+| `EMAIL_HOST_USER` | - | Gmail address for sending OTP emails |
+| `EMAIL_HOST_PASSWORD` | - | Gmail app password (not regular password)|
+
+### Pull latest backend image
+
+```bash
+docker compose pull web
+docker compose up -d --force-recreate web
+```
+
+### Create a admin user
+
+```bash
+docker exec -it bizbuch_backend python manage.py createsuperuser
+```
+
+### Database management
+
+Access PostgreSQL via pgAdmin at http://localhost:5050 (credentials: `admin@admin.com` / `admin`).
+
+Or via command line:
+
+```bash
+docker exec -it bizbuch-db-1 psql -U postgres -d bizbuch
+```
+
## Code style
- Project uses `eslint` and `prettier`. Run `npm run lint` before opening a PR.
- Keep changes focused and include tests for new behavior when feasible.
diff --git a/README.md b/README.md
index b0ebbcd..559f491 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ Unlike traditional social platforms where a single company owns the servers, dat
---
-
+
## ✨ Key Idea
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..582cb01
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,169 @@
+services:
+ web:
+ image: ujjwalkar/bizbuch-backend:latest
+ container_name: bizbuch_backend
+ restart: always
+ command: >
+ sh -c "
+ python manage.py migrate &&
+ python manage.py collectstatic --noinput &&
+ daphne -b 0.0.0.0 -p 8000 --access-log - --verbosity 1 mysite.asgi:application
+ "
+ ports:
+ - "8000:8000" # ✅ only required exposed port
+ environment:
+ - DEBUG=True
+ - OTP_VERIFICATION_ENABLED=False # Set to True to enable OTP verification
+ - EMAIL_HOST_USER=your_email@example.com # Your email here
+ - EMAIL_HOST_PASSWORD=your_email_password_here # Create app password from here https://myaccount.google.com/apppasswords
+
+ # Postgres
+ - DB_HOST=db
+ - DB_PORT=5432
+ - POSTGRES_DB=bizbuch
+ - POSTGRES_USER=postgres
+ - POSTGRES_PASSWORD=postgres
+
+ # Redis
+ - REDIS_HOST=redis
+
+ # S3 (MinIO)
+ - AWS_ACCESS_KEY_ID=bizbuch_admin
+ - AWS_SECRET_ACCESS_KEY=bizbuch_secret_key
+ - AWS_S3_ENDPOINT_URL=http://minio:9000
+ - AWS_S3_BUCKET=bizbuch
+ - AWS_DEFAULT_REGION=us-east-1
+ - AWS_S3_USE_SSL=False
+
+ depends_on:
+ db:
+ condition: service_healthy
+ redis:
+ condition: service_started
+ minio:
+ condition: service_started
+ minio_init:
+ condition: service_completed_successfully
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.10
+
+ db:
+ image: postgres:15
+ restart: always
+ environment:
+ POSTGRES_DB: bizbuch
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ # Optional: expose Postgres port only if needed for external access
+ ports:
+ - "5432:5432"
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres -d bizbuch"]
+ interval: 5s
+ timeout: 3s
+ retries: 20
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.11
+
+ redis:
+ image: redis:7-alpine
+ restart: always
+ # Optional: expose Redis port only if needed for external access
+ # ports:
+ # - "6379:6379"
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.12
+
+ redisinsight:
+ image: redis/redisinsight:latest
+ container_name: redisinsight
+ restart: always
+ ports:
+ - "5540:5540"
+ depends_on:
+ - redis
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.17
+
+ minio:
+ image: minio/minio:latest
+ container_name: minio
+ restart: always
+ environment:
+ MINIO_ROOT_USER: bizbuch_admin
+ MINIO_ROOT_PASSWORD: bizbuch_secret_key
+ command: server /data --console-address ":9001"
+ volumes:
+ - minio_data:/data
+
+ # Optional: expose MinIO ports only if needed for UI/debugging
+ ports:
+ - "9000:9000"
+ - "9001:9001"
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.13
+
+ minio_init:
+ image: minio/mc:latest
+ depends_on:
+ - minio
+ restart: "no"
+ entrypoint: >
+ /bin/sh -c "
+ until (/usr/bin/mc alias set myminio http://minio:9000 bizbuch_admin bizbuch_secret_key) do sleep 2; done;
+ /usr/bin/mc mb -p myminio/bizbuch || true;
+ /usr/bin/mc anonymous set download myminio/bizbuch || true;
+ exit 0;
+ "
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.14
+
+ pgadmin:
+ image: dpage/pgadmin4:latest
+ container_name: pgadmin
+ restart: always
+ environment:
+ PGADMIN_DEFAULT_EMAIL: admin@admin.com
+ PGADMIN_DEFAULT_PASSWORD: admin
+ PGADMIN_CONFIG_SERVER_MODE: "False"
+ PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
+ ports:
+ - "5050:80"
+ volumes:
+ - ./pgadmin/servers.json:/pgadmin4/servers.json:ro
+ depends_on:
+ - db
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.16
+
+ # Uncomment the following section to enable Cloudflare Tunnel
+ tunnel:
+ image: cloudflare/cloudflared:latest
+ command: tunnel --no-autoupdate --url http://web:8000
+ restart: always
+ depends_on:
+ - web
+ networks:
+ bizbuch_network:
+ ipv4_address: 172.28.0.15
+
+volumes:
+ postgres_data:
+ minio_data:
+
+networks:
+ bizbuch_network:
+ driver: bridge
+ ipam:
+ config:
+ - subnet: 172.28.0.0/16
+ gateway: 172.28.0.1
diff --git a/package-lock.json b/package-lock.json
index 08aaefb..bec599f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10498,9 +10498,9 @@
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
- "version": "11.2.4",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
- "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
+ "version": "11.2.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
+ "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
diff --git a/src/assets/screenshots/Screenshot_4.png b/src/assets/screenshots/Screenshot_4.png
new file mode 100644
index 0000000..7e78c39
Binary files /dev/null and b/src/assets/screenshots/Screenshot_4.png differ
diff --git a/src/assets/screenshots/Screenshot_5.png b/src/assets/screenshots/Screenshot_5.png
new file mode 100644
index 0000000..c1dcd5b
Binary files /dev/null and b/src/assets/screenshots/Screenshot_5.png differ
diff --git a/src/assets/screenshots/Screenshot_6.png b/src/assets/screenshots/Screenshot_6.png
new file mode 100644
index 0000000..ec32112
Binary files /dev/null and b/src/assets/screenshots/Screenshot_6.png differ
diff --git a/src/assets/screenshots/Screenshot_7.png b/src/assets/screenshots/Screenshot_7.png
new file mode 100644
index 0000000..049141e
Binary files /dev/null and b/src/assets/screenshots/Screenshot_7.png differ
diff --git a/src/assets/screenshots/Screenshot_8.png b/src/assets/screenshots/Screenshot_8.png
new file mode 100644
index 0000000..1454de1
Binary files /dev/null and b/src/assets/screenshots/Screenshot_8.png differ
diff --git a/src/data/repositories/ProfileRepository.ts b/src/data/repositories/ProfileRepository.ts
index f1bb6f4..fe607a9 100644
--- a/src/data/repositories/ProfileRepository.ts
+++ b/src/data/repositories/ProfileRepository.ts
@@ -21,7 +21,7 @@ export class ProfileRepository implements IProfileRepository {
}
async addWorkExperience(data: AddWorkExperienceData): Promise {
- await postAuth("profiles/me/work-experiences/", data);
+ await postAuth("profiles/me/work-experience/", data);
}
async addEducation(data: AddEducationData): Promise {
diff --git a/src/presentation/components/templates/BottomNavigationTemplate.tsx b/src/presentation/components/templates/BottomNavigationTemplate.tsx
index 60ee32b..f94d5af 100644
--- a/src/presentation/components/templates/BottomNavigationTemplate.tsx
+++ b/src/presentation/components/templates/BottomNavigationTemplate.tsx
@@ -1,6 +1,7 @@
import React, { useMemo, useCallback } from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { getFocusedRouteNameFromRoute, RouteProp } from '@react-navigation/native';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { TabIcon } from '../atoms/TabIcon';
import { TAB_ROUTES } from '../../navigation/TabRoutes';
import { theme } from '../../theme';
@@ -25,18 +26,26 @@ const HIDDEN_TAB_BAR_SCREENS = [
* Tab bar style configuration
* Single Responsibility: Compute tab bar visibility and styling
*/
-const getTabBarStyle = (route: RouteProp>) => {
+const getTabBarStyle = (
+ route: RouteProp>,
+ bottomInset: number
+) => {
const routeName = getFocusedRouteNameFromRoute(route);
if (routeName && HIDDEN_TAB_BAR_SCREENS.includes(routeName as typeof HIDDEN_TAB_BAR_SCREENS[number])) {
return { display: 'none' as const };
}
+ // Use safe area bottom inset to ensure tab bar is visible above home indicator
+ // Minimum padding ensures proper spacing even on devices without notches
+ const safeBottomPadding = Math.max(bottomInset, bottomNavigation.paddingBottom);
+
return {
backgroundColor: theme.colors.white,
borderTopColor: theme.colors.border,
paddingTop: bottomNavigation.paddingTop,
- paddingBottom: bottomNavigation.paddingBottom,
+ paddingBottom: safeBottomPadding,
+ height: 56 + safeBottomPadding, // Fixed height for consistency
};
};
@@ -48,6 +57,8 @@ const getTabBarStyle = (route: RouteProp>) =>
* SOLID: Dependency Inversion - Uses TabIcon atom abstraction
*/
export const BottomNavigationTemplate: React.FC = () => {
+ const insets = useSafeAreaInsets();
+
const getTabBarIcon = useCallback(
(tabKey: string) =>
({ focused, color }: { focused: boolean; color: string }) => {
@@ -67,14 +78,14 @@ export const BottomNavigationTemplate: React.FC = () => {
headerShown: false,
tabBarActiveTintColor: theme.colors.primary,
tabBarInactiveTintColor: theme.colors.gray600,
- tabBarStyle: getTabBarStyle(route),
+ tabBarStyle: getTabBarStyle(route, insets.bottom),
tabBarLabelStyle: {
fontSize: bottomNavigation.labelFontSize,
fontWeight: bottomNavigation.labelFontWeight,
},
tabBarIcon: getTabBarIcon(route.name),
}),
- [getTabBarIcon],
+ [getTabBarIcon, insets.bottom],
);
return (