remove fission server dependency
This commit is contained in:
parent
9c92f38eaa
commit
eb9c55262c
|
|
@ -1,3 +0,0 @@
|
|||
ignore: []
|
||||
url: thick-nylon-eagle.fission.app
|
||||
build: ./build
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue