Skip to content

Sumit0ubey/Pinggo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Typing SVG

GitHub Stars License

Live Demo Repo WebSockets

Django Django Channels ASGI PostgreSQL Redis Cloudinary TailwindCSS HTMX Alpine.js

⚡ Real-Time Conversations. Engineered for Scale.

Global • Group • Private messaging — built on Django + WebSockets with a production-ready ASGI architecture.


📌 Table of Contents


✨ Overview

Pinggo is a real-time chat platform designed to deliver smooth, instant messaging across:

  • 🌍 Global chat
  • 👥 Group rooms
  • 🔐 Private 1:1 DMs
  • 🖼 Media sharing (Cloudinary)

It’s built like a deployable product: ASGI-first, Redis channel layer, PostgreSQL storage, and a modern UI stack for responsive interactions.

Live: http://143.244.132.69/


🚀 Why Pinggo?

  • Realtime-first: WebSocket messaging using Django Channels.
  • Scales horizontally: Redis/Upstash for the channel layer fan-out.
  • Production-ready: ASGI + Daphne/Uvicorn compatible deployment.
  • Cloud media: Cloudinary for avatars and attachments.
  • Modern UI: Tailwind + HTMX + Alpine.js for app-like experience.

🧩 Features

Messaging

  • ✅ Real-time messaging via WebSockets
  • ✅ Global / Group / Private rooms
  • ✅ Room-based broadcast delivery
  • ✅ Persistent history in PostgreSQL

Media

  • ✅ User avatars
  • ✅ Group avatars
  • ✅ File uploads / attachments
  • ✅ Cloudinary integration

Infrastructure-ready

  • ✅ ASGI deployment support
  • ✅ Redis channel layer for scale
  • ✅ Clear separation of routes/consumers/services

🏗 System Design

1) High-Level

flowchart TB
  U[Users / Browsers] -->|HTTPS| LB[Load Balancer / Reverse Proxy<br/>Nginx / Cloud LB]
  U -->|WSS WebSocket Secure| LB

  LB --> APP[ASGI App<br/>Django + Channels<br/>Daphne/Uvicorn Workers]

  APP --> DB[(PostgreSQL<br/>Users, Rooms, Messages, Memberships)]
  APP --> REDIS[(Redis / Upstash<br/>Channel Layer + PubSub)]
  APP --> CLOUD[(Cloudinary<br/>Avatars + Files)]
  APP --> EMAIL[Email Provider / API<br/>Verification / system emails]
Loading

2) Low-Level

flowchart LR
  subgraph Client["Client (Browser)"]
    UI[Chat UI<br/>Tailwind/HTMX/Alpine]
    WS[WebSocket Client]
    HTTP[HTTP Client]
    UI --> WS
    UI --> HTTP
  end

  subgraph Server["Server (Django ASGI)"]
    ROUTES[HTTP Routes<br/>Auth, Rooms, Profiles]
    CONSUMERS[Channels Consumers<br/>WebSocket handlers]
    AUTH[Auth Layer<br/>Sessions/JWT/Allauth]
    SVC[Service Layer<br/>RoomService, MessageService]
    POL[Permissions / Policy]
    SERIAL[Serialization / Validation]
  end

  subgraph Storage[Storage & Infra]
    PG[(PostgreSQL)]
    REDIS[(Redis Channel Layer)]
    CLOUD[(Cloudinary)]
  end

  HTTP --> ROUTES
  WS --> CONSUMERS

  ROUTES --> AUTH
  CONSUMERS --> AUTH

  AUTH --> POL
  ROUTES --> SERIAL
  CONSUMERS --> SERIAL

  SERIAL --> SVC
  POL --> SVC

  SVC --> PG
  CONSUMERS --> REDIS
  SVC --> CLOUD
Loading

3) Data Flow

3A) WebSocket Message Send (Room/Group/Global)

sequenceDiagram
  autonumber
  participant C as Client (Browser)
  participant LB as LB / Nginx
  participant A as ASGI App (Django+Channels)
  participant R as Redis (Channel Layer)
  participant P as PostgreSQL

  C->>LB: WSS connect /ws/chat/{room}/
  LB->>A: Upgrade to WebSocket
  A->>R: Join group (room channel group)

  C->>A: Send message payload (WS frame)
  A->>A: Validate + authorize (membership/permissions)
  A->>P: Persist message (insert)
  A->>R: Publish to room group
  R-->>A: Fan-out event to all subscribers
  A-->>C: Broadcast message event (WS)
Loading

3B) File Upload / Media Share (Cloudinary)

sequenceDiagram
  autonumber
  participant C as Client
  participant A as ASGI App
  participant CL as Cloudinary
  participant P as PostgreSQL

  C->>A: Request upload (HTTP) / get upload signature
  A->>A: Check auth + size/type rules
  A-->>C: Return signed upload params (or upload endpoint)
  C->>CL: Upload file (direct to Cloudinary)
  CL-->>C: Return asset URL + public_id
  C->>A: Send message referencing asset URL (WS/HTTP)
  A->>P: Save message with attachment metadata
  A-->>C: Broadcast message with attachment URL
Loading

3C) Private Chat Initiation (1:1)

sequenceDiagram
  autonumber
  participant C as Client
  participant A as ASGI App
  participant P as PostgreSQL
  participant R as Redis

  C->>A: Create/find DM room (HTTP)
  A->>P: Upsert DM room + membership (two users)
  A-->>C: Return room_id
  C->>A: WSS connect /ws/chat/{room_id}/
  A->>R: Join DM group
  C->>A: Send message (WS)
  A->>P: Persist message
  A->>R: Publish to DM group
  A-->>C: Broadcast to both users
Loading

4) Deployment & Scaling

flowchart TB
  U[Users] --> LB[Cloud LB / Nginx]

  LB --> A1[ASGI Instance #1<br/>Daphne/Uvicorn + Django]
  LB --> A2[ASGI Instance #2<br/>Daphne/Uvicorn + Django]
  LB --> A3[ASGI Instance #3<br/>Daphne/Uvicorn + Django]

  A1 --> REDIS[(Redis / Upstash<br/>Channel Layer)]
  A2 --> REDIS
  A3 --> REDIS

  A1 --> PG[(PostgreSQL)]
  A2 --> PG
  A3 --> PG

  A1 --> CLOUD[(Cloudinary)]
  A2 --> CLOUD
  A3 --> CLOUD
Loading

5) Database ER

erDiagram
  %% USERS
  USER ||--|| PROFILE : has_profile

  %% CHAT GROUPS
  USER ||--o{ CHATGROUP : creates
  USER }o--o{ CHATGROUP : member_of

  %% MESSAGES
  CHATGROUP ||--o{ GROUPMESSAGE : has
  USER ||--o{ GROUPMESSAGE : writes

  PROFILE {
    int id
    int user_id
    string image_url
    string displayname
    string info
  }

  CHATGROUP {
    int id
    string group_name
    string chat_type "global|group|private"
    string description
    string image_url
    int creator_id
    datetime created_at
  }

  GROUPMESSAGE {
    int id
    int group_id
    int author_id
    string message
    string file_url
    string file_type "image|video|audio|pdf|other"
    string file_name
    datetime created_at
  }
Loading

🛠 Tech Stack

Backend

  • Django
  • Django Channels (WebSockets)
  • ASGI + Daphne/Uvicorn
  • PostgreSQL
  • Redis / Upstash Redis

Frontend

  • HTML5
  • Tailwind CSS
  • HTMX
  • Alpine.js
  • JavaScript

Media

  • Cloudinary

📦 Project Structure

Pinggo/
│
├── chats/                # Chat related files and configuration
├── home/                 # Simple app for redirection
├── MailApix/             # app for configuring MailAPIX API as an email backed
├── Pinggo/               # Django project core
├── static/               # Static files folder
├── templates/            # Html files folder
├── users/                # app for user authentication and authorization
├── __init__.py
├── manage.py
├── .env
└──  requirements.txt                 

⚙️ Getting Started

1) Clone & install

git clone https://github.com/Sumit0ubey/Pinggo.git
cd Pinggo

python -m venv venv
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate

pip install -r requirements.txt

2) Migrate DB

python manage.py makemigrations
python manage.py migrate

3) Run locally

python manage.py runserver

🌩 Environment Variables

Create .env based on .env.example.

DEBUG=True
SECRET_KEY=your_secret_key
APP_NAME=Pinggo

# Database
DATABASE_HOST=
DATABASE_PORT=
DATABASE_NAME=
DATABASE_USER=

# Redis / Upstash
UPSTASH_REDIS_URL=

# Cloudinary
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=

# Optional folders
CLOUDINARY_USER_AVATAR=
CLOUDINARY_GROUP_AVATAR=
CLOUDINARY_CHAT_FILES=

🐳 Docker Usage

If you want to run Pinggo directly with containers, use the compose setup in Pinggo/docker-compose.yml.

1) Prepare Docker environment

In the Pinggo/ directory, create .env from Pinggo/.envDockerExample, then update all required values before starting containers.

cd Pinggo

# Linux/macOS
cp .envDockerExample .env

# Windows PowerShell
Copy-Item .envDockerExample .env

2) Understand current compose setup

The current Pinggo/docker-compose.yml starts four services:

  • db -> PostgreSQL 15 (persistent data in postgres_data)
  • redis -> Redis 7 (persistent data in redis_data)
  • web -> Pinggo app container (currently uses image dubeysumit/pinggo:v.1.5)
  • nginx -> reverse proxy on port 80

So right now, by default, your app runs from the already published image in compose.

3) Run directly with current compose file

cd Pinggo
docker compose up --build

If your system uses the legacy command, you can use:

docker-compose up --build

4) Build your own image using Dockerfile

Pinggo/Dockerfile is already set up for production-style startup (collectstatic, migrate, then Daphne). To build your own image from this Dockerfile:

cd Pinggo
docker build -t your-dockerhub-username/pinggo:latest .

Optional push to your own registry repo:

docker login
docker push your-dockerhub-username/pinggo:latest

5) Use your repo image in compose (replace current one)

In Pinggo/docker-compose.yml, update services.web.image.

Current:

web:
  image: dubeysumit/pinggo:v.1.5

Replace with your image:

web:
  image: your-dockerhub-username/pinggo:latest

Then run compose again:

cd Pinggo
docker compose up -d

6) Build from Dockerfile directly in compose (alternative)

If you do not want to pull any registry image, you can make compose build locally from Pinggo/Dockerfile.

Example web service:

web:
  build:
    context: .
    dockerfile: Dockerfile
  image: pinggo:local

Then start:

cd Pinggo
docker compose up --build

7) Stop or run in background

cd Pinggo

# Stop containers
docker compose down

# Run in detached mode
docker compose up -d

🚢 Production Deployment

Run ASGI with Daphne

daphne -b 0.0.0.0 -p 8000 Pinggo.asgi:application

Recommended production setup

  • Nginx reverse proxy (TLS termination)
  • PostgreSQL managed DB
  • Upstash Redis (channel layer)
  • Cloudinary for media
  • DEBUG=False and restricted ALLOWED_HOSTS

🧪 Security & Best Practices

  • ✅ Keep secrets in environment variables
  • ✅ Use DEBUG=False in production
  • ✅ Validate message payloads server-side
  • ✅ Add rate limiting (WS + HTTP) for abuse prevention
  • ✅ Restrict allowed file types/sizes before upload
  • ✅ Use HTTPS/WSS everywhere

🗺 Roadmap

  • Read receipts ✅/👀
  • Typing indicators ✍️
  • Reactions ❤️🔥
  • Push notifications 🔔
  • Search 🔎
  • Moderation tools 🛡
  • Optional E2E encryption 🔐

🤝 Contributing

  1. Fork the repo
  2. Create a branch: git checkout -b feature/amazing-feature
  3. Commit changes: git commit -m "Add amazing feature"
  4. Push: git push origin feature/amazing-feature
  5. Open a PR 🎉

👨‍💻 Author

Sumit Dubey
GitHub: https://github.com/Sumit0ubey


📄 License

This project is licensed under the MIT License.

See the LICENSE file for details.


💬 Pinggo — Built for Conversations that Matter