Add Legal Office seal and complete Azure CDN deployment
- Add Legal Office of the Master seal (SVG design with Maltese Cross, scales of justice, legal scroll) - Create legal-office-manifest-template.json for Legal Office credentials - Update SEAL_MAPPING.md and DESIGN_GUIDE.md with Legal Office seal documentation - Complete Azure CDN infrastructure deployment: - Resource group, storage account, and container created - 17 PNG seal files uploaded to Azure Blob Storage - All manifest templates updated with Azure URLs - Configuration files generated (azure-cdn-config.env) - Add comprehensive Azure CDN setup scripts and documentation - Fix manifest URL generation to prevent double slashes - Verify all seals accessible via HTTPS
This commit is contained in:
@@ -13,11 +13,11 @@ export class ApiClient {
|
||||
|
||||
constructor(baseURL?: string) {
|
||||
// Initialize service clients - each manages its own axios instance
|
||||
this.identity = new IdentityClient();
|
||||
this.eresidency = new EResidencyClient();
|
||||
this.intake = new IntakeClient();
|
||||
this.finance = new FinanceClient();
|
||||
this.dataroom = new DataroomClient();
|
||||
this.identity = new IdentityClient(baseURL);
|
||||
this.eresidency = new EResidencyClient(baseURL);
|
||||
this.intake = new IntakeClient(baseURL);
|
||||
this.finance = new FinanceClient(baseURL);
|
||||
this.dataroom = new DataroomClient(baseURL);
|
||||
}
|
||||
|
||||
setAuthToken(token: string): void {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiClient } from './client';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import type { eResidencyApplication, ApplicationStatus } from '@the-order/schemas';
|
||||
|
||||
export interface SubmitApplicationRequest {
|
||||
@@ -36,14 +36,60 @@ export interface AdjudicateRequest {
|
||||
}
|
||||
|
||||
export class EResidencyClient {
|
||||
constructor(private client: ApiClient) {}
|
||||
protected client: AxiosInstance;
|
||||
|
||||
async submitApplication(request: SubmitApplicationRequest) {
|
||||
return this.client.post<eResidencyApplication>('/applications', request);
|
||||
constructor(baseURL?: string) {
|
||||
const apiBaseURL =
|
||||
baseURL ||
|
||||
(typeof window !== 'undefined'
|
||||
? process.env.NEXT_PUBLIC_ERESIDENCY_SERVICE_URL || 'http://localhost:4001'
|
||||
: 'http://localhost:4001');
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL: apiBaseURL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Set up request interceptor for authentication
|
||||
this.client.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = this.getAuthToken();
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
}
|
||||
|
||||
async getApplication(id: string) {
|
||||
return this.client.get<eResidencyApplication>(`/applications/${id}`);
|
||||
private getAuthToken(): string | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
return localStorage.getItem('auth_token');
|
||||
}
|
||||
|
||||
setAuthToken(token: string): void {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('auth_token', token);
|
||||
}
|
||||
}
|
||||
|
||||
clearAuthToken(): void {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('auth_token');
|
||||
}
|
||||
}
|
||||
|
||||
async submitApplication(request: SubmitApplicationRequest): Promise<eResidencyApplication> {
|
||||
const response = await this.client.post<eResidencyApplication>('/applications', request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getApplication(id: string): Promise<eResidencyApplication> {
|
||||
const response = await this.client.get<eResidencyApplication>(`/applications/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getReviewQueue(filters?: {
|
||||
@@ -52,38 +98,50 @@ export class EResidencyClient {
|
||||
assignedTo?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}) {
|
||||
}): Promise<{
|
||||
applications: eResidencyApplication[];
|
||||
total: number;
|
||||
}> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.riskBand) params.append('riskBand', filters.riskBand);
|
||||
if (filters?.status) params.append('status', filters.status);
|
||||
if (filters?.assignedTo) params.append('assignedTo', filters.assignedTo);
|
||||
if (filters?.limit) params.append('limit', filters.limit.toString());
|
||||
if (filters?.offset) params.append('offset', filters.offset.toString());
|
||||
return this.client.get<{
|
||||
const response = await this.client.get<{
|
||||
applications: eResidencyApplication[];
|
||||
total: number;
|
||||
}>(`/review/queue?${params.toString()}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getApplicationForReview(id: string) {
|
||||
return this.client.get<eResidencyApplication>(`/review/applications/${id}`);
|
||||
async getApplicationForReview(id: string): Promise<eResidencyApplication> {
|
||||
const response = await this.client.get<eResidencyApplication>(`/review/applications/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async adjudicateApplication(id: string, request: AdjudicateRequest) {
|
||||
return this.client.post<eResidencyApplication>(`/review/applications/${id}/adjudicate`, request);
|
||||
async adjudicateApplication(id: string, request: AdjudicateRequest): Promise<eResidencyApplication> {
|
||||
const response = await this.client.post<eResidencyApplication>(`/review/applications/${id}/adjudicate`, request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async revokeCredential(residentNumber: string, reason: string) {
|
||||
return this.client.post('/applications/revoke', { residentNumber, reason });
|
||||
async revokeCredential(residentNumber: string, reason: string): Promise<void> {
|
||||
await this.client.post('/applications/revoke', { residentNumber, reason });
|
||||
}
|
||||
|
||||
async getStatus() {
|
||||
return this.client.get<Array<{
|
||||
async getStatus(): Promise<Array<{
|
||||
residentNumber: string;
|
||||
status: string;
|
||||
issuedAt?: string;
|
||||
revokedAt?: string;
|
||||
}>> {
|
||||
const response = await this.client.get<Array<{
|
||||
residentNumber: string;
|
||||
status: string;
|
||||
issuedAt?: string;
|
||||
revokedAt?: string;
|
||||
}>>('/status');
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiClient } from './client';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import type { eResidentCredential, eCitizenCredential } from '@the-order/schemas';
|
||||
|
||||
export interface IssueVCRequest {
|
||||
@@ -49,18 +49,73 @@ export interface CredentialMetrics {
|
||||
}
|
||||
|
||||
export class IdentityClient {
|
||||
constructor(private client: ApiClient) {}
|
||||
protected client: AxiosInstance;
|
||||
|
||||
async issueCredential(request: IssueVCRequest) {
|
||||
return this.client.post<{ credential: eResidentCredential | eCitizenCredential }>('/vc/issue', request);
|
||||
constructor(baseURL?: string) {
|
||||
const apiBaseURL =
|
||||
baseURL ||
|
||||
(typeof window !== 'undefined'
|
||||
? process.env.NEXT_PUBLIC_IDENTITY_SERVICE_URL || 'http://localhost:4002'
|
||||
: 'http://localhost:4002');
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL: apiBaseURL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Set up request interceptor for authentication
|
||||
this.client.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = this.getAuthToken();
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
}
|
||||
|
||||
async verifyCredential(request: VerifyVCRequest) {
|
||||
return this.client.post<{ valid: boolean }>('/vc/verify', request);
|
||||
private getAuthToken(): string | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
return localStorage.getItem('auth_token');
|
||||
}
|
||||
|
||||
async batchIssue(request: BatchIssuanceRequest) {
|
||||
return this.client.post<{
|
||||
setAuthToken(token: string): void {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('auth_token', token);
|
||||
}
|
||||
}
|
||||
|
||||
clearAuthToken(): void {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('auth_token');
|
||||
}
|
||||
}
|
||||
|
||||
async issueCredential(request: IssueVCRequest): Promise<{ credential: eResidentCredential | eCitizenCredential }> {
|
||||
const response = await this.client.post<{ credential: eResidentCredential | eCitizenCredential }>('/vc/issue', request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async verifyCredential(request: VerifyVCRequest): Promise<{ valid: boolean }> {
|
||||
const response = await this.client.post<{ valid: boolean }>('/vc/verify', request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async batchIssue(request: BatchIssuanceRequest): Promise<{
|
||||
jobId: string;
|
||||
total: number;
|
||||
accepted: number;
|
||||
results: Array<{
|
||||
index: number;
|
||||
credentialId?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
}> {
|
||||
const response = await this.client.post<{
|
||||
jobId: string;
|
||||
total: number;
|
||||
accepted: number;
|
||||
@@ -70,21 +125,31 @@ export class IdentityClient {
|
||||
error?: string;
|
||||
}>;
|
||||
}>('/vc/issue/batch', request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async revokeCredential(credentialId: string, reason?: string) {
|
||||
return this.client.post('/vc/revoke', { credentialId, reason });
|
||||
async revokeCredential(credentialId: string, reason?: string): Promise<void> {
|
||||
await this.client.post('/vc/revoke', { credentialId, reason });
|
||||
}
|
||||
|
||||
async getMetrics(startDate?: Date, endDate?: Date) {
|
||||
async getMetrics(startDate?: Date, endDate?: Date): Promise<CredentialMetrics> {
|
||||
const params = new URLSearchParams();
|
||||
if (startDate) params.append('startDate', startDate.toISOString());
|
||||
if (endDate) params.append('endDate', endDate.toISOString());
|
||||
return this.client.get<CredentialMetrics>(`/metrics?${params.toString()}`);
|
||||
const response = await this.client.get<CredentialMetrics>(`/metrics?${params.toString()}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getMetricsDashboard() {
|
||||
return this.client.get<{
|
||||
async getMetricsDashboard(): Promise<{
|
||||
summary: CredentialMetrics;
|
||||
trends: {
|
||||
daily: Array<{ date: string; count: number }>;
|
||||
weekly: Array<{ week: string; count: number }>;
|
||||
monthly: Array<{ month: string; count: number }>;
|
||||
};
|
||||
topCredentialTypes: Array<{ type: string; count: number; percentage: number }>;
|
||||
}> {
|
||||
const response = await this.client.get<{
|
||||
summary: CredentialMetrics;
|
||||
trends: {
|
||||
daily: Array<{ date: string; count: number }>;
|
||||
@@ -93,6 +158,7 @@ export class IdentityClient {
|
||||
};
|
||||
topCredentialTypes: Array<{ type: string; count: number; percentage: number }>;
|
||||
}>('/metrics/dashboard');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async searchAuditLogs(filters: {
|
||||
@@ -107,8 +173,35 @@ export class IdentityClient {
|
||||
ipAddress?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}) {
|
||||
return this.client.post('/metrics/audit/search', filters);
|
||||
}): Promise<{
|
||||
logs: Array<{
|
||||
id: string;
|
||||
action: string;
|
||||
credentialId?: string;
|
||||
subjectDid?: string;
|
||||
performedBy?: string;
|
||||
timestamp: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}> {
|
||||
const response = await this.client.post<{
|
||||
logs: Array<{
|
||||
id: string;
|
||||
action: string;
|
||||
credentialId?: string;
|
||||
subjectDid?: string;
|
||||
performedBy?: string;
|
||||
timestamp: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}>('/metrics/audit/search', filters);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
export interface DocumentUpload {
|
||||
file: File | Blob;
|
||||
file: File | Blob | Buffer;
|
||||
documentType: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
@@ -65,7 +65,11 @@ export class IntakeClient {
|
||||
|
||||
async uploadDocument(upload: DocumentUpload): Promise<DocumentMetadata> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', upload.file);
|
||||
// FormData.append accepts File, Blob, or string, but Buffer needs to be converted
|
||||
const file: File | Blob = upload.file instanceof Buffer
|
||||
? new Blob([new Uint8Array(upload.file)])
|
||||
: (upload.file as File | Blob);
|
||||
formData.append('file', file);
|
||||
formData.append('documentType', upload.documentType);
|
||||
if (upload.metadata) {
|
||||
formData.append('metadata', JSON.stringify(upload.metadata));
|
||||
|
||||
Reference in New Issue
Block a user