@gaurav_bhandari/common-frontend-services 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/dist/bin/cli.js +22 -0
- package/dist/src/cli/commands/add.js +104 -0
- package/dist/src/cli/commands/list.js +19 -0
- package/dist/src/cli/registry.js +125 -0
- package/package.json +39 -0
- package/src/services/API-Service/baseApiService.ts +296 -0
- package/src/services/API-Service/csrfAPIService.ts +93 -0
- package/src/services/API-Service/docs/WhatIsBaseApiService.md +121 -0
- package/src/services/API-Service/docs/WhatIsCsrfApiService.md +67 -0
- package/src/services/Architecture-Reports-Service/architecture-checklist.md +54 -0
- package/src/services/Architecture-Reports-Service/report-generation-prompt.md +60 -0
- package/src/services/Authorization-Service/docs/WhatIsRBACService.md +112 -0
- package/src/services/Authorization-Service/permissions.ts +16 -0
- package/src/services/Authorization-Service/rbacService.ts +63 -0
- package/src/services/Authorization-Service/rolePermissions.ts +10 -0
- package/src/services/Authorization-Service/userPermissions/adminPermissions.ts +13 -0
- package/src/services/Cache-Service/README.md +50 -0
- package/src/services/Cache-Service/cacheService.ts +142 -0
- package/src/services/Cache-Service/simple-explanation.md +55 -0
- package/src/services/Cache-Service/sw-strategies.ts +51 -0
- package/src/services/Date-Service/dateService.ts +93 -0
- package/src/services/Date-Service/docs/WhatIsDateService.md +63 -0
- package/src/services/Environment-Config-Service/docs/WhatIsEnvironmentConfigService.md +68 -0
- package/src/services/Environment-Config-Service/environmentConfigService.ts +112 -0
- package/src/services/Environment-Config-Service/simple-explanation.md +49 -0
- package/src/services/Feature-Flag-Service/docs/WhatIsFeatureFlagService.md +98 -0
- package/src/services/Feature-Flag-Service/featureFlagService.ts +91 -0
- package/src/services/File-Service/docs/WhatIsFileService.md +78 -0
- package/src/services/File-Service/fileService.ts +224 -0
- package/src/services/File-Service/simple-explanation.md +49 -0
- package/src/services/Google-Login-Service/README.md +48 -0
- package/src/services/Google-Login-Service/googleLoginService.ts +209 -0
- package/src/services/GraphQl-Service/docs/WhatIsGraphQLService.md +101 -0
- package/src/services/GraphQl-Service/graphqlService.ts +81 -0
- package/src/services/Logger-Service/README.md +44 -0
- package/src/services/Logger-Service/loggerService.ts +125 -0
- package/src/services/Logger-Service/simple-explanation.md +43 -0
- package/src/services/Logger-Service/verify-logger.ts +62 -0
- package/src/services/Monitoring-Service/README.md +48 -0
- package/src/services/Monitoring-Service/monitoringService.ts +229 -0
- package/src/services/Monitoring-Service/simple-explanation.md +48 -0
- package/src/services/Monitoring-Service/verify-monitoring.ts +74 -0
- package/src/services/Security-Reports-Service/report-generation-prompt.md +52 -0
- package/src/services/Security-Reports-Service/security-checklist.md +66 -0
- package/src/services/Storage-Service/docs/WhatIsStorageService.md +70 -0
- package/src/services/Storage-Service/indexedDBService.ts +103 -0
- package/src/services/Storage-Service/localStorageService.ts +42 -0
- package/src/services/Storage-Service/sessionStorageService.ts +42 -0
- package/src/services/Validation-Service/form.ts +66 -0
- package/src/services/Validation-Service/rules.ts +338 -0
- package/src/services/Worker-Service/docs/WhatIsWorkerService.md +57 -0
- package/src/services/Worker-Service/workerService.ts +122 -0
- package/src/services/i18n-Service/docs/WhatIsI18nService.md +108 -0
- package/src/services/i18n-Service/i18nService.ts +127 -0
- package/src/services/i18n-Service/locales/en.json +17 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# FeatureFlagService
|
|
2
|
+
|
|
3
|
+
The `FeatureFlagService` provides a simple, framework-agnostic way to manage feature toggles in your application. It allows you to enable or disable features remotely without redeploying code.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Feature flags are boolean values (true/false) associated with a key (e.g., `new-dashboard`). The service fetches these flags from the backend at startup and makes them available throughout the app.
|
|
8
|
+
|
|
9
|
+
### Key Features
|
|
10
|
+
|
|
11
|
+
- **Backend Driven**: Flags are fetched from an API endpoint (`GET /feature-flags`).
|
|
12
|
+
- **Framework Agnostic**: Can be used in Vue, React, or plain TypeScript files.
|
|
13
|
+
- **Singleton**: Ensures flags are fetched only once per session.
|
|
14
|
+
|
|
15
|
+
## How to Use
|
|
16
|
+
|
|
17
|
+
### 1. Initialization
|
|
18
|
+
|
|
19
|
+
You must initialize the service before using it, typically in your application's entry point.
|
|
20
|
+
|
|
21
|
+
**Vue (`main.ts`):**
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { createApp } from "vue";
|
|
25
|
+
import App from "./App.vue";
|
|
26
|
+
import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
|
|
27
|
+
|
|
28
|
+
async function init() {
|
|
29
|
+
await featureFlagService.init(); // Fetch flags
|
|
30
|
+
createApp(App).mount("#app");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
init();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**React (`main.tsx`):**
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import React from "react";
|
|
40
|
+
import ReactDOM from "react-dom/client";
|
|
41
|
+
import App from "./App";
|
|
42
|
+
import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
|
|
43
|
+
|
|
44
|
+
featureFlagService.init().then(() => {
|
|
45
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
46
|
+
<React.StrictMode>
|
|
47
|
+
<App />
|
|
48
|
+
</React.StrictMode>
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Checking Flags
|
|
54
|
+
|
|
55
|
+
Use `isEnabled(key)` to check if a feature is active.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
|
|
59
|
+
|
|
60
|
+
if (featureFlagService.isEnabled("new-dashboard")) {
|
|
61
|
+
// Show new dashboard
|
|
62
|
+
} else {
|
|
63
|
+
// Show legacy dashboard
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Framework specific integration
|
|
68
|
+
|
|
69
|
+
You can easily create wrapper components or directives.
|
|
70
|
+
|
|
71
|
+
**Vue Directive (`v-feature`)**:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// directives/feature.ts
|
|
75
|
+
import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
|
|
76
|
+
|
|
77
|
+
export const vFeature = {
|
|
78
|
+
mounted(el, binding) {
|
|
79
|
+
if (!featureFlagService.isEnabled(binding.value)) {
|
|
80
|
+
el.parentNode?.removeChild(el);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**React Component (`<Feature>`)**:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
|
|
90
|
+
|
|
91
|
+
const Feature = ({ name, children }) => {
|
|
92
|
+
return featureFlagService.isEnabled(name) ? children : null;
|
|
93
|
+
};
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
The service attempts to fetch flags from `GET /feature-flags`. Ensure your `BaseApiService` is correctly configured and that your backend provides this endpoint.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import BaseService from "../API-Service/baseApiService";
|
|
2
|
+
|
|
3
|
+
export class FeatureFlagService {
|
|
4
|
+
private static instance: FeatureFlagService;
|
|
5
|
+
private flags: Record<string, boolean> = {};
|
|
6
|
+
private isInitialized = false;
|
|
7
|
+
private fetchPromise: Promise<void> | null = null;
|
|
8
|
+
|
|
9
|
+
private constructor() {
|
|
10
|
+
// Optionally load default flags from local storage or env
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public static getInstance(): FeatureFlagService {
|
|
14
|
+
if (!FeatureFlagService.instance) {
|
|
15
|
+
FeatureFlagService.instance = new FeatureFlagService();
|
|
16
|
+
}
|
|
17
|
+
return FeatureFlagService.instance;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Initialize the service by fetching flags from the backend.
|
|
22
|
+
* Call this in your main entry point (main.ts/App.tsx).
|
|
23
|
+
*/
|
|
24
|
+
public async init(): Promise<void> {
|
|
25
|
+
if (this.isInitialized) return;
|
|
26
|
+
|
|
27
|
+
// Prevent multiple simultaneous fetches
|
|
28
|
+
if (this.fetchPromise) {
|
|
29
|
+
return this.fetchPromise;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.fetchPromise = this.fetchFlags();
|
|
33
|
+
await this.fetchPromise;
|
|
34
|
+
this.isInitialized = true;
|
|
35
|
+
this.fetchPromise = null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Force refresh flags from the server.
|
|
40
|
+
* Useful if the user changes settings or switches accounts.
|
|
41
|
+
*/
|
|
42
|
+
public async refresh(): Promise<void> {
|
|
43
|
+
this.isInitialized = false;
|
|
44
|
+
await this.init();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if a feature is enabled.
|
|
49
|
+
* @param key The feature flag key (e.g., 'new-dashboard')
|
|
50
|
+
* @param defaultValue Default value if flag is missing (default: false)
|
|
51
|
+
*/
|
|
52
|
+
public isEnabled(key: string, defaultValue = false): boolean {
|
|
53
|
+
if (!this.isInitialized) {
|
|
54
|
+
console.warn(
|
|
55
|
+
`FeatureFlagService: Checking flag '${key}' before initialization.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return this.flags[key] ?? defaultValue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get all currently loaded flags.
|
|
63
|
+
*/
|
|
64
|
+
public getFlags(): Record<string, boolean> {
|
|
65
|
+
return { ...this.flags };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async fetchFlags(retries = 3, delay = 1000): Promise<void> {
|
|
69
|
+
try {
|
|
70
|
+
// Assuming GET /feature-flags returns { "flag-name": true, ... }
|
|
71
|
+
this.flags =
|
|
72
|
+
await BaseService.get<Record<string, boolean>>("feature-flags");
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (retries > 0) {
|
|
75
|
+
console.warn(
|
|
76
|
+
`Failed to fetch feature flags. Retrying in ${delay}ms... (${retries} attempts left)`,
|
|
77
|
+
);
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
79
|
+
return this.fetchFlags(retries - 1, delay * 2);
|
|
80
|
+
} else {
|
|
81
|
+
console.error(
|
|
82
|
+
"Failed to fetch feature flags after multiple attempts:",
|
|
83
|
+
error,
|
|
84
|
+
);
|
|
85
|
+
// Fallback strategies could go here (e.g. use cached values or defaults)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const featureFlagService = FeatureFlagService.getInstance();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# FileService
|
|
2
|
+
|
|
3
|
+
The `FileService` provides utilities for handling file uploads, client-side processing, and previews.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Handling files on the frontend involves more than just sending them to an API. Key challenges include:
|
|
8
|
+
|
|
9
|
+
1. **Large Files**: Uploading 1GB+ files in one go often fails. We need chunking.
|
|
10
|
+
2. **Performance**: Compressing images _before_ upload saves bandwidth.
|
|
11
|
+
3. **UX**: Showing instant previews without waiting for a server round-trip.
|
|
12
|
+
|
|
13
|
+
### Key Features
|
|
14
|
+
|
|
15
|
+
- **Chunked Uploads**: Splits large files into 5MB chunks and uploads them sequentially.
|
|
16
|
+
- **Client-Side Compression**: Resizes and compresses images using the browser's Canvas API.
|
|
17
|
+
- **Instant Previews**: Generates `blob:` or `data:` URLs for immediate display.
|
|
18
|
+
|
|
19
|
+
## How to Use
|
|
20
|
+
|
|
21
|
+
### 1. Simple Upload
|
|
22
|
+
|
|
23
|
+
For small files (e.g., avatars, documents < 10MB).
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { fileService } from "@/File-Service/fileService";
|
|
27
|
+
|
|
28
|
+
async function handleUpload(file: File) {
|
|
29
|
+
try {
|
|
30
|
+
const response = await fileService.upload(file, "users/avatar");
|
|
31
|
+
console.log("Uploaded:", response);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error("Upload failed", error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Chunked Upload (Recommended for Video/Large Files)
|
|
39
|
+
|
|
40
|
+
Automatically handles the `init -> chunk -> complete` flow.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const onProgress = (progress) => {
|
|
44
|
+
console.log(`Upload is ${progress.percentage}% complete`);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
await fileService.uploadChunked(largeVideoFile, "videos/upload", onProgress);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Backend Requirements**:
|
|
51
|
+
Your backend must support the following endpoints for chunked uploads:
|
|
52
|
+
|
|
53
|
+
- `POST /init`: Returns `{ uploadId: string }`
|
|
54
|
+
- `POST /chunk`: Accepts `formData` with `chunk`, `uploadId`, `chunkIndex`
|
|
55
|
+
- `POST /complete`: Accepts `{ uploadId }` and reassembles the file.
|
|
56
|
+
|
|
57
|
+
### 3. Image Compression
|
|
58
|
+
|
|
59
|
+
Compress an image before uploading to save bandwidth.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const compressedFile = await fileService.compressImage(originalFile, {
|
|
63
|
+
maxWidth: 1024,
|
|
64
|
+
quality: 0.7, // 70% quality
|
|
65
|
+
type: "image/webp",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Now upload the smaller file
|
|
69
|
+
await fileService.upload(compressedFile, "posts/image");
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4. Generatng Previews
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// specific method
|
|
76
|
+
const previewUrl = await fileService.generatePreview(file);
|
|
77
|
+
// <img src={previewUrl} />
|
|
78
|
+
```
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import BaseService from "@/API-Service/baseApiService";
|
|
2
|
+
|
|
3
|
+
export interface UploadProgress {
|
|
4
|
+
loaded: number;
|
|
5
|
+
total: number;
|
|
6
|
+
percentage: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface CompressionOptions {
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
maxHeight?: number;
|
|
12
|
+
quality?: number; // 0 to 1
|
|
13
|
+
type?: "image/jpeg" | "image/png" | "image/webp";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Interface for custom chunked upload strategies (e.g., S3, specific backend protocols).
|
|
18
|
+
*/
|
|
19
|
+
export interface ChunkedUploadAdapter {
|
|
20
|
+
/**
|
|
21
|
+
* Initialize the upload.
|
|
22
|
+
* @returns Promise resolving to an upload ID or context object.
|
|
23
|
+
*/
|
|
24
|
+
init(file: File): Promise<string>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Upload a single chunk.
|
|
28
|
+
* @param uploadId The ID returned from init
|
|
29
|
+
* @param chunk The blob of data
|
|
30
|
+
* @param index Chunk index (0-based)
|
|
31
|
+
*/
|
|
32
|
+
uploadChunk(uploadId: string, chunk: Blob, index: number): Promise<void>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Complete the upload.
|
|
36
|
+
*/
|
|
37
|
+
complete<T>(uploadId: string): Promise<T>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class FileService {
|
|
41
|
+
private static instance: FileService;
|
|
42
|
+
|
|
43
|
+
private constructor() {}
|
|
44
|
+
|
|
45
|
+
public static getInstance(): FileService {
|
|
46
|
+
if (!FileService.instance) {
|
|
47
|
+
FileService.instance = new FileService();
|
|
48
|
+
}
|
|
49
|
+
return FileService.instance;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Standard single-request upload
|
|
54
|
+
*/
|
|
55
|
+
public async upload<T>(
|
|
56
|
+
file: File,
|
|
57
|
+
url: string,
|
|
58
|
+
onProgress?: (progress: UploadProgress) => void,
|
|
59
|
+
): Promise<T> {
|
|
60
|
+
const formData = new FormData();
|
|
61
|
+
formData.append("file", file);
|
|
62
|
+
|
|
63
|
+
return BaseService.post<T>(url, formData);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Upload a file in chunks.
|
|
68
|
+
* Supports standard API endpoint (string) or custom adapter.
|
|
69
|
+
*/
|
|
70
|
+
public async uploadChunked<T>(
|
|
71
|
+
file: File,
|
|
72
|
+
destination: string | ChunkedUploadAdapter,
|
|
73
|
+
onProgress?: (progress: UploadProgress) => void,
|
|
74
|
+
chunkSize: number = 5 * 1024 * 1024, // 5MB default
|
|
75
|
+
): Promise<T> {
|
|
76
|
+
const totalChunks = Math.ceil(file.size / chunkSize);
|
|
77
|
+
let uploadId: string;
|
|
78
|
+
|
|
79
|
+
// 1. Initialize
|
|
80
|
+
if (typeof destination === "string") {
|
|
81
|
+
const initResponse = await BaseService.post<{ uploadId: string }>(
|
|
82
|
+
`${destination}/init`,
|
|
83
|
+
{
|
|
84
|
+
filename: file.name,
|
|
85
|
+
totalChunks,
|
|
86
|
+
totalSize: file.size,
|
|
87
|
+
contentType: file.type,
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
uploadId = initResponse.uploadId;
|
|
91
|
+
} else {
|
|
92
|
+
uploadId = await destination.init(file);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let loaded = 0;
|
|
96
|
+
|
|
97
|
+
// 2. Upload chunks sequentially
|
|
98
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
99
|
+
const start = i * chunkSize;
|
|
100
|
+
const end = Math.min(start + chunkSize, file.size);
|
|
101
|
+
const chunk = file.slice(start, end);
|
|
102
|
+
|
|
103
|
+
if (typeof destination === "string") {
|
|
104
|
+
const formData = new FormData();
|
|
105
|
+
formData.append("chunk", chunk);
|
|
106
|
+
formData.append("uploadId", uploadId);
|
|
107
|
+
formData.append("chunkIndex", i.toString());
|
|
108
|
+
await BaseService.post(`${destination}/chunk`, formData);
|
|
109
|
+
} else {
|
|
110
|
+
await destination.uploadChunk(uploadId, chunk, i);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
loaded += chunk.size;
|
|
114
|
+
if (onProgress) {
|
|
115
|
+
onProgress({
|
|
116
|
+
loaded,
|
|
117
|
+
total: file.size,
|
|
118
|
+
percentage: Math.round((loaded / file.size) * 100),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 3. Complete
|
|
124
|
+
if (typeof destination === "string") {
|
|
125
|
+
return BaseService.post<T>(`${destination}/complete`, { uploadId });
|
|
126
|
+
} else {
|
|
127
|
+
return destination.complete<T>(uploadId);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate a preview URL for a given file.
|
|
133
|
+
* Supports Images (via FileReader) and returns a blob URL for others.
|
|
134
|
+
*/
|
|
135
|
+
public async generatePreview(file: File): Promise<string> {
|
|
136
|
+
if (file.type.startsWith("image/")) {
|
|
137
|
+
return new Promise<string>((resolve, reject) => {
|
|
138
|
+
const reader = new FileReader();
|
|
139
|
+
reader.onload = () => resolve(reader.result as string);
|
|
140
|
+
reader.onerror = reject;
|
|
141
|
+
reader.readAsDataURL(file);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return URL.createObjectURL(file);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Compress an image file using HTML Canvas.
|
|
149
|
+
*/
|
|
150
|
+
public async compressImage(
|
|
151
|
+
file: File,
|
|
152
|
+
options: CompressionOptions = {},
|
|
153
|
+
): Promise<File> {
|
|
154
|
+
if (!file.type.startsWith("image/")) {
|
|
155
|
+
console.warn(
|
|
156
|
+
"FileService: compressImage only supports image files. Returning original file.",
|
|
157
|
+
);
|
|
158
|
+
return file;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const {
|
|
162
|
+
maxWidth = 1920,
|
|
163
|
+
maxHeight = 1080,
|
|
164
|
+
quality = 0.8,
|
|
165
|
+
type = "image/jpeg",
|
|
166
|
+
} = options;
|
|
167
|
+
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
const img = new Image();
|
|
170
|
+
img.src = URL.createObjectURL(file);
|
|
171
|
+
|
|
172
|
+
img.onload = () => {
|
|
173
|
+
let width = img.width;
|
|
174
|
+
let height = img.height;
|
|
175
|
+
|
|
176
|
+
// Maintain aspect ratio
|
|
177
|
+
if (width > height) {
|
|
178
|
+
if (width > maxWidth) {
|
|
179
|
+
height = Math.round((height * maxWidth) / width);
|
|
180
|
+
width = maxWidth;
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
if (height > maxHeight) {
|
|
184
|
+
width = Math.round((width * maxHeight) / height);
|
|
185
|
+
height = maxHeight;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const canvas = document.createElement("canvas");
|
|
190
|
+
canvas.width = width;
|
|
191
|
+
canvas.height = height;
|
|
192
|
+
const ctx = canvas.getContext("2d");
|
|
193
|
+
|
|
194
|
+
if (!ctx) {
|
|
195
|
+
reject(new Error("Could not get canvas context"));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
200
|
+
|
|
201
|
+
canvas.toBlob(
|
|
202
|
+
(blob) => {
|
|
203
|
+
if (!blob) {
|
|
204
|
+
reject(new Error("Canvas toBlob failed"));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Create new File object
|
|
208
|
+
const newFile = new File([blob], file.name, {
|
|
209
|
+
type: type,
|
|
210
|
+
lastModified: Date.now(),
|
|
211
|
+
});
|
|
212
|
+
resolve(newFile);
|
|
213
|
+
},
|
|
214
|
+
type,
|
|
215
|
+
quality,
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
img.onerror = (err) => reject(err);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export const fileService = FileService.getInstance();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Simple Explanation of File Service
|
|
2
|
+
|
|
3
|
+
The File Service is like your **Professional Shipping Company**. It handles everything from sending small letters to moving an entire house, ensuring your items (files) arrive safely and efficiently.
|
|
4
|
+
|
|
5
|
+
## 🧠 Think of it like this:
|
|
6
|
+
|
|
7
|
+
- **Standard Upload**: This is like **Mailing a Letter**. You put it in an envelope and send it in one go. Quick and easy for small things.
|
|
8
|
+
- **Chunked Upload**: This is like **Moving a Piano**. You can't fit it through the door, so you take it apart (chunks), carry each piece separately, and reassemble it at the new house. If you trip while carrying one leg, you only pick up that leg, not the whole piano.
|
|
9
|
+
- **Compression**: This is like **Vacuum Sealing** your clothes. You squeeze out the air (unnecessary bytes) so they take up less space in the truck, but they're still clothes when you unpack them.
|
|
10
|
+
|
|
11
|
+
## 🔑 Key Features
|
|
12
|
+
|
|
13
|
+
- **Reliability**: If a big file fails halfway, you don't have to start over from 0%.
|
|
14
|
+
- **Versatility**: It can talk to your own backend server or specialized storage systems (like AWS S3).
|
|
15
|
+
- **User Experience**: It creates previews (thumbnails) so users can see what they are uploading instantly.
|
|
16
|
+
|
|
17
|
+
## 🛠️ Functions Explained
|
|
18
|
+
|
|
19
|
+
### 1. `upload(file, url)`
|
|
20
|
+
|
|
21
|
+
**What it does:** Sends a file in one piece.
|
|
22
|
+
|
|
23
|
+
**Usage:** Best for profile pictures or small documents.
|
|
24
|
+
|
|
25
|
+
### 2. `uploadChunked(file, destination)`
|
|
26
|
+
|
|
27
|
+
**What it does:** Breaks a huge file into small pieces and sends them one by one.
|
|
28
|
+
|
|
29
|
+
**How it works:**
|
|
30
|
+
|
|
31
|
+
1. **Init**: Tells the server "Hey, I'm sending a big file in 10 parts."
|
|
32
|
+
2. **Upload Loop**: Sends Part 1, then Part 2, etc. Tracks progress (e.g., "40% done").
|
|
33
|
+
3. **Complete**: Tells the server "That was the last piece, put it all together now."
|
|
34
|
+
|
|
35
|
+
### 3. `generatePreview(file)`
|
|
36
|
+
|
|
37
|
+
**What it does:** Lets you peek inside the box.
|
|
38
|
+
|
|
39
|
+
**How it works:**
|
|
40
|
+
|
|
41
|
+
- If it's an image, it creates a visual thumbnail.
|
|
42
|
+
- If it's a PDF or other file, it creates a link to open it.
|
|
43
|
+
|
|
44
|
+
### 4. `compressImage(file, options)`
|
|
45
|
+
|
|
46
|
+
**What it does:** Shrinks images to save space and data.
|
|
47
|
+
|
|
48
|
+
**How it works:**
|
|
49
|
+
It takes a huge 4K photo, resizes it (e.g., to 1080p), and slightly lowers the quality (like saving as a JPEG) so it uploads much faster without looking noticeably different.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Google Login Service
|
|
2
|
+
|
|
3
|
+
This service provides a wrapper around Google Identity Services (GIS) for implementing "Sign In With Google".
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automatic Script Loading**: Handles lazy loading of the Google JS SDK.
|
|
8
|
+
- **Environment Configuration**: API keys managed via `EnvironmentConfigService`.
|
|
9
|
+
- **Easy Button Rendering**: Simple method to render the standard Google button into any container.
|
|
10
|
+
- **Type Safety**: TypeScript interfaces for configuration and button options.
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
Ensure `VITE_GOOGLE_CLIENT_ID` is set in your `.env` file or via `window.__CONFIG__`.
|
|
15
|
+
|
|
16
|
+
```env
|
|
17
|
+
VITE_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { googleLoginService } from "./Google-Login-Service/googleLoginService";
|
|
24
|
+
|
|
25
|
+
// 1. Initialize logic
|
|
26
|
+
const handleCredentialResponse = (response: any) => {
|
|
27
|
+
console.log("Encoded JWT ID token: " + response.credential);
|
|
28
|
+
// Send this token to your backend for verification
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
await googleLoginService.init({
|
|
32
|
+
callback: handleCredentialResponse,
|
|
33
|
+
autoSelect: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 2. Render Button
|
|
37
|
+
googleLoginService.renderButton("google-btn-container", {
|
|
38
|
+
theme: "outline",
|
|
39
|
+
size: "large",
|
|
40
|
+
width: "100%",
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## HTML
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<div id="google-btn-container"></div>
|
|
48
|
+
```
|