mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-08 03:40:20 +00:00
added NAS configuration file; updated MediaViewer to support thumbnail carousel; improved error handling in NASAPI and NASAUTH; refactored various comments for clarity
This commit is contained in:
1 parent
9519b32d68
commit
f8e11fc368
27 files changed
+1036
-558
No files matched your search
@@ -1,7 +1,4 @@
|
||||
/**
|
||||
* Abstract base class for authentication services
|
||||
* This provides a common interface for different authentication providers
|
||||
*/
|
||||
// Abstract base class for authentication services — provides a common interface for all providers
|
||||
export class AuthServiceBase {
|
||||
constructor() {
|
||||
if (new.target === AuthServiceBase) {
|
||||
@@ -9,26 +6,11 @@ export class AuthServiceBase {
|
||||
}
|
||||
}
|
||||
|
||||
async authenticate() {
|
||||
throw new Error('authenticate() must be implemented');
|
||||
}
|
||||
|
||||
async getAccessToken() {
|
||||
throw new Error('getAccessToken() must be implemented');
|
||||
}
|
||||
|
||||
async refreshToken() {
|
||||
throw new Error('refreshToken() must be implemented');
|
||||
}
|
||||
|
||||
async revokeAccess() {
|
||||
throw new Error('revokeAccess() must be implemented');
|
||||
}
|
||||
|
||||
async getUserInfo() {
|
||||
throw new Error('getUserInfo() must be implemented');
|
||||
}
|
||||
async authenticate() { throw new Error('authenticate() must be implemented'); }
|
||||
async getAccessToken() { throw new Error('getAccessToken() must be implemented'); }
|
||||
async refreshToken() { throw new Error('refreshToken() must be implemented'); }
|
||||
async revokeAccess() { throw new Error('revokeAccess() must be implemented'); }
|
||||
async getUserInfo() { throw new Error('getUserInfo() must be implemented'); }
|
||||
}
|
||||
|
||||
export default AuthServiceBase;
|
||||
|
||||
+12
-19
@@ -4,7 +4,7 @@ import { AuthServiceBase } from './AuthServiceBase';
|
||||
|
||||
WebBrowser.maybeCompleteAuthSession();
|
||||
|
||||
// TODO: Replace with your real Google Cloud OAuth Client ID
|
||||
// Replace with your real Google Cloud OAuth Client ID (see ignorefiles/GOOGLE_OAUTH_SETUP.md)
|
||||
const GOOGLE_CLIENT_ID = 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com';
|
||||
|
||||
const GOOGLE_DISCOVERY = {
|
||||
@@ -13,7 +13,7 @@ const GOOGLE_DISCOVERY = {
|
||||
revocationEndpoint: 'https://oauth2.googleapis.com/revoke',
|
||||
};
|
||||
|
||||
// Google Photos Library API scopes - read and write
|
||||
// Google Photos Library API scopes — read, write, and user profile
|
||||
const SCOPES = [
|
||||
'https://www.googleapis.com/auth/photoslibrary',
|
||||
'https://www.googleapis.com/auth/photoslibrary.appendonly',
|
||||
@@ -31,19 +31,15 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
this.idToken = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with Google using expo-auth-session
|
||||
* Falls back to placeholder tokens when no real client ID is set
|
||||
*/
|
||||
// Authenticate via Google OAuth; uses placeholder tokens when no real client ID is configured
|
||||
async authenticate() {
|
||||
try {
|
||||
if (GOOGLE_CLIENT_ID === 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com') {
|
||||
console.log('Google Auth: Using placeholder mode (no client ID configured)');
|
||||
console.log('Google Auth: placeholder mode (no client ID configured)');
|
||||
this.accessToken = 'placeholder_google_token_' + Date.now();
|
||||
this.refreshToken = 'placeholder_refresh_' + Date.now();
|
||||
this.expiresAt = Date.now() + 3600000;
|
||||
this.email = 'google-user@placeholder.com';
|
||||
|
||||
return {
|
||||
success: true,
|
||||
accessToken: this.accessToken,
|
||||
@@ -52,8 +48,10 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
};
|
||||
}
|
||||
|
||||
// Real OAuth flow
|
||||
const redirectUri = AuthSession.makeRedirectUri({ preferLocalhost: true });
|
||||
const redirectUri = AuthSession.makeRedirectUri({
|
||||
scheme: 'media-aggregation',
|
||||
preferLocalhost: true,
|
||||
});
|
||||
|
||||
const authRequest = new AuthSession.AuthRequest({
|
||||
clientId: GOOGLE_CLIENT_ID,
|
||||
@@ -114,6 +112,7 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
}
|
||||
}
|
||||
|
||||
// Return a valid access token, refreshing if expired
|
||||
async getAccessToken() {
|
||||
if (this.accessToken && this.expiresAt > Date.now()) return this.accessToken;
|
||||
if (this.refreshToken) return await this.refreshAccessToken();
|
||||
@@ -165,9 +164,7 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
return info || { id: 'google_user', email: this.email || 'unknown', name: 'Google User' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch media items from Google Photos Library API
|
||||
*/
|
||||
// Fetch media items from the Google Photos Library API
|
||||
async getPhotos(pageSize = 50, pageToken = null) {
|
||||
try {
|
||||
const token = await this.getAccessToken();
|
||||
@@ -204,9 +201,7 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch albums from Google Photos Library API
|
||||
*/
|
||||
// Fetch album list from the Google Photos Library API
|
||||
async getAlbums(pageSize = 50, pageToken = null) {
|
||||
try {
|
||||
const token = await this.getAccessToken();
|
||||
@@ -227,9 +222,7 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload media to Google Photos Library
|
||||
*/
|
||||
// Upload a photo to Google Photos Library
|
||||
async uploadPhoto(filePath, filename, mimeType = 'image/jpeg') {
|
||||
try {
|
||||
const token = await this.getAccessToken();
|
||||
|
||||
+6
-25
@@ -1,10 +1,6 @@
|
||||
import { AuthServiceBase } from './AuthServiceBase';
|
||||
|
||||
/**
|
||||
* iCloud Photos Authentication Service
|
||||
* Placeholder - Apple does not provide a public iCloud Photos API.
|
||||
* This is kept for future integration if/when Apple provides API access.
|
||||
*/
|
||||
// iCloud Photos placeholder — Apple does not provide a public iCloud Photos API
|
||||
class IOSAUTH extends AuthServiceBase {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -12,13 +8,8 @@ class IOSAUTH extends AuthServiceBase {
|
||||
}
|
||||
|
||||
async authenticate() {
|
||||
console.log('iCloud Auth: Placeholder - no Apple API available');
|
||||
this.accessToken = 'icloud_placeholder_' + Date.now();
|
||||
return {
|
||||
success: true,
|
||||
accessToken: this.accessToken,
|
||||
message: 'iCloud placeholder - full integration pending',
|
||||
};
|
||||
return { success: true, accessToken: this.accessToken, message: 'iCloud placeholder — full integration pending' };
|
||||
}
|
||||
|
||||
async getAccessToken() {
|
||||
@@ -26,26 +17,16 @@ class IOSAUTH extends AuthServiceBase {
|
||||
throw new Error('Not authenticated with iCloud');
|
||||
}
|
||||
|
||||
async refreshToken() {
|
||||
return this.authenticate();
|
||||
}
|
||||
async refreshToken() { return this.authenticate(); }
|
||||
|
||||
async revokeAccess() {
|
||||
this.accessToken = null;
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async getUserInfo() {
|
||||
return { id: 'icloud_placeholder', name: 'iCloud User' };
|
||||
}
|
||||
|
||||
async getPhotos() {
|
||||
return { mediaItems: [], nextPageToken: null };
|
||||
}
|
||||
|
||||
async getAlbums() {
|
||||
return { albums: [], nextPageToken: null };
|
||||
}
|
||||
async getUserInfo() { return { id: 'icloud_placeholder', name: 'iCloud User' }; }
|
||||
async getPhotos() { return { mediaItems: [], nextPageToken: null }; }
|
||||
async getAlbums() { return { albums: [], nextPageToken: null }; }
|
||||
}
|
||||
|
||||
export default IOSAUTH;
|
||||
|
||||
+34
-154
@@ -1,12 +1,7 @@
|
||||
import { AuthServiceBase } from './AuthServiceBase';
|
||||
import NASAPI from '../API/NASAPI';
|
||||
|
||||
/**
|
||||
* NAS SFTP Authentication Service
|
||||
*
|
||||
* Uses NASAPI for direct NAS connection management
|
||||
* No backend server required
|
||||
*/
|
||||
// NAS SFTP authentication — connects to the backend server via NASAPI
|
||||
class NASAUTH extends AuthServiceBase {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -19,17 +14,9 @@ class NASAUTH extends AuthServiceBase {
|
||||
this.isConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with NAS/SFTP server
|
||||
*/
|
||||
async authenticate(host, port = 22, username, password, photoFolder = '/Photos') {
|
||||
try {
|
||||
// Validate input
|
||||
if (!host || !username || !password) {
|
||||
throw new Error('Host, username, and password are required');
|
||||
}
|
||||
|
||||
console.log(`Connecting to NAS at ${host}:${port}...`);
|
||||
if (!host || !username || !password) throw new Error('Host, username, and password are required');
|
||||
|
||||
this.host = host;
|
||||
this.port = port || 22;
|
||||
@@ -37,17 +24,10 @@ class NASAUTH extends AuthServiceBase {
|
||||
this.password = password;
|
||||
this.photoFolder = photoFolder;
|
||||
|
||||
// Connect via NASAPI
|
||||
const result = await this.nasAPI.connect(host, this.port, username, password, photoFolder);
|
||||
|
||||
await this.nasAPI.connect(host, this.port, username, password, photoFolder);
|
||||
this.isConnected = true;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `NAS configured: ${host}`,
|
||||
host: this.host,
|
||||
username: this.username,
|
||||
};
|
||||
return { success: true, message: `NAS configured: ${host}`, host: this.host, username: this.username };
|
||||
} catch (error) {
|
||||
console.error('NAS authentication error:', error.message);
|
||||
this.isConnected = false;
|
||||
@@ -55,167 +35,67 @@ class NASAUTH extends AuthServiceBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current access/connection status
|
||||
*/
|
||||
async getAccessToken() {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
return {
|
||||
host: this.host,
|
||||
username: this.username,
|
||||
isConnected: this.isConnected,
|
||||
};
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return { host: this.host, username: this.username, isConnected: this.isConnected };
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh connection (reconnect if needed)
|
||||
*/
|
||||
async refreshToken() {
|
||||
try {
|
||||
if (this.host && this.username && this.password) {
|
||||
return await this.authenticate(
|
||||
this.host,
|
||||
this.port,
|
||||
this.username,
|
||||
this.password,
|
||||
this.photoFolder
|
||||
);
|
||||
}
|
||||
throw new Error('No NAS credentials available for reconnection');
|
||||
} catch (error) {
|
||||
console.error('Connection refresh error:', error);
|
||||
throw error;
|
||||
if (this.host && this.username && this.password) {
|
||||
return await this.authenticate(this.host, this.port, this.username, this.password, this.photoFolder);
|
||||
}
|
||||
throw new Error('No NAS credentials available for reconnection');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from NAS
|
||||
*/
|
||||
async revokeAccess() {
|
||||
try {
|
||||
await this.nasAPI.disconnect();
|
||||
|
||||
this.isConnected = false;
|
||||
this.host = null;
|
||||
this.username = null;
|
||||
this.password = null;
|
||||
this.photoFolder = null;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'NAS connection closed',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Disconnect error:', error.message);
|
||||
this.isConnected = false;
|
||||
throw error;
|
||||
}
|
||||
this.isConnected = false;
|
||||
this.host = null;
|
||||
this.username = null;
|
||||
this.password = null;
|
||||
this.photoFolder = null;
|
||||
return { success: true, message: 'NAS connection closed' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get NAS server information
|
||||
*/
|
||||
async getUserInfo() {
|
||||
try {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
|
||||
return {
|
||||
host: this.host,
|
||||
username: this.username,
|
||||
port: this.port,
|
||||
photoFolder: this.photoFolder,
|
||||
connectedAt: new Date().toISOString(),
|
||||
serverType: 'NAS SFTP',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Get user info error:', error);
|
||||
throw error;
|
||||
}
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return {
|
||||
host: this.host,
|
||||
username: this.username,
|
||||
port: this.port,
|
||||
photoFolder: this.photoFolder,
|
||||
connectedAt: new Date().toISOString(),
|
||||
serverType: 'NAS SFTP',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* List directories on NAS
|
||||
*/
|
||||
async listDirectory(remotePath = '/') {
|
||||
try {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
|
||||
return await this.nasAPI.listDirectory(remotePath);
|
||||
} catch (error) {
|
||||
console.error('List directory error:', error);
|
||||
throw error;
|
||||
}
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return await this.nasAPI.listDirectory(remotePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file to NAS
|
||||
*/
|
||||
async uploadFile(localPath, remotePath) {
|
||||
try {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
|
||||
return await this.nasAPI.uploadFile(localPath, remotePath);
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
throw error;
|
||||
}
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return await this.nasAPI.uploadFile(localPath, remotePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get photos from designated NAS photo folder
|
||||
*/
|
||||
async getPhotos(photoFolder = null) {
|
||||
try {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
|
||||
const folder = photoFolder || this.photoFolder || '/Photos';
|
||||
return await this.nasAPI.getPhotos(folder);
|
||||
} catch (error) {
|
||||
console.error('Get photos error:', error);
|
||||
throw error;
|
||||
}
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return await this.nasAPI.getPhotos(photoFolder || this.photoFolder || '/Photos');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create directory on NAS
|
||||
*/
|
||||
async createDirectory(remotePath) {
|
||||
try {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
|
||||
return await this.nasAPI.createDirectory(remotePath);
|
||||
} catch (error) {
|
||||
console.error('Create directory error:', error);
|
||||
throw error;
|
||||
}
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return await this.nasAPI.createDirectory(remotePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file from NAS
|
||||
*/
|
||||
async deleteFile(remotePath) {
|
||||
try {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to NAS');
|
||||
}
|
||||
|
||||
return await this.nasAPI.deleteFile(remotePath);
|
||||
} catch (error) {
|
||||
console.error('Delete file error:', error);
|
||||
throw error;
|
||||
}
|
||||
if (!this.isConnected) throw new Error('Not connected to NAS');
|
||||
return await this.nasAPI.deleteFile(remotePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user