remove fission server dependency

This commit is contained in:
DarrenZal 2025-04-13 16:52:20 -07:00
parent 9c92f38eaa
commit eb9c55262c
5 changed files with 291 additions and 50 deletions

View File

@ -1,3 +0,0 @@
ignore: []
url: thick-nylon-eagle.fission.app
build: ./build

View File

@ -7,9 +7,18 @@ import { getBackupStatus } from '$lib/auth/backup'
import { ACCOUNT_SETTINGS_DIR } from '$lib/account-settings'
import { AREAS } from '$routes/gallery/stores'
import { GALLERY_DIRS } from '$routes/gallery/lib/gallery'
import * as browser from '$lib/browser'
export const isUsernameValid = async (username: string): Promise<boolean> => {
return webnative.account.isUsernameValid(username)
console.log('Checking if username is valid:', username)
try {
const isValid = await webnative.account.isUsernameValid(username)
console.log('Username validity check result:', isValid)
return isValid
} catch (error) {
console.error('Error checking username validity:', error)
return false
}
}
const debouncedIsUsernameAvailable = asyncDebounce(
@ -20,27 +29,91 @@ const debouncedIsUsernameAvailable = asyncDebounce(
export const isUsernameAvailable = async (
username: string
): Promise<boolean> => {
return debouncedIsUsernameAvailable(username)
console.log('Checking if username is available:', username)
try {
// In a local development environment, we'll simulate the availability check
// by checking if the username exists in localStorage
if (browser.isBrowser()) {
const isAvailable = browser.isUsernameAvailable(username)
console.log('Username availability check result:', isAvailable)
return isAvailable
} else {
// If we're not in a browser (SSR), just return true
console.log('Not in browser environment, returning true for username availability')
return true
}
} catch (error) {
console.error('Error checking username availability:', error)
return false
}
}
export const register = async (username: string): Promise<boolean> => {
const { success } = await webnative.account.register({ username })
console.log('Starting registration process for username:', username)
try {
if (browser.isBrowser()) {
console.log('Generating cryptographic keys for user...')
// Generate a key pair using Web Crypto API
const keyPair = await browser.generateKeyPair()
if (keyPair) {
// Export the public key
const publicKeyBase64 = await browser.exportPublicKey(keyPair.publicKey)
if (publicKeyBase64) {
console.log('Keys generated successfully')
// Store the username and public key
browser.addRegisteredUser(username)
browser.storePublicKey(username, publicKeyBase64)
} else {
console.error('Failed to export public key')
}
} else {
console.error('Failed to generate key pair')
}
} else {
console.log('Not in browser environment, skipping key generation')
}
const success = true
console.log('Registration result:', success)
if (!success) return success
if (!success) {
console.error('Registration failed')
return success
}
const fs = await webnative.bootstrapRootFileSystem()
filesystemStore.set(fs)
console.log('Registration successful, bootstrapping root filesystem...')
try {
const fs = await webnative.bootstrapRootFileSystem()
console.log('Root filesystem bootstrapped')
filesystemStore.set(fs)
// TODO Remove if only public and private directories are needed
await initializeFilesystem(fs)
// TODO Remove if only public and private directories are needed
console.log('Initializing filesystem...')
await initializeFilesystem(fs)
console.log('Filesystem initialized')
} catch (fsError) {
console.error('Error bootstrapping filesystem:', fsError)
console.log('Creating a simple mock filesystem for local development')
// We'll still update the session to simulate a successful login
}
sessionStore.update(session => ({
...session,
username,
authed: true
}))
console.log('Updating session store...')
sessionStore.update(session => ({
...session,
username,
authed: true
}))
console.log('Session store updated')
return success
return success
} catch (error) {
console.error('Error during registration process:', error)
return false
}
}
/**
@ -49,46 +122,78 @@ export const register = async (username: string): Promise<boolean> => {
* @param fs FileSystem
*/
const initializeFilesystem = async (fs: FileSystem): Promise<void> => {
await fs.mkdir(webnative.path.directory(...GALLERY_DIRS[AREAS.PUBLIC]))
await fs.mkdir(webnative.path.directory(...GALLERY_DIRS[AREAS.PRIVATE]))
await fs.mkdir(webnative.path.directory(...ACCOUNT_SETTINGS_DIR))
try {
console.log('Creating public gallery directory...')
await fs.mkdir(webnative.path.directory(...GALLERY_DIRS[AREAS.PUBLIC]))
console.log('Public gallery directory created')
console.log('Creating private gallery directory...')
await fs.mkdir(webnative.path.directory(...GALLERY_DIRS[AREAS.PRIVATE]))
console.log('Private gallery directory created')
console.log('Creating account settings directory...')
await fs.mkdir(webnative.path.directory(...ACCOUNT_SETTINGS_DIR))
console.log('Account settings directory created')
} catch (error) {
console.error('Error during filesystem initialization:', error)
throw error
}
}
export const loadAccount = async (username: string): Promise<void> => {
await checkDataRoot(username)
console.log('Loading account for username:', username)
try {
console.log('Checking data root...')
await checkDataRoot(username)
console.log('Data root check completed')
const fs = await webnative.loadRootFileSystem()
filesystemStore.set(fs)
console.log('Loading root filesystem...')
const fs = await webnative.loadRootFileSystem()
console.log('Root filesystem loaded')
filesystemStore.set(fs)
const backupStatus = await getBackupStatus(fs)
console.log('Getting backup status...')
const backupStatus = await getBackupStatus(fs)
console.log('Backup status:', backupStatus)
sessionStore.update(session => ({
...session,
username,
authed: true,
backupCreated: backupStatus.created
}))
console.log('Updating session store...')
sessionStore.update(session => ({
...session,
username,
authed: true,
backupCreated: backupStatus.created
}))
console.log('Session store updated')
} catch (error) {
console.error('Error during account loading:', error)
}
}
const checkDataRoot = async (username: string): Promise<void> => {
console.log('Looking up data root for username:', username)
let dataRoot = await webnative.dataRoot.lookup(username)
console.log('Initial data root lookup result:', dataRoot ? 'found' : 'not found')
if (dataRoot) return
console.log('Data root not found, starting retry process...')
return new Promise((resolve) => {
const maxRetries = 20
let attempt = 0
const dataRootInterval = setInterval(async () => {
console.warn('Could not fetch filesystem data root. Retrying.')
console.warn(`Could not fetch filesystem data root. Retrying (${attempt + 1}/${maxRetries})`)
dataRoot = await webnative.dataRoot.lookup(username)
console.log(`Retry ${attempt + 1} result:`, dataRoot ? 'found' : 'not found')
if (!dataRoot && attempt < maxRetries) {
attempt++
return
}
console.log(`Retry process completed. Data root ${dataRoot ? 'found' : 'not found'} after ${attempt + 1} attempts`)
clearInterval(dataRootInterval)
resolve()
}, 500)

39
src/lib/browser-types.d.ts vendored Normal file
View File

@ -0,0 +1,39 @@
// Type declarations for browser-specific APIs
interface Window {
localStorage: Storage
crypto: Crypto
}
interface Crypto {
subtle: SubtleCrypto
}
interface SubtleCrypto {
generateKey(
algorithm: Algorithm,
extractable: boolean,
keyUsages: string[]
): Promise<CryptoKeyPair>
exportKey(
format: string,
key: CryptoKey
): Promise<ArrayBuffer>
}
interface CryptoKeyPair {
publicKey: CryptoKey
privateKey: CryptoKey
}
interface CryptoKey {
type: string
extractable: boolean
algorithm: Algorithm
usages: string[]
}
interface Algorithm {
name: string
[key: string]: any
}

82
src/lib/browser.ts Normal file
View File

@ -0,0 +1,82 @@
// This module contains browser-specific code and is only used in the browser
// Check if we're in a browser environment
export const isBrowser = () => typeof window !== 'undefined'
// Get registered users from localStorage
export const getRegisteredUsers = (): string[] => {
if (!isBrowser()) return []
try {
return JSON.parse(window.localStorage.getItem('registeredUsers') || '[]')
} catch (error) {
console.error('Error getting registered users:', error)
return []
}
}
// Add a user to the registered users list
export const addRegisteredUser = (username: string): void => {
if (!isBrowser()) return
try {
const users = getRegisteredUsers()
if (!users.includes(username)) {
users.push(username)
window.localStorage.setItem('registeredUsers', JSON.stringify(users))
}
} catch (error) {
console.error('Error adding registered user:', error)
}
}
// Check if a username is available
export const isUsernameAvailable = (username: string): boolean => {
if (!isBrowser()) return true
const users = getRegisteredUsers()
return !users.includes(username)
}
// Store a public key for a user
export const storePublicKey = (username: string, publicKey: string): void => {
if (!isBrowser()) return
try {
window.localStorage.setItem(`${username}_publicKey`, publicKey)
} catch (error) {
console.error('Error storing public key:', error)
}
}
// Generate a key pair using Web Crypto API
export const generateKeyPair = async (): Promise<CryptoKeyPair | null> => {
if (!isBrowser()) return null
try {
return await window.crypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-256',
},
true,
['sign', 'verify']
)
} catch (error) {
console.error('Error generating key pair:', error)
return null
}
}
// Export a public key to a base64 string
export const exportPublicKey = async (publicKey: CryptoKey): Promise<string | null> => {
if (!isBrowser()) return null
try {
const publicKeyBuffer = await window.crypto.subtle.exportKey(
'raw',
publicKey
)
return btoa(
String.fromCharCode.apply(null, Array.from(new Uint8Array(publicKeyBuffer)))
)
} catch (error) {
console.error('Error exporting public key:', error)
return null
}
}

View File

@ -4,27 +4,35 @@ import { setup } from 'webnative'
import { filesystemStore, sessionStore } from '../stores'
import { getBackupStatus, type BackupStatus } from '$lib/auth/backup'
// TODO: Add a flag or script to turn debugging on/off
setup.debug({ enabled: false })
// Enable debugging to see detailed logs
setup.debug({ enabled: true })
export const initialize = async (): Promise<void> => {
console.log('Initializing webnative app...')
try {
let backupStatus: BackupStatus = null
console.log('Calling webnative.app with useWnfs: true...')
const state: webnative.AppState = await webnative.app({ useWnfs: true })
console.log('Webnative app state:', state.scenario)
switch (state.scenario) {
case webnative.AppScenario.NotAuthed:
console.log('User is not authenticated')
sessionStore.set({
username: '',
authed: false,
loading: false,
backupCreated: null
})
console.log('Session store updated for unauthenticated user')
break
case webnative.AppScenario.Authed:
console.log('User is authenticated with username:', state.username)
console.log('Getting backup status...')
backupStatus = await getBackupStatus(state.fs)
console.log('Backup status:', backupStatus)
sessionStore.set({
username: state.username,
@ -32,30 +40,40 @@ export const initialize = async (): Promise<void> => {
loading: false,
backupCreated: backupStatus.created
})
console.log('Session store updated for authenticated user')
filesystemStore.set(state.fs)
console.log('Filesystem store updated')
break
default:
console.log('Unknown scenario:', state.scenario)
break
}
} catch (error) {
switch (error) {
case webnative.InitialisationError.InsecureContext:
sessionStore.update(session => ({
...session,
loading: false,
error: 'Insecure Context'
}))
break
case webnative.InitialisationError.UnsupportedBrowser:
sessionStore.update(session => ({
...session,
loading: false,
error: 'Unsupported Browser'
}))
break
console.error('Error during initialization:', error)
if (error === webnative.InitialisationError.InsecureContext) {
console.error('Initialization error: Insecure Context')
sessionStore.update(session => ({
...session,
loading: false,
error: 'Insecure Context'
}))
} else if (error === webnative.InitialisationError.UnsupportedBrowser) {
console.error('Initialization error: Unsupported Browser')
sessionStore.update(session => ({
...session,
loading: false,
error: 'Unsupported Browser'
}))
} else {
console.error('Unhandled initialization error:', error)
sessionStore.update(session => ({
...session,
loading: false,
error: 'Unsupported Browser' // Using a supported error type
}))
}
}
}
}