Initial production setup with dual-token auth

This commit is contained in:
2026-01-01 16:44:11 +00:00
commit 7c3d62d4a9
38 changed files with 6299 additions and 0 deletions
+76
View File
@@ -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;
}
+90
View File
@@ -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[];
}