Files
kashilo/js/services/auth.js
2026-01-31 14:18:57 +01:00

187 lines
4.3 KiB
JavaScript

/**
* Auth Service - UUID-based anonymous authentication
*
* No email addresses, no personal data
* User remembers only their UUID
*/
import { directus } from './directus.js';
const AUTH_DOMAIN = 'dgray.io';
class AuthService {
constructor() {
this.currentUser = null;
this.listeners = new Set();
}
/**
* Generates a new UUID for account creation
* @returns {string} UUID v4
*/
generateUuid() {
return crypto.randomUUID();
}
/**
* Converts UUID to fake email for Directus
* @param {string} uuid - User UUID
* @returns {string} Fake email address
*/
uuidToEmail(uuid) {
return `${uuid}@${AUTH_DOMAIN}`;
}
/**
* Creates a new anonymous account
* @returns {Promise<{uuid: string, success: boolean, error?: string}>}
*/
async createAccount() {
const uuid = this.generateUuid();
const email = this.uuidToEmail(uuid);
try {
await directus.register(email, uuid);
// Auto-login after registration
await this.login(uuid);
return { uuid, success: true };
} catch (error) {
console.error('Registration failed:', error);
return {
uuid: null,
success: false,
error: error.message || 'Registration failed'
};
}
}
/**
* Logs in with UUID
* @param {string} uuid - User UUID
* @returns {Promise<{success: boolean, error?: string}>}
*/
async login(uuid) {
const email = this.uuidToEmail(uuid);
try {
await directus.login(email, uuid);
this.currentUser = await directus.getCurrentUser();
this.notifyListeners();
// Store UUID for convenience (optional)
this.storeUuid(uuid);
return { success: true };
} catch (error) {
console.error('Login failed:', error);
return {
success: false,
error: error.message || 'Invalid UUID'
};
}
}
/**
* Logs out the current user
*/
async logout() {
try {
await directus.logout();
} catch (e) {
// Ignore errors
}
this.currentUser = null;
this.clearStoredUuid();
this.notifyListeners();
}
/**
* Checks if user is logged in
* @returns {boolean}
*/
isLoggedIn() {
return directus.isAuthenticated();
}
/**
* Gets current user data
* @returns {Promise<Object|null>}
*/
async getUser() {
if (!this.isLoggedIn()) return null;
if (!this.currentUser) {
try {
this.currentUser = await directus.getCurrentUser();
} catch (e) {
return null;
}
}
return this.currentUser;
}
/**
* Stores UUID in localStorage (optional convenience)
* User should still backup their UUID
* @param {string} uuid
*/
storeUuid(uuid) {
localStorage.setItem('dgray_uuid', uuid);
}
/**
* Gets stored UUID if available
* @returns {string|null}
*/
getStoredUuid() {
return localStorage.getItem('dgray_uuid');
}
/**
* Clears stored UUID
*/
clearStoredUuid() {
localStorage.removeItem('dgray_uuid');
}
/**
* Subscribe to auth state changes
* @param {Function} callback
* @returns {Function} Unsubscribe function
*/
subscribe(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
}
/**
* Notifies all listeners of auth state change
*/
notifyListeners() {
this.listeners.forEach(cb => cb(this.isLoggedIn(), this.currentUser));
}
/**
* Tries to restore session from stored tokens
*/
async tryRestoreSession() {
if (directus.isAuthenticated()) {
try {
this.currentUser = await directus.getCurrentUser();
this.notifyListeners();
return true;
} catch (e) {
return false;
}
}
return false;
}
}
export const auth = new AuthService();
export default auth;