Skip to content

albercuba/PimpMyQR

Repository files navigation

Pimp my QR

Pimp my QR is a self-hosted QR-code generator and management console. It is intentionally built like a technical toolbox/admin app rather than a marketing SaaS: compact navigation, dark mode, dashboard shortcuts, detailed project views, artifact downloads, health checks, and scan analytics.

It does not copy QR.io branding, wording, pricing, assets, or UI.

Features

  • Static QR codes for URL, text, email, phone, SMS, Wi-Fi, vCard, WhatsApp, calendar event, location, app links, and landing pages.
  • Dynamic QR codes with /r/:slug redirects, editable destinations, enabled/disabled state, optional expiration, fallback URL, and scan tracking.
  • QR design controls for module style, eye style, foreground/background color, frame text, quiet zone, error correction, and logo settings.
  • Live preview with scan-readability warnings.
  • Logo/image upload for PNG, JPG, SVG, and WebP assets.
  • Seeded templates for restaurant menus, business cards, events, Wi-Fi, social profiles, packaging, coupons, and app downloads.
  • Generated landing pages for dynamic QR codes.
  • Analytics for scans, device type, browser, OS, and top QR projects.
  • CRUD, duplicate, archive/unarchive, favorite/unfavorite, search, tags, and activity logs.
  • Export artifacts for SVG, PNG, and PDF.
  • System status view for backend, database, storage, and Redis placeholder.

Quick Start

cp .env.example .env
npm install
npm run db:generate
npm run db:migrate
npm run db:seed
npm run dev

Demo login after seeding:

demo@pimpmyqr.local
demo-password

Google Sign-In Setup

Google sign-in is optional. Email/password authentication keeps working even when Google is not configured.

  1. Open Google Cloud Console.
  2. Create or select a Google Cloud project.
  3. Go to APIs & Services -> Credentials.
  4. Select Create Credentials -> OAuth client ID.
  5. Choose Application type: Web application.
  6. Add authorized JavaScript origins:
http://localhost:8082
https://your-production-domain.example
  1. Copy the generated Web Client ID. It looks like:
1234567890-abcdefg.apps.googleusercontent.com
  1. Add it to .env for both backend verification and frontend rendering:
GOOGLE_CLIENT_ID="1234567890-abcdefg.apps.googleusercontent.com"
VITE_GOOGLE_CLIENT_ID="1234567890-abcdefg.apps.googleusercontent.com"

The frontend only receives the public Google client ID. The backend verifies the Google ID token audience against GOOGLE_CLIENT_ID, requires a verified email, and returns the same JWT format as email/password login.

After changing VITE_GOOGLE_CLIENT_ID, rebuild/restart the frontend because Vite embeds VITE_ variables at build time:

docker compose down
docker compose up --build

For local npm development, restart:

npm run dev

Docker:

docker compose up --build

Frontend: http://localhost:8082
Backend: http://localhost:4000
Health: http://localhost:4000/health

In Docker, the frontend proxies /api, /r, /health, /uploaded-assets, and /exports to the backend. Use http://<server>:8082 from your browser; the browser does not need to reach backend port 4000 directly.

The Docker Compose stack uses its own PostgreSQL container, database, and volume:

Container: pimpmyqr-postgres
Database:  pimpqr
User:      pimpqr
Volume:    pimpmyqr-postgres-data
Host port: 5434 -> container port 5432

The backend connects to this database inside Docker with:

postgresql://pimpqr:pimpqr@postgres:5432/pimpqr?schema=public

API Routes

Auth:

  • POST /api/auth/register
  • POST /api/auth/login
  • POST /api/auth/google
  • GET /api/me

QR projects:

  • GET /api/qr
  • POST /api/qr
  • GET /api/qr/:id
  • PATCH /api/qr/:id
  • DELETE /api/qr/:id
  • POST /api/qr/:id/duplicate
  • POST /api/qr/:id/favorite
  • POST /api/qr/:id/archive
  • POST /api/qr/:id/export
  • GET /api/qr/:id/artifacts

Templates, assets, analytics:

  • GET /api/templates
  • POST /api/assets
  • GET /api/assets
  • DELETE /api/assets/:id
  • GET /api/analytics/summary
  • GET /api/analytics/qr/:id

Runtime:

  • GET /r/:slug
  • GET /health

QR Readability Notes

Static QR codes encode content directly. After export or printing, the encoded content cannot be changed.

Dynamic QR codes encode a redirect URL. You can edit the destination later, disable the QR, expire it, and track scans.

Heavy customization can make QR codes harder to scan. Keep strong contrast, preserve a quiet zone, prefer high error correction when using logos, and test printed designs on multiple phones before production.

Privacy

Scan analytics do not store raw full IP addresses. The backend hashes an anonymized IP prefix with a salt from IP_HASH_SALT. User agent, referrer, device type, browser, and OS are stored for operational analytics.

No external tracking services are used.

Validation

npm run build
npm run test
npm run lint

Roadmap

  • Team accounts and shared workspaces.
  • Custom domains for dynamic QR redirects.
  • Bulk QR generation.
  • CSV import/export workflows.
  • White-label landing pages.
  • Advanced analytics with local GeoIP support.
  • Billing/subscriptions and plan limits.
  • S3-compatible object storage adapter.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages