Initial production setup with dual-token auth
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Shared Constants
|
||||
*/
|
||||
|
||||
/** Token refresh interval in milliseconds (30 minutes) */
|
||||
export const TOKEN_REFRESH_INTERVAL_MS = 30 * 60 * 1000;
|
||||
|
||||
/** Buffer before token expiry to trigger refresh (15 minutes) */
|
||||
export const TOKEN_EXPIRY_BUFFER_MS = 15 * 60 * 1000;
|
||||
|
||||
/** Token TTL in cache (1 hour) */
|
||||
export const TOKEN_TTL_SECONDS = 3600;
|
||||
|
||||
/** Refresh token TTL in cache (30 days) */
|
||||
export const REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
||||
|
||||
/** API request timeout in milliseconds */
|
||||
export const API_TIMEOUT_MS = 30000;
|
||||
|
||||
/** Cache key prefixes */
|
||||
export const CACHE_KEYS = {
|
||||
TOKEN_PB: (userId: string) => `tokens:${userId}:pb`,
|
||||
TOKEN_GRAPH: (userId: string) => `tokens:${userId}:graph`,
|
||||
REFRESH_PB: (userId: string) => `tokens:${userId}:refresh_pb`,
|
||||
REFRESH_GRAPH: (userId: string) => `tokens:${userId}:refresh_graph`,
|
||||
JOB_FILES: (jobId: string) => `jobs:${jobId}:files`,
|
||||
ACTIVE_USERS: 'active_users',
|
||||
} as const;
|
||||
|
||||
/** Local storage keys */
|
||||
export const STORAGE_KEYS = {
|
||||
PB_TOKEN: 'job-info:pb_token',
|
||||
GRAPH_TOKEN: 'job-info:graph_token',
|
||||
USER: 'job-info:user',
|
||||
EXPIRES_AT: 'job-info:expires_at',
|
||||
LAST_SYNC: 'job-info:last_sync',
|
||||
JOBS_CACHE: 'job-info:jobs',
|
||||
} as const;
|
||||
|
||||
/** API endpoints */
|
||||
export const API_ENDPOINTS = {
|
||||
LOGIN_CALLBACK: '/api/auth/login-callback',
|
||||
LOGOUT: '/api/auth/logout',
|
||||
REFRESH_TOKEN: '/api/auth/refresh-token',
|
||||
JOBS: '/api/jobs',
|
||||
FILES: '/api/files',
|
||||
HEALTH: '/api/health',
|
||||
} as const;
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Authentication Types - Shared between frontend and backend
|
||||
*/
|
||||
|
||||
/** Token types supported by the system */
|
||||
export type TokenType = 'pb' | 'graph';
|
||||
|
||||
/** Stored token data in cache */
|
||||
export interface CachedToken {
|
||||
token: string;
|
||||
refresh_token?: string;
|
||||
expiresAt: number;
|
||||
userId: string;
|
||||
source?: 'cache' | 'refreshed';
|
||||
}
|
||||
|
||||
/** Token response from refresh endpoint */
|
||||
export interface TokenRefreshResponse {
|
||||
token: string;
|
||||
expiresAt: number;
|
||||
source: 'cache' | 'refreshed';
|
||||
type: TokenType;
|
||||
}
|
||||
|
||||
/** User session data from PocketBase */
|
||||
export interface PocketBaseUser {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
verified: boolean;
|
||||
created: string;
|
||||
updated: string;
|
||||
}
|
||||
|
||||
/** Alias for PocketBaseUser */
|
||||
export type User = PocketBaseUser;
|
||||
|
||||
/** Authentication state for frontend */
|
||||
export interface AuthState {
|
||||
isAuthenticated: boolean;
|
||||
user?: PocketBaseUser | null;
|
||||
pbToken?: string | null;
|
||||
graphToken?: string | null;
|
||||
expiresAt?: number;
|
||||
}
|
||||
|
||||
/** Login callback request body */
|
||||
export interface LoginCallbackRequest {
|
||||
pbToken: string;
|
||||
graphToken: string;
|
||||
refreshTokenPb?: string;
|
||||
refreshTokenGraph?: string;
|
||||
user: PocketBaseUser;
|
||||
}
|
||||
|
||||
/** Login callback response */
|
||||
export interface LoginCallbackResponse {
|
||||
success: boolean;
|
||||
user: PocketBaseUser;
|
||||
expiresAt_pb: number;
|
||||
expiresAt_graph: number;
|
||||
}
|
||||
|
||||
/** Logout response */
|
||||
export interface LogoutResponse {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
/** Token validation result */
|
||||
export interface TokenValidation {
|
||||
valid: boolean;
|
||||
expired: boolean;
|
||||
expiresIn: number;
|
||||
userId?: string;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Job Types - Shared between frontend and backend
|
||||
*/
|
||||
|
||||
/** Job status */
|
||||
export type JobStatus = 'active' | 'completed' | 'pending' | 'cancelled' | 'Active' | 'Completed' | 'Pending' | 'On Hold';
|
||||
|
||||
/** Job record from PocketBase */
|
||||
export interface Job {
|
||||
id: string;
|
||||
job_number: string;
|
||||
job_name: string;
|
||||
client_name: string;
|
||||
status: JobStatus;
|
||||
site_id?: string;
|
||||
drive_id?: string;
|
||||
folder_path?: string;
|
||||
created: string;
|
||||
updated: string;
|
||||
// Computed/alias properties for frontend convenience
|
||||
jobNumber?: string;
|
||||
customer?: string;
|
||||
address?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
notes?: StickyNote[];
|
||||
}
|
||||
|
||||
/** Job file from SharePoint/Graph API */
|
||||
export interface JobFile {
|
||||
id: string;
|
||||
name: string;
|
||||
size: number;
|
||||
mimeType: string;
|
||||
webUrl: string;
|
||||
downloadUrl?: string;
|
||||
createdDateTime: string;
|
||||
lastModifiedDateTime: string;
|
||||
createdBy?: {
|
||||
user: {
|
||||
displayName: string;
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
lastModifiedBy?: {
|
||||
user: {
|
||||
displayName: string;
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/** Job files response */
|
||||
export interface JobFilesResponse {
|
||||
jobId: string;
|
||||
files: JobFile[];
|
||||
nextLink?: string;
|
||||
cached: boolean;
|
||||
}
|
||||
|
||||
/** Sticky note for a job */
|
||||
export interface StickyNote {
|
||||
id: string;
|
||||
job_id: string;
|
||||
content: string;
|
||||
color?: 'yellow' | 'blue' | 'green' | 'pink' | 'purple';
|
||||
position_x?: number;
|
||||
position_y?: number;
|
||||
created: string;
|
||||
updated: string;
|
||||
created_by: string;
|
||||
// Alias for frontend
|
||||
createdAt?: string;
|
||||
}
|
||||
|
||||
/** Job list response */
|
||||
export interface JobListResponse {
|
||||
jobs: Job[];
|
||||
page: number;
|
||||
perPage: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
/** Job detail response */
|
||||
export interface JobDetailResponse {
|
||||
job: Job;
|
||||
files: JobFile[];
|
||||
notes: StickyNote[];
|
||||
}
|
||||
Reference in New Issue
Block a user