@ma7moudsalama/falak-app
v1.0.2
Published
Production-ready Vue 3 boilerplate with Firebase, PrimeVue, Tailwind, i18n, Groq AI, Paymob & more
Maintainers
Readme
فَلَك · Falak App
Production-ready Vue 3 boilerplate with Firebase, PrimeVue, Tailwind CSS, i18n (Arabic + English), Groq AI, Paymob payments, offline IndexedDB sync, and AES encryption — all wired up and ready to build on.
Quick Start
# Scaffold a new project
npx @ma7moudsalama/falak-app create my-project
# Or install globally
npm i -g @ma7moudsalama/falak-app
falak create my-project
# Then
cd my-project
cp .env.example .env # fill in your keys
npm run devWhat's Inside
| Layer | Package | Status |
|---|---|---|
| Framework | Vue 3 + Vite | ✅ Configured |
| UI Library | PrimeVue 4 (Aura theme) | ✅ Configured |
| Styling | Tailwind CSS 3 | ✅ Configured |
| State | Pinia | ✅ Configured |
| Routing | Vue Router 4 | ✅ Configured + guards |
| Translation | Vue I18n 10 (EN + AR/RTL) | ✅ Configured |
| Auth | Firebase Auth (Email, Google, Facebook) | ✅ Composable ready |
| Database | Firebase Realtime DB | ✅ Composable ready |
| Offline | IndexedDB via idb | ✅ Auto-sync |
| Encryption | AES-256 via crypto-js | ✅ Composable ready |
| AI | Groq SDK (LLaMA 3.3) | ✅ Composable ready |
| Payments | Paymob | ✅ Composable + subscriptions |
Project Structure
src/
├── assets/
│ └── main.css ← Tailwind + custom utilities
├── components/
│ └── layout/
│ └── AppLayout.vue ← Sidebar + topbar layout shell
├── composables/
│ ├── useAuth.js ← Firebase auth + roles + sessions
│ ├── useDatabase.js ← RTDB + IndexedDB offline manager
│ ├── useCrypto.js ← AES encrypt/decrypt
│ ├── useGroq.js ← Groq AI chat + streaming
│ └── usePaymob.js ← Paymob payments + subscriptions
├── firebase/
│ └── index.js ← Firebase app init + all services
├── i18n/
│ ├── index.js ← Vue I18n setup + locale switcher
│ └── locales/
│ ├── en.json
│ └── ar.json
├── router/
│ └── index.js ← Routes + auth/role guards
├── stores/
│ └── auth.js ← Pinia wrapper for useAuth
├── views/
│ ├── auth/
│ │ ├── LoginView.vue
│ │ ├── RegisterView.vue
│ │ └── ForgotView.vue
│ ├── HomeView.vue
│ ├── DashboardView.vue ← Live data + AI + subscription demo
│ ├── ProfileView.vue
│ ├── AdminView.vue
│ └── NotFoundView.vue
├── App.vue
└── main.jsConfiguration
1. Firebase
Edit src/firebase/index.js or set your .env:
VITE_FIREBASE_API_KEY=...
VITE_FIREBASE_AUTH_DOMAIN=...
VITE_FIREBASE_DATABASE_URL=...
VITE_FIREBASE_PROJECT_ID=...
VITE_FIREBASE_STORAGE_BUCKET=...
VITE_FIREBASE_MESSAGING_SENDER_ID=...
VITE_FIREBASE_APP_ID=...Enable in Firebase Console:
- Authentication → Email/Password, Google, Facebook
- Realtime Database → create database
- Upload
firebase-rules.jsonas your security rules
2. Groq AI
VITE_GROQ_API_KEY=gsk_...
VITE_GROQ_MODEL=llama-3.3-70b-versatileGet your key at console.groq.com.
3. Paymob
VITE_PAYMOB_API_KEY=...
VITE_PAYMOB_INTEGRATION_ID=...
VITE_PAYMOB_IFRAME_ID=...
VITE_PAYMOB_HMAC_SECRET=...Get credentials from accept.paymob.com.
4. Encryption
VITE_ENCRYPTION_KEY=your_strong_random_secretComposables API
useAuth()
import { useAuth } from '@/composables/useAuth.js'
const {
currentUser, // Ref<FirebaseUser | null>
userProfile, // Ref<Object> — RTDB profile
userRole, // Ref<string> — live from RTDB
isAuthenticated, // ComputedRef<bool>
isAdmin, // ComputedRef<bool>
isSuperAdmin, // ComputedRef<bool>
isAuthReady, // Ref<bool> — true after first auth check
isLoading, // Ref<bool>
authError, // Ref<string | null>
register, // ({ email, password, displayName }) → { success, user }
login, // ({ email, password }) → { success, user }
logout, // → { success }
resetPassword, // (email) → { success }
changePassword, // ({ currentPassword, newPassword }) → { success }
loginWithGoogle, // (useRedirect?) → { success, user }
loginWithFacebook,// (useRedirect?) → { success, user }
updateUserProfile,// (data) → { success }
setUserRole, // (uid, role) → { success } ← admin only
deleteAccount, // → { success }
hasPermission, // (permission) → bool
hasRole, // (...roles) → bool
ROLES // { SUPER_ADMIN, ADMIN, USER, GUEST }
} = useAuth()User roles are stored in /users/{uid}/role in RTDB and listened to in real-time.
Session data (online, lastSeen) is written to /sessions/{uid} with onDisconnect handling.
useDatabase(options?)
import { useDatabase } from '@/composables/useDatabase.js'
const db = useDatabase({
encryptedPaths: ['payments/*'], // paths to auto-encrypt
encryptFields: ['ssn', 'card'] // field names to encrypt
})
// One-time read (offline-aware)
const user = await db.get('users/uid123')
// Write (blocked offline with error)
await db.set('todos/1', { text: 'Buy milk', done: false })
await db.update('todos/1', { done: true })
await db.push('todos', { text: 'New item' }) // → { success, key }
await db.remove('todos/1')
// Real-time listener (reactive ref + IDB sync)
const { data, unsubscribe } = db.listen('todos')
// data.value updates live
// Real-time child events (efficient for large lists)
const { items, unsubscribe } = db.listenList('messages')
// items.value = { key1: {...}, key2: {...} }
// Offline-only reads
const cached = await db.getOffline('todos')
const all = await db.getAllOffline('todos')
// Network status
db.isOnline // Ref<bool>Offline behavior:
- All write operations return
{ success: false, error: 'You are offline. Data is read-only.' }when offline. - Read operations return IndexedDB cached data.
- Remote changes are auto-synced to IDB when online.
useCrypto()
import { useCrypto } from '@/composables/useCrypto.js'
const { encrypt, decrypt, encryptObject, decryptObject,
encryptFields, decryptFields, hash, hmac, randomToken } = useCrypto()
const cipher = encrypt('sensitive data')
const plain = decrypt(cipher)
const encObj = encryptObject({ ssn: '123-45-6789', name: 'Max' })
const obj = decryptObject(encObj)
// Encrypt only specific fields
const safe = encryptFields(userData, ['ssn', 'bankAccount'])
const back = decryptFields(safe, ['ssn', 'bankAccount'])
// SHA256 hash
const hashed = hash('mypassword')
// HMAC (used by Paymob webhook verification)
const sig = hmac('message', 'secret')useGroq(systemPrompt?)
import { useGroq } from '@/composables/useGroq.js'
const groq = useGroq('You are a helpful assistant.')
// Single prompt
const reply = await groq.chat('What is Vue.js?')
// Streaming
await groq.streamChat('Tell me a story', (chunk, full) => {
output.value = full
})
// Multi-turn conversation
await groq.sendConversation('Hello!')
await groq.sendConversation('What did I just say?')
// Streaming multi-turn
await groq.streamConversation('Continue...', (chunk) => { ... })
// Audio transcription (Whisper)
const text = await groq.transcribe(audioBlob, 'ar')
groq.clearConversation()
groq.setSystemPrompt('New system prompt')usePaymob() + useSubscription()
import { usePaymob, useSubscription, PLANS } from '@/composables/usePaymob.js'
// ── Payments ──
const paymob = usePaymob()
// Full payment flow → opens iframe URL
const result = await paymob.initPayment({
amountCents: 14900, // 149.00 EGP
currency: 'EGP',
billingData: { ... }
})
// result.iframeUrl → embed or open
// Open in new tab
await paymob.openPaymentTab({ amountCents: 14900, ... })
// Verify Paymob webhook HMAC
const valid = paymob.verifyHmac(callbackData)
// ── Subscriptions ──
const sub = useSubscription()
// Initialize free plan for new users
await sub.initFreeplan(userId)
// Activate after successful payment
await sub.activateSubscription(userId, 'PRO', { paymobOrderId: 12345, amountCents: 14900 })
// Real-time subscription listener
const { data } = sub.listenSubscription(userId)
// data.value = { planId, status, expiresAt, features, ... }
// Feature gating
const canUseAPI = await sub.hasFeature(userId, 'api_access')
const active = await sub.isActive(userId)
await sub.cancelSubscription(userId)Subscription data in RTDB (/subscriptions/{uid}):
{
"planId": "pro",
"planName": "Pro",
"status": "active",
"features": ["read", "write", "advanced_features", "api_access"],
"startedAt": 1700000000000,
"expiresAt": 1702592000000,
"autoRenew": true,
"paymentHistory": [{ "paidAt": 1700000000000, "amountCents": 14900 }]
}i18n
import { setLocale } from '@/i18n/index.js'
setLocale('ar') // switches to Arabic + sets dir="rtl"
setLocale('en') // switches to English + sets dir="ltr"In templates:
<p>{{ $t('auth.email') }}</p>
<p>{{ $d(date, 'short') }}</p>
<p>{{ $n(149, 'egp') }}</p>To add a new language, add its locale file to src/i18n/locales/ and register it in src/i18n/index.js.
Security Rules
Upload firebase-rules.json to your Firebase Realtime Database rules:
Firebase Console → Realtime Database → Rules → paste contentsScripts
npm run dev # Start dev server (http://localhost:5173)
npm run build # Production build → dist/
npm run preview # Preview production build
npm run lint # Lint + auto-fixLicense
MIT © Falak App
