From f83edfa12a00f582e31eebb0a285f22066bc8ef3 Mon Sep 17 00:00:00 2001 From: mudabbir-ahmad Date: Tue, 14 Apr 2026 19:20:38 +0100 Subject: [PATCH] feat: update backend URL and Google client IDs to use environment variables; add .env.example.txt --- .env.example.txt | 4 + .gitignore | 2 + README.md | 324 +++++++++-------------------------------- src/API/NASAPI.js | 2 +- src/AUTH/GoogleAUTH.js | 13 +- 5 files changed, 86 insertions(+), 259 deletions(-) create mode 100644 .env.example.txt diff --git a/.env.example.txt b/.env.example.txt new file mode 100644 index 0000000..3577223 --- /dev/null +++ b/.env.example.txt @@ -0,0 +1,4 @@ +EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID= +EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID= +EXPO_PUBLIC_BACKEND_URL=http://localhost:3001 + diff --git a/.gitignore b/.gitignore index f2eb683..c4961db 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ *.log *.lock *.tmp +.env +!.env.example.txt diff --git a/README.md b/README.md index cd4da28..9b218e3 100644 --- a/README.md +++ b/README.md @@ -1,277 +1,97 @@ # Media Aggregation and Sorting System +This is my final-year mobile app project. -A React Native/Expo application that aggregates photos and videos from multiple sources (Google Photos, iCloud, and Network-Attached Storage) into a unified photo library. +It is an Expo/React Native app that pulls media from multiple sources (device storage, Google Photos picker flow, and NAS) and shows everything in one timeline-style view. -## Features +## What This Project Does -### Core Functionality -- **User Authentication**: Local account creation and login with SQLite database storage -- **Multi-Service Integration**: - - 🔵 **Google Photos**: OAuth2 integration for Google Photos API - - 🍎 **iCloud Photos**: Apple iCloud account support (placeholder) - - 💾 **NAS Storage**: SFTP connection to Ubuntu servers for photo/video access - -- **Onboarding Flow**: - - Service selection (enable 1-3 services) - - Service configuration with credentials - - Default service selection for uploads +- Local user accounts (register/login) +- Local SQLite database on-device (`PhotosApp.db`) +- Service onboarding for: + - Google Photos + - NAS + - iCloud placeholder +- Unified media view with source badges +- Collections/albums screen +- Search screen +- Settings screen for theme, services, and backup options -- **Photo Gallery**: - - Unified photo library view combining all services - - Google Photos-styled grid layout - - Modal image viewer with metadata display - - Support for photos and videos +## Current Architecture -### Architecture +### App entry and navigation -#### Database Schema -- **users**: User account storage (username, email, password) -- **linked_accounts**: External service credentials and configurations -- **media**: Photo/video metadata from all sources -- **sync_logs**: Sync operation history and status +- Entrypoint is `App.js` +- `App.js` initializes the database, then mounts navigation +- Stack flow: + - `Login` + - `Register` + - `Onboarding` + - `MainTabs` + - `Settings` +- Bottom tabs in `MainTabs`: + - `Library` (`HomeScreen`) + - `Search` (`SearchScreen`) + - `Collections` (`AlbumsScreen`) -#### Authentication Services -All authentication services follow the `AuthServiceBase` interface for easy interchangeability: +### Data layer -1. **GoogleAUTH**: OAuth2 authentication with Google Photos API -2. **IOSAUTH**: Apple iCloud authentication (currently placeholder) -3. **NASAUTH**: SFTP connection to Ubuntu servers via SSH2 +Database setup lives in `src/services/DatabaseService.js`. -#### Navigation Flow -``` -Login/Register → Onboarding (Service Setup) → Home (Photo Gallery) -``` +Tables currently used: +- `users` +- `linked_accounts` +- `media` + +Passwords are hashed with SHA-256 via `src/services/CredentialEncryption.js`. +Linked account payloads are stored as Base64-encoded JSON strings. + +### Service/auth layer + +Auth services are in `src/AUTH/`: +- `GoogleAUTH.js` +- `NASAUTH.js` +- `IOSAUTH.js` (placeholder) + +Common interface base: `src/AUTH/AuthServiceBase.js`. + +API client wrappers are in `src/API/`. + +### Backend (NAS side) + +There is a separate Node/Express backend in `ServerFiles/server.js`. + +It handles NAS connection and file operations for the mobile app. ## Project Structure -``` +```text Media-Aggregation-and-Sorting-System/ +├── App.js +├── app.json +├── package.json ├── src/ +│ ├── API/ │ ├── AUTH/ -│ │ ├── AuthServiceBase.js # Abstract auth service interface -│ │ ├── GoogleAUTH.js # Google Photos authentication -│ │ ├── IOSAUTH.js # iCloud authentication -│ │ └── NASAUTH.js # NAS SFTP authentication -│ ├── screens/ -│ │ ├── LoginScreen.js # Login form -│ │ ├── RegisterScreen.js # Registration form -│ │ ├── OnboardingScreen.js # Service setup wizard -│ │ └── HomeScreen.js # Main photo gallery -│ ├── services/ -│ │ └── DatabaseService.js # SQLite database operations │ ├── components/ -│ └── API/ -├── App.js # Navigation setup -├── app.json # Expo configuration -├── package.json # Dependencies -└── README.md +│ ├── screens/ +│ └── services/ +├── ServerFiles/ +│ ├── server.js +│ └── package.json +└── TEST/ ``` -## Installation - -### Prerequisites -- Node.js and npm -- Expo CLI: `npm install -g expo-cli` -- iOS Simulator or Android Emulator (or physical device with Expo Go) - -### Setup Steps - -1. **Clone and Install Dependencies** - ```bash - cd Media-Aggregation-and-Sorting-System - npm install - ``` - -2. **Start the Development Server** - ```bash - npm start - ``` - -3. **Run on Device/Emulator** - - iOS: Press `i` in the terminal - - Android: Press `a` in the terminal - - Physical Device: Scan QR code with Expo Go app - -## Usage - -### Creating an Account -1. Open the app → Select "Register here" -2. Enter username, email, and password (min 6 characters) -3. Confirm password and create account - -### Login -1. Enter registered email and password -2. Click "Login" - -### Onboarding (Service Configuration) -**Step 1: Select Services** -- Toggle which services to enable (1-3 maximum) -- Google Photos, iCloud, and/or NAS - -**Step 2: Configure Services** -- **Google Photos**: Tap to authenticate with Google (OAuth flow) -- **iCloud**: Tap to authenticate with Apple (OAuth flow) -- **NAS**: Enter connection details: - - Server address (IP or hostname) - - SSH port (default: 22) - - Username - - Password - - Photos folder path - - Test connection before proceeding - -**Step 3: Choose Default Service** -- Select which service to use by default for uploads - -### Photo Gallery -- View all photos/videos from connected services -- Service badges at top show connected accounts -- ★ indicates the default service -- Tap any photo to view in enlarged modal -- Modal shows metadata: title, description, source, type, date - -## API Integration (TODO) - -### Google Photos Implementation -- Replace placeholder `authenticate()` with actual Google Sign-In -- Implement OAuth2 token exchange -- Add `/photoslibrary/v1/mediaItems:search` API calls -- Handle pagination and filtering - -### iCloud Implementation -- Integrate Apple Sign-In -- Implement CloudKit API for photo access -- Handle iCloud authentication - -### NAS Implementation -- Replace SSH2 placeholders with actual library implementation -- Implement SFTP file listing and download -- Add file upload functionality -- Handle directory navigation - -## Configuration - -### Environment Variables (Optional) -Create a `.env` file for sensitive configuration: -```env -GOOGLE_CLIENT_ID=your_client_id -GOOGLE_CLIENT_SECRET=your_client_secret -NAS_DEFAULT_PORT=22 +## Running the Project +Useful scripts: +```bash +expo run:android ``` -### Database -- SQLite database is automatically created on first run -- Location: Device's app-specific directory -- Filename: `PhotosApp.db` -## Authentication Service Pattern +### NAS backend -Each service implements the same interface: - -```javascript -class AuthService extends AuthServiceBase { - async authenticate(credentials) { } - async getAccessToken() { } - async refreshToken() { } - async revokeAccess() { } - async getUserInfo() { } - // Service-specific methods -} -``` - -### Easy Interchangeability -To swap or extend authentication: - -1. Create new class extending `AuthServiceBase` -2. Implement all abstract methods -3. Update the service type in `DatabaseService.linkAccount()` -4. Update `OnboardingScreen` UI if needed - -## Data Flow - -``` -Login/Register - ↓ -Authentication → Save to SQLite - ↓ -Onboarding → Service Selection - ↓ -Service Auth → Store Credentials - ↓ -Home Screen → Fetch from Services - ↓ -Display in Gallery -``` - -## Security Considerations - -⚠️ **Current Implementation Warnings**: -- Passwords stored in plain text in SQLite (development only) -- NAS passwords stored in plain text (development only) -- OAuth tokens not persisted securely - -**For Production**: -- Use secure storage (Keychain/Keystore) -- Encrypt sensitive data in database -- Implement token refresh and expiration -- Use OAuth for all services -- Never store plain-text passwords - -## Dependencies - -### Core -- `react`: 19.1.0 -- `react-native`: 0.81.5 -- `expo`: ~54.0.31 - -### Navigation -- `@react-navigation/native`: ^6.1.17 -- `@react-navigation/native-stack`: ^6.9.26 -- `react-native-screens`: ^3.31.1 -- `react-native-safe-area-context`: ^4.10.5 -- `react-native-gesture-handler`: ^2.16.1 - -### Database & Storage -- `react-native-sqlite-storage`: ^6.0.0 -- `expo-file-system`: ~17.0.1 -- `expo-media-library`: ~15.9.0 - -### Network -- `ssh2`: ^1.15.0 -- `react-native-ssh2`: ^1.0.0 - -### Media -- `expo-image-picker`: ~14.7.1 - -## Troubleshooting - -### Database Initialization Error -``` -Solution: Clear app cache/reinstall -npx expo prebuild --clean +```bash +cd ./ServerFiles +npm i npm start ``` - -### Navigation Issues -Ensure all screens are properly exported and imported in App.js - -### Service Connection Failures -- Check network connectivity -- Verify API credentials -- Check NAS server accessibility (port 22 open) -- Review console logs for detailed error messages - -## Future Enhancements - -- [ ] Implement actual Google Photos API integration -- [ ] Implement actual iCloud Photos integration -- [ ] Complete SFTP functionality for NAS -- [ ] Photo upload to default service -- [ ] Photo search and filtering -- [ ] Album management -- [ ] Automatic sync scheduling -- [ ] Cloud backup -- [ ] Photo sharing features -- [ ] Advanced metadata editing -- [ ] Duplicate detection -- [ ] Smart album creation -- [ ] Facial recognition (optional) \ No newline at end of file diff --git a/src/API/NASAPI.js b/src/API/NASAPI.js index 867d099..69eff9a 100644 --- a/src/API/NASAPI.js +++ b/src/API/NASAPI.js @@ -1,6 +1,6 @@ import axios from 'axios'; -const DEFAULT_BACKEND_URL = 'http://192.168.1.100:3001'; +const DEFAULT_BACKEND_URL = process.env.EXPO_PUBLIC_BACKEND_URL || 'http://localhost:3001'; class NASAPI { constructor(backendUrl = DEFAULT_BACKEND_URL) { diff --git a/src/AUTH/GoogleAUTH.js b/src/AUTH/GoogleAUTH.js index 25102c5..b4ff691 100644 --- a/src/AUTH/GoogleAUTH.js +++ b/src/AUTH/GoogleAUTH.js @@ -7,8 +7,9 @@ import { logError } from '../services/ErrorHandler'; WebBrowser.maybeCompleteAuthSession(); -const ANDROID_CLIENT_ID = '313090284964-018uf4v1bmmrra09lvmpnuug2qs09047.apps.googleusercontent.com'; -const IOS_CLIENT_ID = '313090284964-luka809nuc66bb4cg4gameovtqg8uoj1.apps.googleusercontent.com'; +const ANDROID_CLIENT_ID = process.env.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID || ''; +const IOS_CLIENT_ID = process.env.EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID || ''; +const ACTIVE_CLIENT_ID = ANDROID_CLIENT_ID || IOS_CLIENT_ID; const GOOGLE_DISCOVERY = { authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth', @@ -39,7 +40,7 @@ class GoogleAUTH extends AuthServiceBase { } async authenticate() { - if (ANDROID_CLIENT_ID === 'REPLACE_WITH_ANDROID_CLIENT_ID.apps.googleusercontent.com') { + if (!ACTIVE_CLIENT_ID) { console.warn('Google Auth: no Android client ID — using placeholder mode'); this.accessToken = 'placeholder_google_token_' + Date.now(); this.refreshToken = 'placeholder_refresh_' + Date.now(); @@ -57,7 +58,7 @@ class GoogleAUTH extends AuthServiceBase { console.log('Requesting scopes:', SCOPES.join(' ')); const authRequest = new AuthSession.AuthRequest({ - clientId: ANDROID_CLIENT_ID, + clientId: ACTIVE_CLIENT_ID, scopes: SCOPES, redirectUri: REDIRECT_URI, responseType: AuthSession.ResponseType.Code, @@ -77,7 +78,7 @@ class GoogleAUTH extends AuthServiceBase { } const tokenBody = new URLSearchParams({ - client_id: ANDROID_CLIENT_ID, + client_id: ACTIVE_CLIENT_ID, code: result.params.code, code_verifier: authRequest.codeVerifier, grant_type: 'authorization_code', @@ -135,7 +136,7 @@ class GoogleAUTH extends AuthServiceBase { async refreshAccessToken() { const body = new URLSearchParams({ - client_id: ANDROID_CLIENT_ID, + client_id: ACTIVE_CLIENT_ID, refresh_token: this.refreshToken, grant_type: 'refresh_token', });