Important
This documentation is a bit outdated. It would be highly appreciated if you put the time to rewrite this based on the current project structure!
A fun web project that lets users "pray" (send salavats) for schools to be canceled! This is a full-stack application with a modern Vue 3 frontend, a Hono API backend, and a PostgreSQL database layer.
The project follows a three-tier architecture:
┌─────────────┐
│ client-web │ Vue 3 + Vite Frontend
└──────┬──────┘
│ HTTP
▼
┌─────────────┐
│ server │ Hono API Backend (TypeScript)
└──────┬──────┘
│ HTTP (with Auth)
▼
┌─────────────┐
│ dal │ Data Access Layer (Node.js)
└──────┬──────┘
│
▼
┌─────────────┐
│ PostgreSQL │ Database
└─────────────┘
- client-web: Vue 3 frontend with TailwindCSS, reactive state management, and optimistic updates
- server: Hono API backend with rate limiting, caching, and admin endpoints
- dal: Data Access Layer that interfaces with PostgreSQL, handles database operations
- Global and daily counters for prayers (salavats)
- Large circular button to send a salavat
- Optimistic updates on click with periodic server sync (every 2 seconds)
- Rate limiting (40 requests per minute per IP)
- Animated counters using CountUp.js
- Dynamic configuration system
- UI in Persian (Farsi)
- Responsive and mobile-friendly design
- Admin endpoints for managing clicks and configurations
- Docker support for all components
- Node.js 18+
- npm
- PostgreSQL database
- Docker (optional, for containerized deployment)
git clone https://github.com/amirparsadd/salavat-madrese
cd salavat-madreseNavigate to the dal directory:
cd dalCreate a .env file:
DATABASE_CONNECTION_STRING=postgresql://user:password@host:port/database
ACCESS_TOKEN=your-secure-access-token-here
PORT=3000
HOSTNAME=0.0.0.0Install dependencies and start the DAL:
npm install
npm startThe DAL will automatically initialize the database schema (creates clicks and configs tables) on first run.
Navigate to the server directory:
cd ../serverCreate a .env file:
DAL_ENDPOINT=http://localhost:3000
DAL_ACCESS_TOKEN=your-secure-access-token-here
PORT=3000Install dependencies:
npm installRun in development mode:
npm run devOr build and run in production:
npm run build
npm startThe server will run at http://localhost:3000 by default.
Navigate to the client-web directory:
cd ../client-webCreate a .env file:
VITE_API_ENDPOINT=http://localhost:3000Install dependencies:
npm installRun in development mode:
npm run devThe app will run at http://localhost:5173 by default.
Build for production:
npm run build
npm run previewEach component has its own Dockerfile. You can build and run them individually:
# Build DAL
cd dal
docker build -t salavat-dal .
# Build Server
cd ../server
docker build -t salavat-server .
# Build Client
cd ../client-web
docker build -t salavat-client .# Run DAL (make sure PostgreSQL is accessible)
docker run -p 3000:3000 \
-e DATABASE_CONNECTION_STRING=postgresql://... \
-e ACCESS_TOKEN=your-token \
salavat-dal
# Run Server
docker run -p 3000:3000 \
-e DAL_ENDPOINT=http://dal-container:3000 \
-e DAL_ACCESS_TOKEN=your-token \
salavat-server
# Run Client
docker run -p 80:80 salavat-clientFetch current click data (total and daily counts).
Response:
{
"total": 12345,
"daily": {
"amount": 567,
"lastUpdate": 1234567890
}
}Increment the click counter. Rate limited to 40 requests per minute per IP.
Response:
Submitted (201)
Fetch all configuration values.
Response:
{
"support": "https://t.me/amirparsab90",
"servicestatus": "https://uptimekuma.afrachin.ir/status/salavat-madrese"
}Fetch a specific configuration value.
Response:
{
"data": "config-value"
}All admin endpoints require the Authorization header with the DAL_ACCESS_TOKEN value.
Set or update a configuration value.
Headers:
Authorization: your-dal-access-token
Body:
{
"value": "new-config-value"
}Manually add clicks (admin only).
Headers:
Authorization: your-dal-access-token
Body:
{
"amount": 100
}salavat-madrese/
├── client-web/ # Vue 3 Frontend
│ ├── src/
│ │ ├── App.vue # Main UI component
│ │ ├── main.ts # Entry point
│ │ ├── component/
│ │ │ └── Counter.vue # Animated counter component
│ │ ├── state/
│ │ │ ├── clicks.ts # Click state management
│ │ │ ├── configs.ts # Config state management
│ │ │ └── interval.ts # Sync interval management
│ │ └── style.css # Global styles
│ ├── public/ # Static assets
│ ├── Dockerfile # Multi-stage build with nginx
│ └── package.json
│
├── server/ # Hono API Backend
│ ├── src/
│ │ ├── index.ts # Main server file
│ │ ├── clicks.ts # Click data management
│ │ ├── configs.ts # Config caching and management
│ │ ├── admin-middleware.ts # Admin authentication
│ │ └── utils.ts # Utility functions
│ ├── Dockerfile
│ └── package.json
│
└── dal/ # Data Access Layer
├── src/
│ ├── index.js # HTTP server
│ ├── handler.js # Request handler
│ ├── db.js # Database operations
│ └── config.js # Configuration loader
├── start-database.sh # Database initialization script
├── Dockerfile
└── package.json
- Start the DAL service (connects to PostgreSQL)
- Start the backend server (connects to DAL)
- Start the frontend (connects to backend)
- Open the website in your browser
- See the total and daily prayer counts
- Click the 🙏 button to send a salavat
- Counters update instantly locally and sync with the server every 2 seconds
DATABASE_CONNECTION_STRING: PostgreSQL connection stringACCESS_TOKEN: Token for authenticating requests from serverPORT: Port to run on (default: 3000)HOSTNAME: Hostname to bind to (default: 0.0.0.0)
DAL_ENDPOINT: URL of the DAL serviceDAL_ACCESS_TOKEN: Token to authenticate with DALPORT: Port to run on (default: 3000)
VITE_API_ENDPOINT: URL of the backend server
The app supports dynamic configuration through the /configs endpoints. Default configs include:
support: Support contact linkservicestatus: Service status page URL
These can be updated via the admin API without redeploying.
- Framework: Vue 3 with Composition API
- Build Tool: Vite
- Styling: TailwindCSS v4
- Animations: CountUp.js for counter animations
- State Management: Vue reactive state
- Sync Interval: 2 seconds for fetching updated counts
- Framework: Hono
- Language: TypeScript
- Rate Limiting: 40 requests/minute per IP using
hono-rate-limiter - Caching: In-memory cache for configs (cleared every 5 seconds)
- Sync: Periodic sync with DAL (every 1 second by default)
- CORS: Enabled for cross-origin requests
- Database: PostgreSQL
- Tables:
clicks: Stores total count and daily trackingconfigs: Key-value configuration storage
- Features: Automatic schema initialization, daily counter reset logic
- Start PostgreSQL database
- Start DAL:
cd dal && npm start - Start Server:
cd server && npm run dev(usestsx watchfor hot reload) - Start Client:
cd client-web && npm run dev(uses Vite dev server)
# Build server
cd server
npm run build
# Build client
cd client-web
npm run buildMIT License