- Remove NeoUserProvider + NeoInstituteProvider from App.tsx startup chain - Strip user_db_name/school_db_name from CCUser; add school_id (Phase B wires it to Supabase) - Remove DatabaseNameService from AuthContext and UserContext - Remove provisionUser() call from login path; API endpoint preserved for Phase B decision - Simplify UserContext.resolveProfile: fast-path JWT metadata then background Supabase fetch - Replace user.user_db_name reads in singlePlayerPage + snapshotService with null-safe guards - Add useDeviceContext hook (desktop/tablet/phone/iwb, persists to localStorage) App now loads to dashboard without any Neo4j dependency at startup. Canvas opens to blank TLDraw state; Phase B rebuilds navigation on Supabase. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
118 lines
4.5 KiB
TypeScript
118 lines
4.5 KiB
TypeScript
import { supabase } from '../../supabaseClient';
|
|
import { CCUser, convertToCCUser } from '../../services/auth/authService';
|
|
import { EmailCredentials } from '../../services/auth/authService';
|
|
import { formatEmailForDatabase } from '../graph/neoDBService';
|
|
import { RegistrationResponse } from '../../services/auth/authService';
|
|
import { storageService, StorageKeys } from './localStorageService';
|
|
import { logger } from '../../debugConfig';
|
|
import { provisionUser } from '../provisioningService';
|
|
|
|
const REGISTRATION_SERVICE = 'registration-service';
|
|
|
|
export class RegistrationService {
|
|
private static instance: RegistrationService;
|
|
|
|
private constructor() {}
|
|
|
|
static getInstance(): RegistrationService {
|
|
if (!RegistrationService.instance) {
|
|
RegistrationService.instance = new RegistrationService();
|
|
}
|
|
return RegistrationService.instance;
|
|
}
|
|
|
|
async register(credentials: EmailCredentials, displayName: string): Promise<RegistrationResponse> {
|
|
try {
|
|
logger.debug(REGISTRATION_SERVICE, '🔄 Starting registration', {
|
|
email: credentials.email,
|
|
role: credentials.role,
|
|
hasDisplayName: !!displayName
|
|
});
|
|
|
|
// Generate username from email (or use another method)
|
|
const username = formatEmailForDatabase(credentials.email);
|
|
|
|
// 1. First sign up the user in auth
|
|
const { data: authData, error: signUpError } = await supabase.auth.signUp({
|
|
email: credentials.email,
|
|
password: credentials.password,
|
|
options: {
|
|
data: {
|
|
user_type: credentials.role,
|
|
username: username,
|
|
display_name: displayName
|
|
}
|
|
}
|
|
});
|
|
|
|
if (signUpError) {
|
|
logger.error(REGISTRATION_SERVICE, '❌ Supabase signup error', { error: signUpError });
|
|
throw signUpError;
|
|
}
|
|
|
|
if (!authData.user) {
|
|
logger.error(REGISTRATION_SERVICE, '❌ No user data after registration');
|
|
throw new Error('No user data after registration');
|
|
}
|
|
|
|
const ccUser: CCUser = convertToCCUser(authData.user, authData.user.user_metadata);
|
|
|
|
// 2. Update the profile with the correct user type
|
|
const { error: updateError } = await supabase
|
|
.from('profiles')
|
|
.update({
|
|
user_type: credentials.role,
|
|
username: username,
|
|
display_name: displayName
|
|
})
|
|
.eq('id', authData.user.id)
|
|
.select()
|
|
.single();
|
|
|
|
if (updateError) {
|
|
logger.error(REGISTRATION_SERVICE, '❌ Failed to update profile', updateError);
|
|
throw updateError;
|
|
}
|
|
|
|
storageService.set(StorageKeys.IS_NEW_REGISTRATION, true);
|
|
|
|
let provisioningToken = authData.session?.access_token || null;
|
|
if (!provisioningToken) {
|
|
const { data: sessionData } = await supabase.auth.getSession();
|
|
provisioningToken = sessionData.session?.access_token || null;
|
|
}
|
|
|
|
// 3. Create Neo4j nodes
|
|
try {
|
|
const provisioned = await provisionUser(ccUser.id, provisioningToken);
|
|
if (provisioned) {
|
|
logger.info(REGISTRATION_SERVICE, '✅ Provisioning successful', {
|
|
userId: ccUser.id,
|
|
userDbName: provisioned.user_db_name,
|
|
workerDbName: provisioned.worker_db_name
|
|
});
|
|
} else {
|
|
logger.warn(REGISTRATION_SERVICE, '⚠️ Provisioning skipped or pending', { userId: ccUser.id });
|
|
}
|
|
} catch (provisionError) {
|
|
logger.warn(REGISTRATION_SERVICE, '⚠️ Provisioning error', {
|
|
userId: ccUser.id,
|
|
error: provisionError
|
|
});
|
|
}
|
|
|
|
return {
|
|
user: ccUser,
|
|
accessToken: authData.session?.access_token || null,
|
|
userRole: credentials.role,
|
|
message: 'Registration successful'
|
|
};
|
|
} catch (error) {
|
|
logger.error(REGISTRATION_SERVICE, '❌ Registration failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
export const registrationService = RegistrationService.getInstance();
|