Skip to content

StuartHely/Realcasuallease

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

437 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CasualLease

A casual leasing platform for shopping centres. Enables tenants to browse, book, and manage temporary retail spaces (sites/kiosks) within shopping centres.

Quick Start

# Install dependencies
pnpm install

# Run development server
pnpm dev

# Build for production
pnpm build

# Start production server
pnpm start

# Type check
pnpm run check

# Run tests
pnpm test

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                         Client (React)                          │
│  - Vite + React 19 + TypeScript                                 │
│  - TailwindCSS + Radix UI components                            │
│  - tRPC client for type-safe API calls                          │
│  - Google Maps (overview map, autocomplete) + MapLibre GL       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                      Server (Express + tRPC)                    │
│  - Express.js with tRPC router                                  │
│  - Drizzle ORM for database operations                          │
│  - Dual auth: JWT password-based (primary) + OAuth fallback     │
│  - Forge API proxy for storage, AWS SDK for other services      │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        AWS Services                             │
│  - Storage: Forge API proxy (S3 SDK installed but secondary)    │
│  - Location Service: Geocoding, places, routing                 │
│  - Bedrock: LLM (Claude), Image generation (Stable Diffusion)   │
│  - Transcribe: Voice-to-text                                    │
│  - EKS: Container orchestration                                 │
│  - RDS PostgreSQL: Database                                     │
└─────────────────────────────────────────────────────────────────┘

Project Structure

.
├── client/                 # React frontend
│   └── src/
│       ├── components/     # Reusable UI components
│       ├── pages/          # Route pages
│       ├── hooks/          # Custom React hooks
│       ├── lib/            # Utilities (trpc client, etc.)
│       └── contexts/       # React contexts
│
├── server/                 # Express backend
│   ├── _core/              # Core infrastructure
│   │   ├── index.ts        # Server entry point
│   │   ├── trpc.ts         # tRPC setup + procedure hierarchy
│   │   ├── context.ts      # Dual auth (JWT + OAuth fallback)
│   │   ├── env.ts          # Environment variables
│   │   ├── rateLimit.ts    # IP-based login rate limiter
│   │   ├── authService.ts  # Authentication logic
│   │   ├── amazonLocation.ts # AWS Location (geocoding, directions)
│   │   ├── llm.ts          # AWS Bedrock LLM
│   │   ├── imageGeneration.ts # AWS Bedrock images
│   │   └── voiceTranscription.ts # AWS Transcribe
│   ├── routers.ts          # Thin aggregator (~55 lines)
│   ├── routers/            # 17 domain-specific router files
│   │   ├── auth.ts, profile.ts, centres.ts, sites.ts
│   │   ├── bookings.ts, search.ts, admin.ts, users.ts
│   │   ├── usageCategories.ts, searchAnalytics.ts
│   │   ├── systemConfig.ts, faqs.ts, dashboard.ts
│   │   ├── budgets.ts, owners.ts, adminBooking.ts
│   │   └── assets.ts
│   ├── db.ts               # Database queries
│   └── storage.ts          # Forge API proxy for file storage
│
├── shared/                 # Shared code (client + server)
│   └── types.ts            # Shared TypeScript types
│
├── drizzle/                # Database schema & migrations
│   └── schema.ts           # Drizzle ORM schema
│
├── k8s/                    # Kubernetes manifests
│   ├── deployment.yml
│   ├── service.yml
│   ├── configmap.yml
│   └── ingress.yml
│
├── scripts/                # Utility scripts
│   ├── seed-admin-user.ts  # Create admin user
│   ├── import-*.ts         # Data import scripts
│   └── debug/              # 68 debug/test scripts (moved from root)
│
└── .github/workflows/      # CI/CD
    └── deploy.yml          # EKS deployment

Environment Variables

Required

Variable Description
DATABASE_URL PostgreSQL connection string
JWT_SECRET Secret for JWT token signing
AWS_REGION AWS region (default: ap-southeast-2)
AWS_S3_BUCKET S3 bucket for file storage
AWS_ACCESS_KEY_ID AWS credentials (or use IRSA)
AWS_SECRET_ACCESS_KEY AWS credentials (or use IRSA)
BUILT_IN_FORGE_API_URL Storage proxy base URL (required for file storage)
BUILT_IN_FORGE_API_KEY Storage proxy API key (required for file storage)

Optional

Variable Description Default
OAUTH_SERVER_URL OAuth server for legacy auth -
OWNER_OPEN_ID Owner OAuth identifier -
AMAZON_LOCATION_PLACE_INDEX Place index name casuallease-place-index
AMAZON_LOCATION_ROUTE_CALCULATOR Route calculator name casuallease-route-calculator
SMTP_HOST Email server host -
SMTP_PORT Email server port 587
SMTP_USER Email username -
SMTP_PASS Email password -
SMTP_FROM Default from address -

AWS Services Integration

File Storage (Forge API Proxy)

File: server/storage.ts

Storage uses a Forge API proxy (BUILT_IN_FORGE_API_URL + BUILT_IN_FORGE_API_KEY), not direct AWS S3 SDK calls. The AWS S3 SDK is installed but only used for specific secondary operations.

import { storagePut, storageGet } from './storage';

// Upload a file
const { url } = await storagePut('path/to/file.jpg', buffer, 'image/jpeg');

// Get download URL
const { url } = await storageGet('path/to/file.jpg');

Amazon Location Service - Maps & Geocoding

File: server/_core/amazonLocation.ts

import { geocode, reverseGeocode, placesAutocomplete, getDirections } from './_core/amazonLocation';

// Address to coordinates
const result = await geocode('123 Main St, Sydney NSW');

// Coordinates to address
const result = await reverseGeocode(-33.8688, 151.2093);

// Place autocomplete suggestions
const suggestions = await placesAutocomplete('Westfield', { maxResults: 5 });

// Get directions between points
const route = await getDirections(
  { lat: -33.8688, lng: 151.2093 },
  { lat: -33.9173, lng: 151.2313 }
);

Required AWS Resources:

  • Place Index: casuallease-place-index
  • Route Calculator: casuallease-route-calculator

AWS Bedrock - LLM

File: server/_core/llm.ts

import { invokeLLM } from './_core/llm';

const response = await invokeLLM({
  model: 'anthropic.claude-3-haiku-20240307-v1:0',
  messages: [
    { role: 'user', content: 'Describe this shopping centre...' }
  ],
  maxTokens: 1000,
});

AWS Bedrock - Image Generation

File: server/_core/imageGeneration.ts

import { generateImage } from './_core/imageGeneration';

const { url } = await generateImage({
  prompt: 'A modern shopping centre kiosk with LED lighting',
  width: 1024,
  height: 1024,
});

AWS Transcribe - Voice to Text

File: server/_core/voiceTranscription.ts

import { transcribeAudio } from './_core/voiceTranscription';

const { text, segments } = await transcribeAudio(audioUrl, {
  language: 'en-AU',
});

Database Schema

Key tables in drizzle/schema.ts:

Table Description
users User accounts (customers, admins, owners)
customer_profiles Extended customer info (company, ABN, insurance)
owners Shopping centre owners with bank details
shopping_centres Shopping centre locations
floor_levels Multi-level centre floors
sites Bookable spaces within centres
bookings Site reservations
usage_categories Product/service categories
transactions Payment records

User Roles

  • customer - Can browse and book sites
  • owner_centre_manager - Manage specific centre
  • owner_state_admin - Manage centres in a state
  • mega_state_admin - State-level admin access
  • mega_admin - Full system access

Auth Procedure Hierarchy (server/_core/trpc.ts)

  • publicProcedure — anyone (no auth required)
  • protectedProcedure — any logged-in user
  • ownerProcedure — any non-customer role (all owner_* + mega_state_admin + mega_admin)
  • adminProcedure — mega_admin or mega_state_admin only

Authentication

Dual auth system in server/_core/context.ts: password-based JWT (primary, 7-day sessions) with SDK/OAuth fallback. Login is rate-limited (5 attempts per 15min via server/_core/rateLimit.ts). A future Cognito integration is planned to replace the custom JWT auth.

Adding New Features

Adding a New API Endpoint

  1. Add the procedure to the appropriate domain router in server/routers/:
// server/routers/myDomain.ts
import { protectedProcedure } from "../_core/trpc";
import { z } from "zod";
import * as db from "../db";

export const myNewEndpoint = protectedProcedure
  .input(z.object({ id: z.number() }))
  .query(async ({ input, ctx }) => {
    // Access user via ctx.user
    return await db.getMyData(input.id);
  });

If creating a new router file, register it in server/routers.ts.

  1. Add database function if needed in server/db.ts:
export async function getMyData(id: number) {
  return await db.select().from(myTable).where(eq(myTable.id, id));
}
  1. Use in client:
const { data } = trpc.myRouter.myNewEndpoint.useQuery({ id: 1 });

Adding a New Database Table

  1. Add to drizzle/schema.ts:
export const myNewTable = pgTable("my_new_table", {
  id: serial("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull(),
  createdAt: timestamp("createdAt").defaultNow().notNull(),
});
  1. Generate and run migration:
pnpm run db:push

Adding a New Page

  1. Create component in client/src/pages/MyPage.tsx
  2. Add route in client/src/App.tsx:
<Route path="/my-page" component={MyPage} />

Deployment

GitHub Actions (Automatic)

Push to main triggers:

  1. Type check
  2. Docker build (ARM64)
  3. Push to ECR
  4. Deploy to EKS
  5. Run migrations

Manual Deployment

# Build Docker image
docker build -t casuallease .

# Push to ECR
aws ecr get-login-password | docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com
docker push <account>.dkr.ecr.<region>.amazonaws.com/casuallease:latest

# Apply to EKS
kubectl apply -f k8s/

Post-Deploy

# Run migrations on pod
kubectl exec -n casuallease <pod> -- npx drizzle-kit push

# Seed admin user
kubectl exec -n casuallease <pod> -- npx tsx scripts/seed-admin-user.ts

Default Admin Credentials

After seeding:

  • Username: admin
  • Password: Set via scripts/seed-admin-user.ts

Testing

# Run all tests
pnpm test

# Run specific test file
pnpm test server/booking.test.ts

# Run with coverage
pnpm test -- --coverage

Common Tasks

Import Shopping Centre Data

# Copy data to pod
kubectl cp import-data.json casuallease/<pod>:/tmp/

# Run import
kubectl exec -n casuallease <pod> -- node scripts/import-centres.js

Check Pod Logs

kubectl logs -n casuallease -l app=casuallease -f

Database Access

# Connect via pod
kubectl exec -it -n casuallease <pod> -- node -e "
  const { Pool } = require('pg');
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  // Run queries...
"

Tech Stack

Layer Technology
Frontend React 19, TypeScript, Vite, TailwindCSS
UI Components Radix UI, Lucide Icons
State TanStack Query, tRPC
Backend Express, tRPC, Node.js
Database PostgreSQL, Drizzle ORM
Auth JWT (7-day sessions), bcrypt, OAuth fallback
Maps Google Maps (client), Amazon Location (server), MapLibre GL
Storage Forge API proxy (S3 SDK secondary)
AI AWS Bedrock (Claude, Stable Diffusion)
Deployment Docker, Kubernetes (EKS)
CI/CD GitHub Actions

License

MIT

About

A comprehensive platform for short-term retail leasing in Australian shopping centres, featuring centralized booking API, multi-site synchronization, landlord/tenant dashboards, and admin controls with Stripe payment integration. · Built with Manus

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors