mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-07 19:40:20 +00:00
refactor app configuration and improve Google OAuth handling; update .gitignore and enhance test documentation
This commit is contained in:
1 parent
cb74405f5f
commit
d865708e28
6 files changed
+79
-41
No files matched your search
@@ -1,2 +1,4 @@
|
|||||||
!/Ignore files/
|
!/Ignore files/
|
||||||
!/package-lock.json
|
!/package-lock.json
|
||||||
|
!/.expo/
|
||||||
|
!/.idea/
|
||||||
+20
-19
@@ -1,22 +1,23 @@
|
|||||||
/**
|
// Google Photos API Test
|
||||||
* Google Photos OAuth & API Test
|
//
|
||||||
*
|
// Usage:
|
||||||
* Usage:
|
// node TEST/testGooglePhotosAPI.js <ACCESS_TOKEN>
|
||||||
* node TEST/testGooglePhotosAPI.js <ACCESS_TOKEN>
|
// GOOGLE_ACCESS_TOKEN=ya29.xxx node TEST/testGooglePhotosAPI.js
|
||||||
* GOOGLE_ACCESS_TOKEN=ya29.xxx node TEST/testGooglePhotosAPI.js
|
//
|
||||||
*
|
// Getting a token from OAuth Playground (https://developers.google.com/oauthplayground):
|
||||||
* How to get a token for manual testing:
|
// 1. Click the gear icon → check "Use your own OAuth credentials"
|
||||||
* 1. Go to https://developers.google.com/oauthplayground
|
// 2. Enter your Client ID and Client Secret from Google Cloud Console
|
||||||
* 2. Select "Photos Library API v1" → photoslibrary scope
|
// 3. In the left panel select ALL of these scopes:
|
||||||
* 3. Authorise the API
|
// https://www.googleapis.com/auth/photoslibrary
|
||||||
* 4. Exchange the authorization code for tokens
|
// https://www.googleapis.com/auth/photoslibrary.appendonly
|
||||||
* 5. Copy the access token and pass it to this script
|
// https://www.googleapis.com/auth/userinfo.email
|
||||||
*
|
// https://www.googleapis.com/auth/userinfo.profile
|
||||||
* Tests:
|
// 4. Click "Authorize APIs" and sign in with your test Google account
|
||||||
* 1. Validates the token against the userinfo endpoint
|
// 5. Click "Exchange authorization code for tokens"
|
||||||
* 2. Lists media items from Google Photos
|
// 6. Copy the Access token (starts with ya29.) and pass it here
|
||||||
* 3. Lists albums from Google Photos
|
//
|
||||||
*/
|
// Note: if you only see 403 PERMISSION_DENIED, the token was issued without
|
||||||
|
// the photoslibrary scope — go back and re-authorise with all scopes above.
|
||||||
|
|
||||||
const API_BASE = 'https://photoslibrary.googleapis.com/v1';
|
const API_BASE = 'https://photoslibrary.googleapis.com/v1';
|
||||||
const USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo';
|
const USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"expo": {
|
"expo": {
|
||||||
"name": "Media-Aggregation-and-Sorting-System",
|
"name": "MASS Photo App",
|
||||||
"slug": "Media-Aggregation-and-Sorting-System",
|
"slug": "mass-expo",
|
||||||
|
"owner": "bobthebob",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/icon.png",
|
"icon": "./assets/icon.png",
|
||||||
"userInterfaceStyle": "light",
|
"userInterfaceStyle": "automatic",
|
||||||
"newArchEnabled": true,
|
|
||||||
"splash": {
|
"splash": {
|
||||||
"image": "./assets/splash-icon.png",
|
"image": "./assets/splash-icon.png",
|
||||||
"resizeMode": "contain",
|
"resizeMode": "contain",
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
"supportsTablet": true
|
"supportsTablet": true
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
|
"package": "com.bobthebob.massphotoapp",
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
"foregroundImage": "./assets/adaptive-icon.png",
|
"foregroundImage": "./assets/adaptive-icon.png",
|
||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
},
|
}
|
||||||
"edgeToEdgeEnabled": true
|
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"favicon": "./assets/favicon.png"
|
"favicon": "./assets/favicon.png"
|
||||||
@@ -28,6 +28,11 @@
|
|||||||
"scheme": "media-aggregation",
|
"scheme": "media-aggregation",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"expo-sqlite"
|
"expo-sqlite"
|
||||||
]
|
],
|
||||||
|
"extra": {
|
||||||
|
"eas": {
|
||||||
|
"projectId": "472bba14-1f7f-4cbe-b37b-3ec2c4077d03"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"cli": {
|
||||||
|
"version": ">= 16.0.0",
|
||||||
|
"appVersionSource": "local"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"development": {
|
||||||
|
"developmentClient": true,
|
||||||
|
"distribution": "internal",
|
||||||
|
"android": {
|
||||||
|
"buildType": "apk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"distribution": "internal",
|
||||||
|
"android": {
|
||||||
|
"buildType": "apk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"android": {
|
||||||
|
"buildType": "app-bundle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit": {
|
||||||
|
"production": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+10
-10
@@ -4,16 +4,20 @@ import { AuthServiceBase } from './AuthServiceBase';
|
|||||||
|
|
||||||
WebBrowser.maybeCompleteAuthSession();
|
WebBrowser.maybeCompleteAuthSession();
|
||||||
|
|
||||||
// Replace with your real Google Cloud OAuth Client ID (see ignorefiles/GOOGLE_OAUTH_SETUP.md)
|
|
||||||
const GOOGLE_CLIENT_ID = '313090284964-rgq1u7np6ogucu9o97s134n5nc6nj7kf.apps.googleusercontent.com';
|
const GOOGLE_CLIENT_ID = '313090284964-rgq1u7np6ogucu9o97s134n5nc6nj7kf.apps.googleusercontent.com';
|
||||||
|
|
||||||
|
// makeRedirectUri uses the scheme from app.json ("media-aggregation") to build
|
||||||
|
// the deep link URI that Android will route back into the app after OAuth.
|
||||||
|
// The resulting URI is: media-aggregation:// (registered in Google Cloud Console)
|
||||||
|
// For Expo Go dev testing the loopback http://localhost is used instead.
|
||||||
|
const REDIRECT_URI = AuthSession.makeRedirectUri({ scheme: 'media-aggregation' });
|
||||||
|
|
||||||
const GOOGLE_DISCOVERY = {
|
const GOOGLE_DISCOVERY = {
|
||||||
authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
|
authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
|
||||||
tokenEndpoint: 'https://oauth2.googleapis.com/token',
|
tokenEndpoint: 'https://oauth2.googleapis.com/token',
|
||||||
revocationEndpoint: 'https://oauth2.googleapis.com/revoke',
|
revocationEndpoint: 'https://oauth2.googleapis.com/revoke',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Google Photos Library API scopes — read, write, and user profile
|
|
||||||
const SCOPES = [
|
const SCOPES = [
|
||||||
'https://www.googleapis.com/auth/photoslibrary',
|
'https://www.googleapis.com/auth/photoslibrary',
|
||||||
'https://www.googleapis.com/auth/photoslibrary.appendonly',
|
'https://www.googleapis.com/auth/photoslibrary.appendonly',
|
||||||
@@ -48,15 +52,12 @@ class GoogleAUTH extends AuthServiceBase {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const redirectUri = AuthSession.makeRedirectUri({
|
console.log('OAuth redirect URI:', REDIRECT_URI);
|
||||||
scheme: 'media-aggregation',
|
|
||||||
preferLocalhost: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const authRequest = new AuthSession.AuthRequest({
|
const authRequest = new AuthSession.AuthRequest({
|
||||||
clientId: GOOGLE_CLIENT_ID,
|
clientId: GOOGLE_CLIENT_ID,
|
||||||
scopes: SCOPES,
|
scopes: SCOPES,
|
||||||
redirectUri,
|
redirectUri: REDIRECT_URI,
|
||||||
responseType: AuthSession.ResponseType.Code,
|
responseType: AuthSession.ResponseType.Code,
|
||||||
usePKCE: true,
|
usePKCE: true,
|
||||||
extraParams: { access_type: 'offline', prompt: 'consent' },
|
extraParams: { access_type: 'offline', prompt: 'consent' },
|
||||||
@@ -72,7 +73,7 @@ class GoogleAUTH extends AuthServiceBase {
|
|||||||
{
|
{
|
||||||
clientId: GOOGLE_CLIENT_ID,
|
clientId: GOOGLE_CLIENT_ID,
|
||||||
code: result.params.code,
|
code: result.params.code,
|
||||||
redirectUri,
|
redirectUri: REDIRECT_URI,
|
||||||
extraParams: { code_verifier: authRequest.codeVerifier },
|
extraParams: { code_verifier: authRequest.codeVerifier },
|
||||||
},
|
},
|
||||||
GOOGLE_DISCOVERY
|
GOOGLE_DISCOVERY
|
||||||
@@ -80,7 +81,6 @@ class GoogleAUTH extends AuthServiceBase {
|
|||||||
|
|
||||||
this.accessToken = tokenResponse.accessToken;
|
this.accessToken = tokenResponse.accessToken;
|
||||||
this.refreshToken = tokenResponse.refreshToken;
|
this.refreshToken = tokenResponse.refreshToken;
|
||||||
this.idToken = tokenResponse.idToken;
|
|
||||||
this.expiresAt = tokenResponse.issuedAt
|
this.expiresAt = tokenResponse.issuedAt
|
||||||
? (tokenResponse.issuedAt + (tokenResponse.expiresIn || 3600)) * 1000
|
? (tokenResponse.issuedAt + (tokenResponse.expiresIn || 3600)) * 1000
|
||||||
: Date.now() + 3600000;
|
: Date.now() + 3600000;
|
||||||
@@ -91,7 +91,7 @@ class GoogleAUTH extends AuthServiceBase {
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
accessToken: this.accessToken,
|
accessToken: this.accessToken,
|
||||||
refreshToken: this.refreshToken,
|
refreshToken: null,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -199,13 +199,13 @@ const styles = StyleSheet.create({
|
|||||||
centerContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 24 },
|
centerContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 24 },
|
||||||
loadingText: { marginTop: 12, fontSize: 14 },
|
loadingText: { marginTop: 12, fontSize: 14 },
|
||||||
// Filter chips
|
// Filter chips
|
||||||
filterRow: { paddingHorizontal: 12, paddingVertical: 8, gap: 8 },
|
filterRow: { paddingHorizontal: 12, paddingVertical: 6, gap: 6 },
|
||||||
chip: {
|
chip: {
|
||||||
flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, paddingVertical: 7,
|
flexDirection: 'row', alignItems: 'center', paddingHorizontal: 10, paddingVertical: 4,
|
||||||
borderRadius: 20, gap: 6,
|
borderRadius: 12, gap: 4,
|
||||||
},
|
},
|
||||||
chipIcon: { fontSize: 13 },
|
chipIcon: { fontSize: 11 },
|
||||||
chipText: { fontSize: 13, fontWeight: '500', color: '#3c4043' },
|
chipText: { fontSize: 12, fontWeight: '500', color: '#3c4043' },
|
||||||
// Empty
|
// Empty
|
||||||
emptyTitle: { fontSize: 18, fontWeight: '600', marginBottom: 6 },
|
emptyTitle: { fontSize: 18, fontWeight: '600', marginBottom: 6 },
|
||||||
emptyText: { fontSize: 13, textAlign: 'center', marginBottom: 20 },
|
emptyText: { fontSize: 13, textAlign: 'center', marginBottom: 20 },
|
||||||
|
|||||||
Reference in new issue
Block a user