Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,84 @@
# QR to 3D AR Hybrid Visualization System

Scan a QR code → view the linked product as an interactive 3D model in your browser (WebAR).

## Architecture

```
Browser (WebAR) → Express API (Render) → PostgreSQL (Supabase)
QR code scan
```

## Quick Start (Docker)

```bash
git clone https://github.com/Vishnumgit/analysis-feedback-repo.git
cd analysis-feedback-repo
cp backend/.env.example backend/.env # fill in values
docker compose up --build
```

| Service | URL |
|----------|-----------------------------------|
| API | http://localhost:3000/api/health |
| Frontend | http://localhost:5173 |
| Database | localhost:5432 |

## Deployment

See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) for step-by-step instructions to deploy on:

- **Database** – Supabase (free tier)
- **Backend API** – Render (free tier)
- **Frontend** – Vercel (free tier)

## API Reference

See [docs/API.md](docs/API.md) for all endpoints.

Key endpoints:

| Method | Path | Description |
|--------|--------------------------------|-------------------------|
| GET | /api/health | Server health check |
| GET | /api/products | List products |
| GET | /api/products/:id | Get product by ID |
| GET | /api/qr/:qrCode | Look up QR code |
| POST | /api/qr/:qrCode/scan | Record a scan event |
| POST | /api/analytics/session | Log an AR session |

## Project Structure

```
├── backend/ Express.js API
│ ├── src/
│ │ ├── server.js Entry point
│ │ ├── config/database.js
│ │ └── routes/
│ │ ├── qr.js
│ │ ├── products.js
│ │ └── analytics.js
│ ├── Dockerfile
│ └── package.json
├── web/ WebAR frontend
│ ├── index.html
│ ├── main.js Three.js viewer + API helpers
│ └── package.json
├── database/
│ ├── 001_init_schema.sql Table definitions
│ └── seed_data.sql Sample products & QR codes
├── docs/
│ ├── API.md
│ ├── DEPLOYMENT.md
│ └── SETUP.md
└── docker-compose.yml
```

## Local Development

See [docs/SETUP.md](docs/SETUP.md) for detailed instructions.

## License

MIT
69 changes: 69 additions & 0 deletions backend/src/routes/analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict';

const express = require('express');
const { body, validationResult } = require('express-validator');
const pool = require('../config/database');

const router = express.Router();

const validate = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
};

/**
* POST /api/analytics/session
* Log an AR session event.
*
* Body:
* product_id {number} required
* platform {string} optional e.g. "web" | "mobile"
* duration {number} optional seconds spent in AR
* user_agent {string} optional
* latitude {number} optional
* longitude {number} optional
*/
router.post(
'/session',
[
body('product_id').isInt({ min: 1 }).toInt(),
body('platform').optional().isString().trim().isLength({ max: 50 }),
body('duration').optional().isFloat({ min: 0 }).toFloat(),
body('user_agent').optional().isString().trim().isLength({ max: 500 }),
body('latitude').optional().isFloat({ min: -90, max: 90 }).toFloat(),
body('longitude').optional().isFloat({ min: -180, max: 180 }).toFloat(),
],
validate,
async (req, res) => {
try {
const { product_id, platform, duration, user_agent, latitude, longitude } = req.body;

const result = await pool.query(
`INSERT INTO ar_sessions (product_id, user_agent, latitude, longitude, duration_sec, platform)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING session_id, product_id, platform, created_at`,
[
product_id,
user_agent || null,
latitude || null,
longitude || null,
duration != null ? Math.round(duration) : null,
platform || null,
]
);

res.status(201).json({
success: true,
data: result.rows[0],
});
} catch (err) {
console.error('POST /api/analytics/session error:', err.message);
res.status(500).json({ error: 'Internal server error' });
}
}
);

module.exports = router;
6 changes: 4 additions & 2 deletions backend/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ const helmet = require('helmet');
const morgan = require('morgan');
const rateLimit = require('express-rate-limit');

const qrRoutes = require('./routes/qr');
const productRoutes = require('./routes/products');
const qrRoutes = require('./routes/qr');
const productRoutes = require('./routes/products');
const analyticsRoutes = require('./routes/analytics');

const app = express();

Expand Down Expand Up @@ -71,6 +72,7 @@ app.get('/api/health', (_req, res) => {

app.use('/api/qr', qrRoutes);
app.use('/api/products', productRoutes);
app.use('/api/analytics', analyticsRoutes);

// ── 404 handler ───────────────────────────────────────────────
app.use((_req, res) => {
Expand Down
1 change: 1 addition & 0 deletions database/001_init_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ CREATE TABLE IF NOT EXISTS ar_sessions (
latitude FLOAT,
longitude FLOAT,
duration_sec INT, -- seconds spent in AR
platform VARCHAR(50), -- e.g. 'web' | 'mobile'
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Expand Down
42 changes: 42 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,48 @@ Create a new product entry (admin use).

---

## Analytics

### `POST /api/analytics/session`

Log an AR session event (called automatically by the WebAR viewer).

**Request body**
```json
{
"product_id": 1,
"platform": "web",
"duration": 45,
"user_agent": "Mozilla/5.0…",
"latitude": 37.7749,
"longitude": -122.4194
}
```

| Field | Type | Required | Description |
|--------------|---------|----------|------------------------------------|
| `product_id` | integer | ✅ | ID of the viewed product |
| `platform` | string | – | `web` or `mobile` |
| `duration` | number | – | Seconds spent in AR |
| `user_agent` | string | – | Browser user-agent string |
| `latitude` | number | – | GPS latitude (–90 to 90) |
| `longitude` | number | – | GPS longitude (–180 to 180) |

**Response 201**
```json
{
"success": true,
"data": {
"session_id": 1,
"product_id": 1,
"platform": "web",
"created_at": "2026-03-17T12:00:00.000Z"
}
}
```

---

## Error Codes

| Code | Meaning |
Expand Down
Loading