@oxyhq/services 5.10.4 → 5.10.6
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 +62 -14
- package/lib/commonjs/core/OxyServices.js +797 -5
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/index.js +8 -83
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +31 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +12 -12
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +2 -2
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/followStore.js +4 -4
- package/lib/commonjs/ui/stores/followStore.js.map +1 -1
- package/lib/commonjs/utils/s3FileManager.js +243 -0
- package/lib/commonjs/utils/s3FileManager.js.map +1 -0
- package/lib/commonjs/utils/s3FileManagerExample.js +407 -0
- package/lib/commonjs/utils/s3FileManagerExample.js.map +1 -0
- package/lib/commonjs/utils/s3FileManagerRN.js +274 -0
- package/lib/commonjs/utils/s3FileManagerRN.js.map +1 -0
- package/lib/module/core/OxyServices.js +796 -4
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/index.js +9 -24
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +12 -12
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +2 -2
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +1 -1
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js +1 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
- package/lib/module/ui/stores/followStore.js +4 -4
- package/lib/module/ui/stores/followStore.js.map +1 -1
- package/lib/module/utils/s3FileManager.js +237 -0
- package/lib/module/utils/s3FileManager.js.map +1 -0
- package/lib/module/utils/s3FileManagerExample.js +400 -0
- package/lib/module/utils/s3FileManagerExample.js.map +1 -0
- package/lib/module/utils/s3FileManagerRN.js +268 -0
- package/lib/module/utils/s3FileManagerRN.js.map +1 -0
- package/lib/typescript/core/OxyServices.d.ts +292 -3
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/index.d.ts +7 -16
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/s3FileManager.d.ts +81 -0
- package/lib/typescript/utils/s3FileManager.d.ts.map +1 -0
- package/lib/typescript/utils/s3FileManagerExample.d.ts +87 -0
- package/lib/typescript/utils/s3FileManagerExample.d.ts.map +1 -0
- package/lib/typescript/utils/s3FileManagerRN.d.ts +104 -0
- package/lib/typescript/utils/s3FileManagerRN.d.ts.map +1 -0
- package/package.json +3 -1
- package/src/core/OxyServices.ts +881 -46
- package/src/core/index.ts +9 -24
- package/src/index.ts +17 -1
- package/src/ui/screens/FileManagementScreen.tsx +12 -12
- package/src/ui/screens/ProfileScreen.tsx +3 -3
- package/src/ui/screens/SignInScreen.tsx +1 -1
- package/src/ui/screens/karma/KarmaCenterScreen.tsx +2 -2
- package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +1 -1
- package/src/ui/screens/karma/KarmaRulesScreen.tsx +1 -1
- package/src/ui/stores/followStore.ts +4 -4
- package/src/utils/s3FileManager.ts +281 -0
- package/src/utils/s3FileManagerExample.ts +432 -0
- package/src/utils/s3FileManagerRN.ts +322 -0
- package/lib/commonjs/core/OxyServicesMain.js +0 -51
- package/lib/commonjs/core/OxyServicesMain.js.map +0 -1
- package/lib/commonjs/core/analytics/AnalyticsService.js +0 -67
- package/lib/commonjs/core/analytics/AnalyticsService.js.map +0 -1
- package/lib/commonjs/core/auth/AuthService.js +0 -538
- package/lib/commonjs/core/auth/AuthService.js.map +0 -1
- package/lib/commonjs/core/devices/DeviceService.js +0 -61
- package/lib/commonjs/core/devices/DeviceService.js.map +0 -1
- package/lib/commonjs/core/files/FileService.js +0 -180
- package/lib/commonjs/core/files/FileService.js.map +0 -1
- package/lib/commonjs/core/karma/KarmaService.js +0 -100
- package/lib/commonjs/core/karma/KarmaService.js.map +0 -1
- package/lib/commonjs/core/locations/LocationService.js +0 -131
- package/lib/commonjs/core/locations/LocationService.js.map +0 -1
- package/lib/commonjs/core/payments/PaymentService.js +0 -124
- package/lib/commonjs/core/payments/PaymentService.js.map +0 -1
- package/lib/commonjs/core/users/UserService.js +0 -234
- package/lib/commonjs/core/users/UserService.js.map +0 -1
- package/lib/module/core/OxyServicesMain.js +0 -47
- package/lib/module/core/OxyServicesMain.js.map +0 -1
- package/lib/module/core/analytics/AnalyticsService.js +0 -62
- package/lib/module/core/analytics/AnalyticsService.js.map +0 -1
- package/lib/module/core/auth/AuthService.js +0 -533
- package/lib/module/core/auth/AuthService.js.map +0 -1
- package/lib/module/core/devices/DeviceService.js +0 -57
- package/lib/module/core/devices/DeviceService.js.map +0 -1
- package/lib/module/core/files/FileService.js +0 -175
- package/lib/module/core/files/FileService.js.map +0 -1
- package/lib/module/core/karma/KarmaService.js +0 -95
- package/lib/module/core/karma/KarmaService.js.map +0 -1
- package/lib/module/core/locations/LocationService.js +0 -127
- package/lib/module/core/locations/LocationService.js.map +0 -1
- package/lib/module/core/payments/PaymentService.js +0 -119
- package/lib/module/core/payments/PaymentService.js.map +0 -1
- package/lib/module/core/users/UserService.js +0 -230
- package/lib/module/core/users/UserService.js.map +0 -1
- package/lib/typescript/core/OxyServicesMain.d.ts +0 -33
- package/lib/typescript/core/OxyServicesMain.d.ts.map +0 -1
- package/lib/typescript/core/analytics/AnalyticsService.d.ts +0 -26
- package/lib/typescript/core/analytics/AnalyticsService.d.ts.map +0 -1
- package/lib/typescript/core/auth/AuthService.d.ts +0 -165
- package/lib/typescript/core/auth/AuthService.d.ts.map +0 -1
- package/lib/typescript/core/devices/DeviceService.d.ts +0 -20
- package/lib/typescript/core/devices/DeviceService.d.ts.map +0 -1
- package/lib/typescript/core/files/FileService.d.ts +0 -59
- package/lib/typescript/core/files/FileService.d.ts.map +0 -1
- package/lib/typescript/core/karma/KarmaService.d.ts +0 -50
- package/lib/typescript/core/karma/KarmaService.d.ts.map +0 -1
- package/lib/typescript/core/locations/LocationService.d.ts +0 -39
- package/lib/typescript/core/locations/LocationService.d.ts.map +0 -1
- package/lib/typescript/core/payments/PaymentService.d.ts +0 -50
- package/lib/typescript/core/payments/PaymentService.d.ts.map +0 -1
- package/lib/typescript/core/users/UserService.d.ts +0 -111
- package/lib/typescript/core/users/UserService.d.ts.map +0 -1
- package/src/core/OxyServicesMain.ts +0 -57
- package/src/core/analytics/AnalyticsService.ts +0 -64
- package/src/core/auth/AuthService.ts +0 -560
- package/src/core/devices/DeviceService.ts +0 -55
- package/src/core/files/FileService.ts +0 -198
- package/src/core/karma/KarmaService.ts +0 -104
- package/src/core/locations/LocationService.ts +0 -141
- package/src/core/payments/PaymentService.ts +0 -133
- package/src/core/users/UserService.ts +0 -241
package/src/core/index.ts
CHANGED
|
@@ -1,28 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OxyServices Core Module -
|
|
2
|
+
* OxyServices Core Module - Unified Architecture
|
|
3
3
|
*
|
|
4
|
-
* This module exports the
|
|
5
|
-
*
|
|
4
|
+
* This module exports the unified OxyServices class that provides all API functionality
|
|
5
|
+
* in one simple, easy-to-use interface.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Main OxyServices class (
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
// Individual service classes for focused usage
|
|
12
|
-
export { AuthService } from './auth/AuthService';
|
|
13
|
-
export { UserService } from './users/UserService';
|
|
14
|
-
export { PaymentService } from './payments/PaymentService';
|
|
15
|
-
export { KarmaService } from './karma/KarmaService';
|
|
16
|
-
export { FileService } from './files/FileService';
|
|
17
|
-
export { LocationService } from './locations/LocationService';
|
|
18
|
-
export { AnalyticsService } from './analytics/AnalyticsService';
|
|
19
|
-
export { DeviceService } from './devices/DeviceService';
|
|
20
|
-
|
|
21
|
-
// Base class for custom service extensions
|
|
22
|
-
export { OxyServices as BaseOxyServices } from './OxyServices';
|
|
23
|
-
|
|
24
|
-
// Constants
|
|
25
|
-
export { OXY_CLOUD_URL } from './files/FileService';
|
|
8
|
+
// Main OxyServices class (unified)
|
|
9
|
+
export { OxyServices } from './OxyServices';
|
|
10
|
+
export { OXY_CLOUD_URL } from './OxyServices';
|
|
26
11
|
|
|
27
12
|
// Re-export all models and types for convenience
|
|
28
13
|
export * from '../models/interfaces';
|
|
@@ -32,8 +17,8 @@ export * from '../models/session';
|
|
|
32
17
|
export { DeviceManager } from '../utils/deviceManager';
|
|
33
18
|
export type { DeviceFingerprint, StoredDeviceInfo } from '../utils/deviceManager';
|
|
34
19
|
|
|
35
|
-
// Import
|
|
36
|
-
import {
|
|
20
|
+
// Import for default export
|
|
21
|
+
import { OxyServices } from './OxyServices';
|
|
37
22
|
|
|
38
23
|
// Default export for backward compatibility
|
|
39
|
-
export default
|
|
24
|
+
export default OxyServices;
|
package/src/index.ts
CHANGED
|
@@ -94,4 +94,20 @@ export {
|
|
|
94
94
|
logPerformance
|
|
95
95
|
} from './utils/loggerUtils';
|
|
96
96
|
export * from './utils/asyncUtils';
|
|
97
|
-
export * from './utils/hookUtils';
|
|
97
|
+
export * from './utils/hookUtils';
|
|
98
|
+
|
|
99
|
+
// S3 File Management exports
|
|
100
|
+
export {
|
|
101
|
+
S3FileManager,
|
|
102
|
+
createS3FileManager
|
|
103
|
+
} from './utils/s3FileManager';
|
|
104
|
+
export {
|
|
105
|
+
S3FileManagerRN,
|
|
106
|
+
createS3FileManagerRN
|
|
107
|
+
} from './utils/s3FileManagerRN';
|
|
108
|
+
export type {
|
|
109
|
+
S3Config,
|
|
110
|
+
UploadOptions,
|
|
111
|
+
FileInfo
|
|
112
|
+
} from './utils/s3FileManager';
|
|
113
|
+
export type { RNFile } from './utils/s3FileManagerRN';
|
|
@@ -118,7 +118,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
118
118
|
setLoading(true);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
const response = await oxyServices.
|
|
121
|
+
const response = await oxyServices.listUserFiles(targetUserId);
|
|
122
122
|
setFiles(response.files || []);
|
|
123
123
|
} catch (error: any) {
|
|
124
124
|
console.error('Failed to load files:', error);
|
|
@@ -172,7 +172,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
172
172
|
await Promise.all(
|
|
173
173
|
photosToLoad.map(async (photo) => {
|
|
174
174
|
try {
|
|
175
|
-
const downloadUrl = oxyServices.
|
|
175
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
176
176
|
|
|
177
177
|
if (Platform.OS === 'web') {
|
|
178
178
|
const img = new (window as any).Image();
|
|
@@ -340,7 +340,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
340
340
|
console.log('Current user ID:', user?.id);
|
|
341
341
|
setDeleting(fileId);
|
|
342
342
|
|
|
343
|
-
const result = await oxyServices.
|
|
343
|
+
const result = await oxyServices.deleteFile(fileId);
|
|
344
344
|
console.log('Delete result:', result);
|
|
345
345
|
|
|
346
346
|
toast.success('File deleted successfully');
|
|
@@ -408,7 +408,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
408
408
|
console.log('Downloading file:', { fileId, filename });
|
|
409
409
|
|
|
410
410
|
// Use the public download URL method
|
|
411
|
-
const downloadUrl = oxyServices.
|
|
411
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(fileId);
|
|
412
412
|
console.log('Download URL:', downloadUrl);
|
|
413
413
|
|
|
414
414
|
try {
|
|
@@ -426,7 +426,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
426
426
|
console.warn('Link download failed, trying fetch method:', linkError);
|
|
427
427
|
|
|
428
428
|
// Method 2: Fallback to authenticated download
|
|
429
|
-
const blob = await oxyServices.
|
|
429
|
+
const blob = await oxyServices.getFileContentAsBlob(fileId);
|
|
430
430
|
const url = window.URL.createObjectURL(blob);
|
|
431
431
|
|
|
432
432
|
const link = document.createElement('a');
|
|
@@ -491,11 +491,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
491
491
|
file.contentType.startsWith('video/') ||
|
|
492
492
|
file.contentType.startsWith('audio/')) {
|
|
493
493
|
// For images, PDFs, videos, and audio, we'll use the URL directly
|
|
494
|
-
const downloadUrl = oxyServices.
|
|
494
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(file.id);
|
|
495
495
|
setFileContent(downloadUrl);
|
|
496
496
|
} else {
|
|
497
497
|
// For text files, get the content using authenticated request
|
|
498
|
-
const content = await oxyServices.
|
|
498
|
+
const content = await oxyServices.getFileContentAsText(file.id);
|
|
499
499
|
setFileContent(content);
|
|
500
500
|
}
|
|
501
501
|
} catch (error: any) {
|
|
@@ -532,7 +532,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
532
532
|
};
|
|
533
533
|
|
|
534
534
|
const renderSimplePhotoItem = useCallback((photo: FileMetadata, index: number) => {
|
|
535
|
-
const downloadUrl = oxyServices.
|
|
535
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
536
536
|
|
|
537
537
|
// Calculate photo item width based on actual container size from bottom sheet
|
|
538
538
|
let itemsPerRow = 3; // Default for mobile
|
|
@@ -598,7 +598,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
598
598
|
}, [oxyServices, containerWidth]);
|
|
599
599
|
|
|
600
600
|
const renderJustifiedPhotoItem = useCallback((photo: FileMetadata, width: number, height: number, isLast: boolean) => {
|
|
601
|
-
const downloadUrl = oxyServices.
|
|
601
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
602
602
|
|
|
603
603
|
return (
|
|
604
604
|
<TouchableOpacity
|
|
@@ -689,7 +689,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
689
689
|
{isImage && (
|
|
690
690
|
Platform.OS === 'web' ? (
|
|
691
691
|
<img
|
|
692
|
-
src={oxyServices.
|
|
692
|
+
src={oxyServices.getFileDownloadUrl(file.id)}
|
|
693
693
|
style={{
|
|
694
694
|
width: '100%',
|
|
695
695
|
height: '100%',
|
|
@@ -709,7 +709,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
709
709
|
/>
|
|
710
710
|
) : (
|
|
711
711
|
<Image
|
|
712
|
-
source={{ uri: oxyServices.
|
|
712
|
+
source={{ uri: oxyServices.getFileDownloadUrl(file.id) }}
|
|
713
713
|
style={styles.previewImage}
|
|
714
714
|
resizeMode="cover"
|
|
715
715
|
onError={() => {
|
|
@@ -817,7 +817,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
817
817
|
};
|
|
818
818
|
|
|
819
819
|
const renderPhotoItem = (photo: FileMetadata, index: number) => {
|
|
820
|
-
const downloadUrl = oxyServices.
|
|
820
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
821
821
|
|
|
822
822
|
// Calculate photo item width based on actual container size from bottom sheet
|
|
823
823
|
let itemsPerRow = 3; // Default for mobile
|
|
@@ -61,7 +61,7 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ userId, username, theme,
|
|
|
61
61
|
|
|
62
62
|
// Load user profile and karma total
|
|
63
63
|
Promise.all([
|
|
64
|
-
oxyServices.
|
|
64
|
+
oxyServices.getUserById(userId).catch((err: any) => {
|
|
65
65
|
console.error('getUserById error:', err);
|
|
66
66
|
// If this is the current user and the API call fails, use current user data as fallback
|
|
67
67
|
if (currentUser && currentUser.id === userId) {
|
|
@@ -70,8 +70,8 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ userId, username, theme,
|
|
|
70
70
|
}
|
|
71
71
|
throw err;
|
|
72
72
|
}),
|
|
73
|
-
oxyServices.
|
|
74
|
-
oxyServices.
|
|
73
|
+
oxyServices.getUserKarmaTotal ?
|
|
74
|
+
oxyServices.getUserKarmaTotal(userId).catch((err: any) => {
|
|
75
75
|
console.warn('getUserKarmaTotal error:', err);
|
|
76
76
|
return { total: undefined };
|
|
77
77
|
}) :
|
|
@@ -136,7 +136,7 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
136
136
|
|
|
137
137
|
try {
|
|
138
138
|
// First check if username exists by trying to get profile
|
|
139
|
-
const profile = await oxyServices.
|
|
139
|
+
const profile = await oxyServices.getProfileByUsername(usernameToValidate);
|
|
140
140
|
|
|
141
141
|
if (profile) {
|
|
142
142
|
const profileData = {
|
|
@@ -38,8 +38,8 @@ const KarmaCenterScreen: React.FC<BaseScreenProps> = ({
|
|
|
38
38
|
setIsLoading(true);
|
|
39
39
|
setError(null);
|
|
40
40
|
Promise.all([
|
|
41
|
-
oxyServices.
|
|
42
|
-
oxyServices.
|
|
41
|
+
oxyServices.getUserKarmaTotal(user.id),
|
|
42
|
+
oxyServices.getUserKarmaHistory(user.id, 20, 0),
|
|
43
43
|
])
|
|
44
44
|
.then(([totalRes, historyRes]) => {
|
|
45
45
|
setKarmaTotal(totalRes.total);
|
|
@@ -19,7 +19,7 @@ const KarmaLeaderboardScreen: React.FC<BaseScreenProps> = ({ goBack, theme, navi
|
|
|
19
19
|
useEffect(() => {
|
|
20
20
|
setIsLoading(true);
|
|
21
21
|
setError(null);
|
|
22
|
-
oxyServices.
|
|
22
|
+
oxyServices.getKarmaLeaderboard()
|
|
23
23
|
.then((data: any) => setLeaderboard(Array.isArray(data) ? data : []))
|
|
24
24
|
.catch((err: any) => setError(err.message || 'Failed to load leaderboard'))
|
|
25
25
|
.finally(() => setIsLoading(false));
|
|
@@ -18,7 +18,7 @@ const KarmaRulesScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
|
|
|
18
18
|
useEffect(() => {
|
|
19
19
|
setIsLoading(true);
|
|
20
20
|
setError(null);
|
|
21
|
-
oxyServices.
|
|
21
|
+
oxyServices.getKarmaRules()
|
|
22
22
|
.then((data: any) => setRules(Array.isArray(data) ? data : []))
|
|
23
23
|
.catch((err: any) => setError(err.message || 'Failed to load rules'))
|
|
24
24
|
.finally(() => setIsLoading(false));
|
|
@@ -53,7 +53,7 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
|
|
|
53
53
|
errors: { ...state.errors, [userId]: null },
|
|
54
54
|
}));
|
|
55
55
|
try {
|
|
56
|
-
const response = await oxyServices.
|
|
56
|
+
const response = await oxyServices.getFollowStatus(userId);
|
|
57
57
|
set((state: FollowState) => ({
|
|
58
58
|
followingUsers: { ...state.followingUsers, [userId]: response.isFollowing },
|
|
59
59
|
fetchingUsers: { ...state.fetchingUsers, [userId]: false },
|
|
@@ -75,10 +75,10 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
|
|
|
75
75
|
let response: any;
|
|
76
76
|
let newFollowState;
|
|
77
77
|
if (isCurrentlyFollowing) {
|
|
78
|
-
response = await oxyServices.
|
|
78
|
+
response = await oxyServices.unfollowUser(userId);
|
|
79
79
|
newFollowState = false;
|
|
80
80
|
} else {
|
|
81
|
-
response = await oxyServices.
|
|
81
|
+
response = await oxyServices.followUser(userId);
|
|
82
82
|
newFollowState = true;
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -158,7 +158,7 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
|
|
|
158
158
|
loadingCounts: { ...state.loadingCounts, [userId]: true },
|
|
159
159
|
}));
|
|
160
160
|
try {
|
|
161
|
-
const user = await oxyServices.
|
|
161
|
+
const user = await oxyServices.getUserById(userId);
|
|
162
162
|
if (user && user._count) {
|
|
163
163
|
set((state: FollowState) => ({
|
|
164
164
|
followerCounts: {
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
|
|
2
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
3
|
+
|
|
4
|
+
export interface S3Config {
|
|
5
|
+
region: string;
|
|
6
|
+
accessKeyId: string;
|
|
7
|
+
secretAccessKey: string;
|
|
8
|
+
bucketName: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UploadOptions {
|
|
12
|
+
contentType?: string;
|
|
13
|
+
metadata?: Record<string, string>;
|
|
14
|
+
publicRead?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface FileInfo {
|
|
18
|
+
key: string;
|
|
19
|
+
size: number;
|
|
20
|
+
lastModified: Date;
|
|
21
|
+
contentType?: string;
|
|
22
|
+
metadata?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class S3FileManager {
|
|
26
|
+
private s3Client: S3Client;
|
|
27
|
+
private bucketName: string;
|
|
28
|
+
|
|
29
|
+
constructor(config: S3Config) {
|
|
30
|
+
this.s3Client = new S3Client({
|
|
31
|
+
region: config.region,
|
|
32
|
+
credentials: {
|
|
33
|
+
accessKeyId: config.accessKeyId,
|
|
34
|
+
secretAccessKey: config.secretAccessKey,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
this.bucketName = config.bucketName;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Upload a file to S3
|
|
42
|
+
*/
|
|
43
|
+
async uploadFile(
|
|
44
|
+
key: string,
|
|
45
|
+
file: File | Buffer | string,
|
|
46
|
+
options: UploadOptions = {}
|
|
47
|
+
): Promise<string> {
|
|
48
|
+
let body: Buffer | string;
|
|
49
|
+
let contentType = options.contentType;
|
|
50
|
+
|
|
51
|
+
if (file instanceof File) {
|
|
52
|
+
body = await this.fileToBuffer(file);
|
|
53
|
+
contentType = contentType || file.type;
|
|
54
|
+
} else if (typeof file === 'string') {
|
|
55
|
+
body = file;
|
|
56
|
+
contentType = contentType || 'text/plain';
|
|
57
|
+
} else {
|
|
58
|
+
body = file;
|
|
59
|
+
contentType = contentType || 'application/octet-stream';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const command = new PutObjectCommand({
|
|
63
|
+
Bucket: this.bucketName,
|
|
64
|
+
Key: key,
|
|
65
|
+
Body: body,
|
|
66
|
+
ContentType: contentType,
|
|
67
|
+
Metadata: options.metadata,
|
|
68
|
+
ACL: options.publicRead ? 'public-read' : 'private',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await this.s3Client.send(command);
|
|
72
|
+
return `https://${this.bucketName}.s3.amazonaws.com/${key}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Download a file from S3
|
|
77
|
+
*/
|
|
78
|
+
async downloadFile(key: string): Promise<Buffer> {
|
|
79
|
+
const command = new GetObjectCommand({
|
|
80
|
+
Bucket: this.bucketName,
|
|
81
|
+
Key: key,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const response = await this.s3Client.send(command);
|
|
85
|
+
|
|
86
|
+
if (!response.Body) {
|
|
87
|
+
throw new Error('File not found or empty');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Convert stream to buffer
|
|
91
|
+
const chunks: Uint8Array[] = [];
|
|
92
|
+
const reader = response.Body.transformToWebStream().getReader();
|
|
93
|
+
|
|
94
|
+
while (true) {
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
if (done) break;
|
|
97
|
+
chunks.push(value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return Buffer.concat(chunks);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Delete a file from S3
|
|
105
|
+
*/
|
|
106
|
+
async deleteFile(key: string): Promise<void> {
|
|
107
|
+
const command = new DeleteObjectCommand({
|
|
108
|
+
Bucket: this.bucketName,
|
|
109
|
+
Key: key,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await this.s3Client.send(command);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Generate a presigned URL for file upload
|
|
117
|
+
*/
|
|
118
|
+
async getPresignedUploadUrl(
|
|
119
|
+
key: string,
|
|
120
|
+
contentType: string,
|
|
121
|
+
expiresIn: number = 3600
|
|
122
|
+
): Promise<string> {
|
|
123
|
+
const command = new PutObjectCommand({
|
|
124
|
+
Bucket: this.bucketName,
|
|
125
|
+
Key: key,
|
|
126
|
+
ContentType: contentType,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return getSignedUrl(this.s3Client, command, { expiresIn });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generate a presigned URL for file download
|
|
134
|
+
*/
|
|
135
|
+
async getPresignedDownloadUrl(
|
|
136
|
+
key: string,
|
|
137
|
+
expiresIn: number = 3600
|
|
138
|
+
): Promise<string> {
|
|
139
|
+
const command = new GetObjectCommand({
|
|
140
|
+
Bucket: this.bucketName,
|
|
141
|
+
Key: key,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return getSignedUrl(this.s3Client, command, { expiresIn });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* List files in a directory
|
|
149
|
+
*/
|
|
150
|
+
async listFiles(prefix: string = '', maxKeys: number = 1000): Promise<FileInfo[]> {
|
|
151
|
+
const command = new ListObjectsV2Command({
|
|
152
|
+
Bucket: this.bucketName,
|
|
153
|
+
Prefix: prefix,
|
|
154
|
+
MaxKeys: maxKeys,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const response = await this.s3Client.send(command);
|
|
158
|
+
|
|
159
|
+
if (!response.Contents) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return response.Contents.map((item) => ({
|
|
164
|
+
key: item.Key!,
|
|
165
|
+
size: item.Size || 0,
|
|
166
|
+
lastModified: item.LastModified!,
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if a file exists
|
|
172
|
+
*/
|
|
173
|
+
async fileExists(key: string): Promise<boolean> {
|
|
174
|
+
try {
|
|
175
|
+
const command = new GetObjectCommand({
|
|
176
|
+
Bucket: this.bucketName,
|
|
177
|
+
Key: key,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
await this.s3Client.send(command);
|
|
181
|
+
return true;
|
|
182
|
+
} catch (error: any) {
|
|
183
|
+
if (error.name === 'NoSuchKey') {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get file metadata
|
|
192
|
+
*/
|
|
193
|
+
async getFileMetadata(key: string): Promise<FileInfo | null> {
|
|
194
|
+
try {
|
|
195
|
+
const command = new GetObjectCommand({
|
|
196
|
+
Bucket: this.bucketName,
|
|
197
|
+
Key: key,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const response = await this.s3Client.send(command);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
key,
|
|
204
|
+
size: parseInt(response.ContentLength?.toString() || '0'),
|
|
205
|
+
lastModified: response.LastModified!,
|
|
206
|
+
contentType: response.ContentType,
|
|
207
|
+
metadata: response.Metadata,
|
|
208
|
+
};
|
|
209
|
+
} catch (error: any) {
|
|
210
|
+
if (error.name === 'NoSuchKey') {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Convert File object to Buffer (for React Native compatibility)
|
|
219
|
+
*/
|
|
220
|
+
private async fileToBuffer(file: File): Promise<Buffer> {
|
|
221
|
+
return new Promise((resolve, reject) => {
|
|
222
|
+
const reader = new FileReader();
|
|
223
|
+
reader.onload = () => {
|
|
224
|
+
if (reader.result instanceof ArrayBuffer) {
|
|
225
|
+
resolve(Buffer.from(reader.result));
|
|
226
|
+
} else {
|
|
227
|
+
reject(new Error('Failed to read file'));
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
reader.onerror = () => reject(reader.error);
|
|
231
|
+
reader.readAsArrayBuffer(file);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Upload multiple files
|
|
237
|
+
*/
|
|
238
|
+
async uploadMultipleFiles(
|
|
239
|
+
files: Array<{ key: string; file: File | Buffer | string; options?: UploadOptions }>
|
|
240
|
+
): Promise<string[]> {
|
|
241
|
+
const uploadPromises = files.map(({ key, file, options }) =>
|
|
242
|
+
this.uploadFile(key, file, options)
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
return Promise.all(uploadPromises);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Delete multiple files
|
|
250
|
+
*/
|
|
251
|
+
async deleteMultipleFiles(keys: string[]): Promise<void> {
|
|
252
|
+
const deletePromises = keys.map(key => this.deleteFile(key));
|
|
253
|
+
await Promise.all(deletePromises);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Copy file from one key to another
|
|
258
|
+
*/
|
|
259
|
+
async copyFile(sourceKey: string, destinationKey: string): Promise<void> {
|
|
260
|
+
const command = new PutObjectCommand({
|
|
261
|
+
Bucket: this.bucketName,
|
|
262
|
+
Key: destinationKey,
|
|
263
|
+
Body: await this.downloadFile(sourceKey),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await this.s3Client.send(command);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Move file (copy + delete)
|
|
271
|
+
*/
|
|
272
|
+
async moveFile(sourceKey: string, destinationKey: string): Promise<void> {
|
|
273
|
+
await this.copyFile(sourceKey, destinationKey);
|
|
274
|
+
await this.deleteFile(sourceKey);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Export a factory function for easier configuration
|
|
279
|
+
export function createS3FileManager(config: S3Config): S3FileManager {
|
|
280
|
+
return new S3FileManager(config);
|
|
281
|
+
}
|