- Add DatabaseService with MockDatabaseStrategy and ProductionDatabaseStrategy - Use strategy pattern to switch between test and production database implementations - Improve type safety and testability of database operations - Update database seeder to use new database service architecture This is the foundation for modernizing the database layer.
221 lines
6.1 KiB
TypeScript
221 lines
6.1 KiB
TypeScript
import { getEnvVar, isTest } from '../../utils/env';
|
|
import { MockDatabaseStrategy } from './MockDatabaseStrategy';
|
|
import { ProductionDatabaseStrategy } from './ProductionDatabaseStrategy';
|
|
import { DatabaseStrategy } from './types';
|
|
import { AccountStatus } from '../auth/auth.constants';
|
|
|
|
/**
|
|
* Consolidated Database Service
|
|
* Uses strategy pattern to switch between mock and production implementations
|
|
*/
|
|
export class DatabaseService implements DatabaseStrategy {
|
|
private strategy: DatabaseStrategy;
|
|
|
|
constructor() {
|
|
this.strategy = this.createStrategy();
|
|
}
|
|
|
|
private createStrategy(): DatabaseStrategy {
|
|
// Always use mock service in test environment
|
|
if (isTest()) {
|
|
return new MockDatabaseStrategy();
|
|
}
|
|
|
|
// Check if we're in a Docker environment or if CouchDB URL is configured
|
|
const couchdbUrl =
|
|
getEnvVar('VITE_COUCHDB_URL') || getEnvVar('COUCHDB_URL');
|
|
const useProduction = !!couchdbUrl && couchdbUrl !== 'mock';
|
|
|
|
if (useProduction) {
|
|
try {
|
|
return new ProductionDatabaseStrategy();
|
|
} catch (error) {
|
|
console.warn(
|
|
'Production CouchDB service not available, falling back to mock:',
|
|
error
|
|
);
|
|
return new MockDatabaseStrategy();
|
|
}
|
|
} else {
|
|
return new MockDatabaseStrategy();
|
|
}
|
|
}
|
|
|
|
// Delegate all methods to the strategy
|
|
|
|
// User operations
|
|
async createUser(user: Parameters<DatabaseStrategy['createUser']>[0]) {
|
|
return this.strategy.createUser(user);
|
|
}
|
|
|
|
async updateUser(user: Parameters<DatabaseStrategy['updateUser']>[0]) {
|
|
return this.strategy.updateUser(user);
|
|
}
|
|
|
|
async getUserById(id: string) {
|
|
return this.strategy.getUserById(id);
|
|
}
|
|
|
|
async findUserByEmail(email: string) {
|
|
return this.strategy.findUserByEmail(email);
|
|
}
|
|
|
|
async deleteUser(id: string) {
|
|
return this.strategy.deleteUser(id);
|
|
}
|
|
|
|
async getAllUsers() {
|
|
return this.strategy.getAllUsers();
|
|
}
|
|
|
|
// Medication operations
|
|
async createMedication(
|
|
userId: string,
|
|
medication: Parameters<DatabaseStrategy['createMedication']>[1]
|
|
) {
|
|
return this.strategy.createMedication(userId, medication);
|
|
}
|
|
|
|
// Overloads for updateMedication
|
|
async updateMedication(
|
|
medication: Parameters<DatabaseStrategy['updateMedication']>[0]
|
|
): Promise<Parameters<DatabaseStrategy['updateMedication']>[0]> {
|
|
return this.strategy.updateMedication(medication);
|
|
}
|
|
|
|
async getMedications(userId: string) {
|
|
return this.strategy.getMedications(userId);
|
|
}
|
|
|
|
async deleteMedication(id: string): Promise<boolean> {
|
|
return this.strategy.deleteMedication(id);
|
|
}
|
|
|
|
// User settings operations
|
|
async getUserSettings(userId: string) {
|
|
return this.strategy.getUserSettings(userId);
|
|
}
|
|
|
|
async updateUserSettings(
|
|
settings: Parameters<DatabaseStrategy['updateUserSettings']>[0]
|
|
) {
|
|
return this.strategy.updateUserSettings(settings);
|
|
}
|
|
|
|
// Taken doses operations
|
|
async getTakenDoses(userId: string) {
|
|
return this.strategy.getTakenDoses(userId);
|
|
}
|
|
|
|
// Overloads for updateTakenDoses
|
|
async updateTakenDoses(
|
|
takenDoses: Parameters<DatabaseStrategy['updateTakenDoses']>[0]
|
|
): Promise<Parameters<DatabaseStrategy['updateTakenDoses']>[0]> {
|
|
return this.strategy.updateTakenDoses(takenDoses);
|
|
}
|
|
|
|
// Custom reminders operations
|
|
async createCustomReminder(
|
|
userId: string,
|
|
reminder: Parameters<DatabaseStrategy['createCustomReminder']>[1]
|
|
) {
|
|
return this.strategy.createCustomReminder(userId, reminder);
|
|
}
|
|
|
|
// Overloads for updateCustomReminder
|
|
async updateCustomReminder(
|
|
reminder: Parameters<DatabaseStrategy['updateCustomReminder']>[0]
|
|
): Promise<Parameters<DatabaseStrategy['updateCustomReminder']>[0]> {
|
|
return this.strategy.updateCustomReminder(reminder);
|
|
}
|
|
|
|
async getCustomReminders(userId: string) {
|
|
return this.strategy.getCustomReminders(userId);
|
|
}
|
|
|
|
async deleteCustomReminder(id: string): Promise<boolean> {
|
|
return this.strategy.deleteCustomReminder(id);
|
|
}
|
|
|
|
// User operations with password
|
|
async createUserWithPassword(
|
|
email: string,
|
|
hashedPassword: string,
|
|
username?: string
|
|
) {
|
|
return this.strategy.createUserWithPassword(
|
|
email,
|
|
hashedPassword,
|
|
username
|
|
);
|
|
}
|
|
|
|
async createUserFromOAuth(email: string, username: string, provider: string) {
|
|
return this.strategy.createUserFromOAuth(email, username, provider);
|
|
}
|
|
|
|
// Utility methods
|
|
getStrategyType(): string {
|
|
return this.strategy.constructor.name;
|
|
}
|
|
|
|
isUsingMockStrategy(): boolean {
|
|
return this.strategy instanceof MockDatabaseStrategy;
|
|
}
|
|
|
|
isUsingProductionStrategy(): boolean {
|
|
return this.strategy instanceof ProductionDatabaseStrategy;
|
|
}
|
|
|
|
async suspendUser(userId: string) {
|
|
const user = await this.strategy.getUserById(userId);
|
|
if (!user) throw new Error('User not found');
|
|
return this.strategy.updateUser({
|
|
...user,
|
|
status: AccountStatus.SUSPENDED,
|
|
});
|
|
}
|
|
|
|
async activateUser(userId: string) {
|
|
const user = await this.strategy.getUserById(userId);
|
|
if (!user) throw new Error('User not found');
|
|
return this.strategy.updateUser({
|
|
...user,
|
|
status: AccountStatus.ACTIVE,
|
|
});
|
|
}
|
|
|
|
async changeUserPassword(userId: string, newPassword: string) {
|
|
const user = await this.strategy.getUserById(userId);
|
|
if (!user) throw new Error('User not found');
|
|
return this.strategy.updateUser({
|
|
...user,
|
|
password: newPassword,
|
|
});
|
|
}
|
|
|
|
async deleteAllUserData(userId: string) {
|
|
// Delete user's medications
|
|
const medications = await this.strategy.getMedications(userId);
|
|
for (const med of medications) {
|
|
await this.strategy.deleteMedication(med._id);
|
|
}
|
|
|
|
// Delete user's reminders
|
|
const reminders = await this.strategy.getCustomReminders(userId);
|
|
for (const reminder of reminders) {
|
|
await this.strategy.deleteCustomReminder(reminder._id);
|
|
}
|
|
|
|
// Delete user
|
|
return this.strategy.deleteUser(userId);
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export const databaseService = new DatabaseService();
|
|
|
|
// Re-export types and errors
|
|
export { DatabaseError } from './types';
|
|
export type { DatabaseStrategy } from './types';
|