mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-07 19: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,24 +1,19 @@
|
||||
import * as Crypto from 'expo-crypto';
|
||||
|
||||
/**
|
||||
* Credential Encryption Service
|
||||
*
|
||||
* Uses expo-crypto for password hashing (SHA-256)
|
||||
* Uses base64 encoding for storing account data objects in the database
|
||||
* Handles password hashing (SHA-256) and base64 encoding of account data objects
|
||||
*/
|
||||
class CredentialEncryption {
|
||||
/**
|
||||
* Hash a password using SHA-256 via expo-crypto
|
||||
* Returns a hex digest string
|
||||
* Hash a password with SHA-256 and return a hex digest
|
||||
*/
|
||||
async hashPassword(password) {
|
||||
try {
|
||||
if (!password) throw new Error('Password is required');
|
||||
const digest = await Crypto.digestStringAsync(
|
||||
return await Crypto.digestStringAsync(
|
||||
Crypto.CryptoDigestAlgorithm.SHA256,
|
||||
password
|
||||
);
|
||||
return digest;
|
||||
} catch (error) {
|
||||
console.error('Password hashing error:', error);
|
||||
throw new Error('Failed to hash password: ' + error.message);
|
||||
@@ -26,7 +21,7 @@ class CredentialEncryption {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a password against a stored hash
|
||||
* Compare a plaintext password against a stored hash
|
||||
*/
|
||||
async verifyPassword(password, storedHash) {
|
||||
try {
|
||||
@@ -39,15 +34,13 @@ class CredentialEncryption {
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt an object for database storage (base64-encoded JSON)
|
||||
* Encode an object as a base64 JSON string for database storage
|
||||
*/
|
||||
encrypt(data) {
|
||||
try {
|
||||
if (!data) throw new Error('No data provided to encrypt');
|
||||
const jsonString = JSON.stringify(data);
|
||||
// btoa is available in React Native / Hermes
|
||||
const encoded = btoa(unescape(encodeURIComponent(jsonString)));
|
||||
return encoded;
|
||||
return btoa(unescape(encodeURIComponent(jsonString)));
|
||||
} catch (error) {
|
||||
console.error('Encryption error:', error);
|
||||
throw new Error('Failed to encrypt data: ' + error.message);
|
||||
@@ -55,7 +48,7 @@ class CredentialEncryption {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a base64-encoded JSON string back to an object
|
||||
* Decode a base64 JSON string back to an object
|
||||
*/
|
||||
decrypt(encryptedData) {
|
||||
try {
|
||||
@@ -70,4 +63,3 @@ class CredentialEncryption {
|
||||
}
|
||||
|
||||
export default new CredentialEncryption();
|
||||
|
||||
@@ -5,9 +5,7 @@ let db = null;
|
||||
let initPromise = null;
|
||||
|
||||
const DatabaseService = {
|
||||
/**
|
||||
* Initialize the database and create tables
|
||||
*/
|
||||
// Open the database and create all tables
|
||||
initDatabase: async () => {
|
||||
if (initPromise) return initPromise;
|
||||
|
||||
@@ -63,13 +61,9 @@ const DatabaseService = {
|
||||
)
|
||||
`);
|
||||
|
||||
// Migration: add columns if they don't exist
|
||||
try {
|
||||
await db.execAsync(`ALTER TABLE users ADD COLUMN auto_backup_services TEXT DEFAULT '[]'`);
|
||||
} catch (e) { /* column already exists */ }
|
||||
try {
|
||||
await db.execAsync(`ALTER TABLE users ADD COLUMN theme_preference TEXT DEFAULT 'light'`);
|
||||
} catch (e) { /* column already exists */ }
|
||||
// Add optional columns (safe migration — errors ignored if columns already exist)
|
||||
try { await db.execAsync(`ALTER TABLE users ADD COLUMN auto_backup_services TEXT DEFAULT '[]'`); } catch (_) {}
|
||||
try { await db.execAsync(`ALTER TABLE users ADD COLUMN theme_preference TEXT DEFAULT 'light'`); } catch (_) {}
|
||||
|
||||
console.log('Database initialized successfully');
|
||||
return true;
|
||||
@@ -89,9 +83,6 @@ const DatabaseService = {
|
||||
if (!db) throw new Error('Database not initialized');
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new user with hashed password
|
||||
*/
|
||||
createUser: async (username, email, password) => {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
@@ -107,9 +98,7 @@ const DatabaseService = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Login user by username or email, verify hashed password
|
||||
*/
|
||||
// Authenticate by username or email
|
||||
loginUser: async (identifier, password) => {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
@@ -129,9 +118,6 @@ const DatabaseService = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if username or email already exists
|
||||
*/
|
||||
userExists: async (username, email) => {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
@@ -150,34 +136,26 @@ const DatabaseService = {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
if (!userId && userId !== 0) throw new Error('User ID is required');
|
||||
const row = await db.getFirstAsync('SELECT * FROM users WHERE id = ?', [userId]);
|
||||
return row || null;
|
||||
return (await db.getFirstAsync('SELECT * FROM users WHERE id = ?', [userId])) || null;
|
||||
} catch (error) {
|
||||
console.error('Error getting user:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Link an external service account to a user
|
||||
*/
|
||||
// Link an external service account (Google, iCloud, NAS) to a local user
|
||||
linkAccount: async (userId, serviceType, accountData, isDefault = false) => {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
if (!userId) throw new Error('User ID is required');
|
||||
if (!serviceType) throw new Error('Service type is required');
|
||||
if (!accountData || typeof accountData !== 'object') {
|
||||
throw new Error('Account data must be a valid object');
|
||||
}
|
||||
if (!accountData || typeof accountData !== 'object') throw new Error('Account data must be a valid object');
|
||||
|
||||
const encryptedData = CredentialEncryption.encrypt(accountData);
|
||||
if (!encryptedData) throw new Error('Encryption returned empty data');
|
||||
|
||||
// Remove existing account of same type first
|
||||
await db.runAsync(
|
||||
'DELETE FROM linked_accounts WHERE user_id = ? AND service_type = ?',
|
||||
[userId, serviceType]
|
||||
);
|
||||
// Replace any existing link for the same service type
|
||||
await db.runAsync('DELETE FROM linked_accounts WHERE user_id = ? AND service_type = ?', [userId, serviceType]);
|
||||
|
||||
const result = await db.runAsync(
|
||||
'INSERT INTO linked_accounts (user_id, service_type, account_data, is_default) VALUES (?, ?, ?, ?)',
|
||||
@@ -195,13 +173,9 @@ const DatabaseService = {
|
||||
await DatabaseService.ensureDb();
|
||||
if (!userId && userId !== 0) throw new Error('User ID is required');
|
||||
const rows = await db.getAllAsync(
|
||||
'SELECT * FROM linked_accounts WHERE user_id = ? AND is_enabled = 1',
|
||||
[userId]
|
||||
'SELECT * FROM linked_accounts WHERE user_id = ? AND is_enabled = 1', [userId]
|
||||
);
|
||||
return rows.map((row) => ({
|
||||
...row,
|
||||
account_data: CredentialEncryption.decrypt(row.account_data),
|
||||
}));
|
||||
return rows.map((row) => ({ ...row, account_data: CredentialEncryption.decrypt(row.account_data) }));
|
||||
} catch (error) {
|
||||
console.error('Error getting linked accounts:', error);
|
||||
throw error;
|
||||
@@ -238,8 +212,7 @@ const DatabaseService = {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
const row = await db.getFirstAsync(
|
||||
'SELECT * FROM linked_accounts WHERE user_id = ? AND is_default = 1',
|
||||
[userId]
|
||||
'SELECT * FROM linked_accounts WHERE user_id = ? AND is_default = 1', [userId]
|
||||
);
|
||||
if (!row) return null;
|
||||
return { ...row, account_data: CredentialEncryption.decrypt(row.account_data) };
|
||||
@@ -270,10 +243,7 @@ const DatabaseService = {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
if (!userId && userId !== 0) throw new Error('User ID is required');
|
||||
return await db.getAllAsync(
|
||||
'SELECT * FROM media WHERE user_id = ? ORDER BY created_date DESC',
|
||||
[userId]
|
||||
);
|
||||
return await db.getAllAsync('SELECT * FROM media WHERE user_id = ? ORDER BY created_date DESC', [userId]);
|
||||
} catch (error) {
|
||||
console.error('Error getting media:', error);
|
||||
throw error;
|
||||
@@ -352,14 +322,8 @@ const DatabaseService = {
|
||||
getAllLinkedAccounts: async (userId) => {
|
||||
try {
|
||||
await DatabaseService.ensureDb();
|
||||
const rows = await db.getAllAsync(
|
||||
'SELECT * FROM linked_accounts WHERE user_id = ?',
|
||||
[userId]
|
||||
);
|
||||
return rows.map((row) => ({
|
||||
...row,
|
||||
account_data: CredentialEncryption.decrypt(row.account_data),
|
||||
}));
|
||||
const rows = await db.getAllAsync('SELECT * FROM linked_accounts WHERE user_id = ?', [userId]);
|
||||
return rows.map((row) => ({ ...row, account_data: CredentialEncryption.decrypt(row.account_data) }));
|
||||
} catch (error) {
|
||||
console.error('Error getting all linked accounts:', error);
|
||||
throw error;
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as SecureStore from 'expo-secure-store';
|
||||
|
||||
const THEME_KEY = 'app_theme_preference';
|
||||
|
||||
// Available theme definitions
|
||||
const lightTheme = {
|
||||
name: 'light',
|
||||
background: '#ffffff',
|
||||
|
||||
Reference in new issue
Block a user