Create comprehensive Obsidian-style documentation structure

- Reorganize all markdown documentation into structured docs/ folder
- Create 7 main documentation categories (00-overview through 06-deployment)
- Add comprehensive index files for each category with cross-linking
- Implement Obsidian-compatible [[link]] syntax throughout
- Move legacy/deprecated documentation to archive folder
- Establish documentation standards and maintenance guidelines
- Provide complete coverage of modular architecture, services, and deployment
- Enable better navigation and discoverability for developers and contributors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-09-06 14:31:27 +02:00
parent 46856134ef
commit cdf099e45f
29 changed files with 3733 additions and 0 deletions

1
docs/.obsidian/app.json vendored Normal file
View file

@ -0,0 +1 @@
{}

1
docs/.obsidian/appearance.json vendored Normal file
View file

@ -0,0 +1 @@
{}

31
docs/.obsidian/core-plugins.json vendored Normal file
View file

@ -0,0 +1,31 @@
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"canvas": true,
"outgoing-link": true,
"tag-pane": true,
"properties": false,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"bookmarks": true,
"markdown-importer": false,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": false,
"sync": true,
"webviewer": false
}

175
docs/.obsidian/workspace.json vendored Normal file
View file

@ -0,0 +1,175 @@
{
"main": {
"id": "ff312565b85205f5",
"type": "split",
"children": [
{
"id": "22824a70121de2e3",
"type": "tabs",
"children": [
{
"id": "fe085d296b05d361",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "01-architecture/index.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "index"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "473f9c90dc0ac250",
"type": "split",
"children": [
{
"id": "ee303aa846d48de1",
"type": "tabs",
"children": [
{
"id": "b3b6e397fb343c96",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "Files"
}
},
{
"id": "57989530481d5df7",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "Search"
}
},
{
"id": "77b47c3e2e5e4005",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "Bookmarks"
}
}
]
}
],
"direction": "horizontal",
"width": 300
},
"right": {
"id": "5468ccf17d8ecb0f",
"type": "split",
"children": [
{
"id": "6a62aec4067b5e7c",
"type": "tabs",
"children": [
{
"id": "37a0910880ab8e26",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"file": "01-architecture/index.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks for index"
}
},
{
"id": "4520ebffbf27e768",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "01-architecture/index.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links from index"
}
},
{
"id": "210907b9838cac35",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "Tags"
}
},
{
"id": "f2e44745914b6556",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "01-architecture/index.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of index"
}
}
]
}
],
"direction": "horizontal",
"width": 300,
"collapsed": true
},
"left-ribbon": {
"hiddenItems": {
"switcher:Open quick switcher": false,
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false
}
},
"active": "fe085d296b05d361",
"lastOpenFiles": [
"00-overview/index.md"
]
}

136
docs/00-overview/index.md Normal file
View file

@ -0,0 +1,136 @@
# 📖 Overview
> **Welcome to the Ario Web Application** - A modular Vue 3 + TypeScript application with Nostr protocol integration and Lightning Network payments.
## Table of Contents
- [[#What is Ario?]]
- [[#Key Features]]
- [[#Technology Stack]]
- [[#Quick Start]]
- [[#Documentation Navigation]]
## What is Ario?
Ario is a decentralized social and marketplace application built on the Nostr protocol with Lightning Network integration. It provides users with:
- **Decentralized Social Networking** - Connect through the Nostr protocol
- **Lightning Payments** - Instant, low-fee Bitcoin payments
- **Event Ticketing** - Create and manage events with Lightning payments
- **Encrypted Messaging** - Private, secure communications
- **Marketplace Functionality** - Buy and sell goods using Lightning Network
## Key Features
### 🔒 **Privacy-First Architecture**
- No central servers storing user data
- Nostr protocol ensures user sovereignty
- Client-side key management with secure encryption
### ⚡ **Lightning Network Integration**
- Instant Bitcoin payments for events and marketplace
- Low transaction fees
- Invoice generation and payment tracking
### 🏗️ **Modular Architecture**
- Plugin-based module system
- Dependency injection for service management
- Clean separation of concerns
- Easy to extend and maintain
### 📱 **Multi-Platform Support**
- Progressive Web App (PWA) capabilities
- Electron desktop application
- Responsive design for mobile and desktop
### 🌍 **Internationalization**
- Multi-language support with Vue-i18n
- Theme switching (light/dark modes)
- Accessibility-focused design
## Technology Stack
### Frontend Core
- **Vue 3** with Composition API and `<script setup>`
- **TypeScript** throughout for type safety
- **Vite** for fast development and optimized builds
- **TailwindCSS v4** with Shadcn/ui components
### State Management & Routing
- **Pinia** for reactive state management
- **Vue Router** for navigation
- **Vue-i18n** for internationalization
### Nostr & Lightning
- **Nostr-tools** for protocol implementation
- **WebLN** for Lightning Network browser integration
- **QR Code generation** for Lightning invoices
### Desktop & PWA
- **Electron** for desktop application packaging
- **Vite PWA Plugin** for Progressive Web App features
- **Service Worker** for offline capabilities
## Quick Start
### Prerequisites
- Node.js 18+ and npm
- Basic knowledge of Vue 3 and TypeScript
### Development Setup
```bash
# Navigate to the web app directory
cd web-app/
# Install dependencies
npm install
# Start development server
npm run dev
# For Electron development
npm run electron:dev
```
### Environment Configuration
```bash
# Copy environment template
cp .env.example .env
# Configure Nostr relays
VITE_NOSTR_RELAYS='["wss://relay1.example.com","wss://relay2.example.com"]'
# Configure admin pubkeys for announcements
VITE_ADMIN_PUBKEYS='["hexadecimal_pubkey_1","hexadecimal_pubkey_2"]'
```
## Documentation Navigation
### Essential Reading (Start Here)
1. **[[getting-started|🚀 Getting Started]]** - Detailed setup instructions
2. **[[project-goals|🎯 Project Goals]]** - Vision and objectives
3. **[[tech-stack|🛠️ Technology Stack]]** - Detailed technology overview
### Architecture & Development
- **[[../01-architecture/index|🏗️ Architecture Overview]]** - System design patterns
- **[[../04-development/index|💻 Development Guide]]** - Coding standards and workflows
### Feature Modules
- **[[../02-modules/index|📦 Modules Overview]]** - Feature module documentation
- **[[../03-core-services/index|⚙️ Core Services]]** - Shared infrastructure services
### Integration & Deployment
- **[[../05-api-reference/index|📡 API Reference]]** - External integrations
- **[[../06-deployment/index|🚀 Deployment]]** - Production deployment guide
## See Also
- **[[../01-architecture/modular-design|Modular Architecture]]** - Understanding the plugin system
- **[[../04-development/coding-standards|Coding Standards]]** - Code quality guidelines
- **[[../02-modules/base-module/index|Base Module]]** - Core infrastructure documentation
---
**Tags:** #overview #introduction #getting-started
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,137 @@
# 🎯 Project Goals
> **Ario's mission** - Building a decentralized, user-sovereign platform for social interaction, commerce, and events powered by Bitcoin and Nostr.
## Table of Contents
- [[#Vision]]
- [[#Core Objectives]]
- [[#Technical Goals]]
- [[#User Experience Goals]]
- [[#Ecosystem Goals]]
## Vision
Ario aims to create a **decentralized alternative** to traditional social media and marketplace platforms by leveraging the Nostr protocol and Lightning Network. Our vision is a world where users have complete control over their data, identity, and financial transactions.
### Key Principles
- **User Sovereignty** - Users own their data and identity
- **Censorship Resistance** - No central authority can silence users
- **Financial Freedom** - Direct peer-to-peer value exchange
- **Open Source** - Transparent, auditable, and collaborative development
## Core Objectives
### 1. **Decentralized Social Networking**
- Enable users to connect without intermediaries
- Provide familiar social media features on decentralized infrastructure
- Support rich content sharing (text, images, events)
- Enable real-time communication through Nostr relays
### 2. **Lightning-Powered Commerce**
- Facilitate instant, low-fee Bitcoin payments
- Enable event ticketing with Lightning invoices
- Support marketplace transactions
- Provide seamless payment UX comparable to traditional payment methods
### 3. **Privacy and Security**
- Client-side key management with secure encryption
- No central storage of sensitive user data
- Optional anonymity and pseudonymity support
- End-to-end encrypted messaging capabilities
### 4. **User-Friendly Experience**
- Intuitive interface that doesn't compromise on decentralization
- Progressive Web App with offline capabilities
- Mobile-first responsive design
- Multi-language and accessibility support
## Technical Goals
### Architecture Excellence
- **Modular Design** - Plugin-based architecture for easy extension
- **Type Safety** - TypeScript throughout for reliability
- **Performance** - Optimized builds and lazy loading
- **Maintainability** - Clean code patterns and comprehensive documentation
### Protocol Implementation
- **Nostr Compliance** - Full implementation of relevant NIPs (Nostr Implementation Possibilities)
- **Lightning Integration** - WebLN support and invoice management
- **Relay Management** - Intelligent relay selection and failover
- **Real-time Updates** - Efficient event subscription and filtering
### Developer Experience
- **Clear Abstractions** - Well-defined service interfaces
- **Dependency Injection** - Loose coupling between modules
- **Comprehensive Testing** - Unit and integration test coverage
- **Documentation** - Thorough guides for contributors
## User Experience Goals
### Onboarding Experience
- **Simple Setup** - Easy key generation or import process
- **Educational Content** - Help users understand Nostr and Lightning
- **Progressive Disclosure** - Advanced features revealed as users are ready
- **Recovery Options** - Secure backup and recovery mechanisms
### Daily Usage
- **Fast Performance** - Sub-second response times for common actions
- **Reliable Connectivity** - Robust relay connection management
- **Intuitive Navigation** - Clear information architecture
- **Rich Interactions** - Engaging social and commerce features
### Advanced Features
- **Power User Tools** - Advanced relay management and filtering
- **Privacy Controls** - Granular privacy and visibility settings
- **Integration APIs** - Support for third-party integrations
- **Customization** - Theming and layout preferences
## Ecosystem Goals
### Developer Ecosystem
- **Open Source Community** - Encourage contributions and forks
- **Plugin Architecture** - Enable third-party module development
- **API Documentation** - Clear integration guides for developers
- **Development Tools** - Debugging and development utilities
### Network Effects
- **Relay Diversity** - Support a healthy, distributed relay network
- **Interoperability** - Compatibility with other Nostr clients
- **Standard Compliance** - Active participation in NIP development
- **Community Building** - Foster vibrant user communities
### Economic Sustainability
- **Lightning Adoption** - Drive Lightning Network usage growth
- **Value Creation** - Enable new economic models through programmable money
- **Fee Optimization** - Minimize transaction costs for users
- **Revenue Sharing** - Fair compensation for relay operators and developers
## Success Metrics
### User Adoption
- Monthly active users across web and desktop platforms
- User retention rates and engagement metrics
- Geographic distribution and diversity
### Technical Performance
- Application load times and responsiveness
- Relay connection success rates and latency
- Payment success rates and confirmation times
### Ecosystem Health
- Number of connected relays and geographic distribution
- Integration with other Nostr clients and services
- Developer contributions and community participation
## See Also
- **[[getting-started|Getting Started Guide]]** - Begin your journey with Ario
- **[[tech-stack|Technology Stack]]** - Technical implementation details
- **[[../01-architecture/index|Architecture Overview]]** - System design principles
- **[[../04-development/index|Development Guide]]** - Contributing to Ario
---
**Tags:** #vision #goals #strategy #roadmap
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,241 @@
# 🛠️ Technology Stack
> **Modern web technologies** powering Ario's decentralized architecture with Vue 3, TypeScript, Nostr, and Lightning Network integration.
## Table of Contents
- [[#Frontend Core]]
- [[#State Management & Routing]]
- [[#Styling & UI Components]]
- [[#Nostr & Lightning Integration]]
- [[#Build Tools & Development]]
- [[#Desktop & PWA]]
- [[#Architecture Patterns]]
## Frontend Core
### **Vue 3** - Progressive JavaScript Framework
- **Composition API** with `<script setup>` syntax for optimal DX
- **Reactivity System** with `ref()`, `reactive()`, and `computed()`
- **Component Architecture** with Single File Components (SFCs)
- **Template Compilation** for optimized runtime performance
**Why Vue 3?**
- Excellent TypeScript integration
- Composition API enables better code reuse
- Small bundle size and fast performance
- Great developer experience with Vite
### **TypeScript** - Type-Safe JavaScript
- **Strict Type Checking** throughout the application
- **Interface-Based Architecture** for better code contracts
- **Generic Types** for reusable service patterns
- **Compile-Time Error Detection** preventing runtime issues
**Configuration:**
```json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
```
### **Vite** - Next-Generation Build Tool
- **Lightning-Fast Dev Server** with HMR (Hot Module Replacement)
- **Optimized Production Builds** with Rollup
- **Plugin Ecosystem** for Vue, TypeScript, and PWA support
- **ES Modules** native support for modern browsers
## State Management & Routing
### **Pinia** - Vue Store Library
- **Composition API Integration** for consistent patterns
- **TypeScript Support** with full type inference
- **Devtools Integration** for debugging
- **Server-Side Rendering** compatibility
**Store Pattern Example:**
```typescript
export const useAuthStore = defineStore('auth', () => {
const user = ref<NostrUser | null>(null)
const isAuthenticated = computed(() => !!user.value)
const login = async (privateKey: string) => {
// Authentication logic
}
return { user, isAuthenticated, login }
})
```
### **Vue Router** - Client-Side Routing
- **File-Based Routing** with automatic route generation
- **Route Guards** for authentication and authorization
- **Lazy Loading** for code-splitting by route
- **Nested Routes** for complex layout structures
### **Vue-i18n** - Internationalization
- **Reactive Language Switching** with Composition API
- **Pluralization Support** for different languages
- **Number and Date Formatting** localization
- **Lazy Loading** of translation files
## Styling & UI Components
### **TailwindCSS v4** - Utility-First CSS Framework
- **Design System** with consistent spacing and typography
- **Dark Mode Support** with CSS variables
- **Component Variants** using Tailwind's class composition
- **Optimized Builds** with unused CSS elimination
### **Shadcn/ui** - High-Quality Component Library
- **Accessible Components** following WAI-ARIA guidelines
- **Customizable Styling** with CSS variables and Tailwind
- **Copy-Paste Architecture** rather than npm dependencies
- **TypeScript Support** with proper type definitions
**Component Structure:**
```
src/components/ui/
├── button/ # Button variants and sizes
├── card/ # Card layouts and containers
├── dialog/ # Modal and dialog components
├── form/ # Form inputs and validation
└── toast/ # Notification components
```
## Nostr & Lightning Integration
### **Nostr Protocol** - Decentralized Social Protocol
- **nostr-tools** - Core Nostr client implementation
- **Event Publishing** and subscription management
- **Key Management** with secure client-side storage
- **Relay Management** with connection pooling and fallback
**Nostr Client Architecture:**
```typescript
class NostrClient {
private pool: RelayPool
private subscriptions: Map<string, Sub>
async publishEvent(event: Event): Promise<void>
subscribe(filters: Filter[], onEvent: EventHandler): Sub
connect(relays: string[]): Promise<void>
}
```
### **Lightning Network** - Bitcoin Payment Layer
- **WebLN Integration** for browser-based Lightning wallets
- **Invoice Generation** with QR code support
- **Payment Verification** and status tracking
- **LNbits Integration** for wallet backend services
### **QR Code Generation** - Payment Interfaces
- **Lightning Invoice QR Codes** for mobile wallet scanning
- **Contact Information Sharing** with Nostr public keys
- **Event Tickets** with embedded payment information
## Build Tools & Development
### **Vite Configuration**
- **Plugin Architecture** with Vue, TypeScript, and PWA plugins
- **Code Splitting** with manual chunk optimization
- **Asset Optimization** with image processing and compression
- **Development Server** with proxy configuration for API calls
**Bundle Analysis:**
```bash
npm run analyze # Opens bundle analyzer visualization
```
### **ESLint + Prettier** - Code Quality
- **Vue-Specific Rules** for SFC linting
- **TypeScript Integration** with type-aware linting
- **Automatic Formatting** on save and commit
- **Import Sorting** and unused import removal
### **Git Hooks** - Pre-commit Quality Gates
- **Type Checking** before commits
- **Linting** and formatting validation
- **Test Execution** for modified files
- **Build Verification** to catch issues early
## Desktop & PWA
### **Electron** - Cross-Platform Desktop Apps
- **Electron Forge** for packaging and distribution
- **Auto-Update** functionality for seamless updates
- **Native Menus** and system tray integration
- **File System Access** for local data storage
### **PWA (Progressive Web App)**
- **Service Worker** with caching strategies
- **App Manifest** for installation and app-like experience
- **Offline Support** with background sync
- **Push Notifications** for real-time updates
**PWA Configuration:**
```typescript
// vite.config.ts
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}']
}
})
```
## Architecture Patterns
### **Dependency Injection** - Service Management
- **DI Container** for service registration and resolution
- **Service Tokens** for type-safe service access
- **Lifecycle Management** with initialization and disposal
- **Testing Support** with mock service injection
### **Module Plugin System** - Extensible Architecture
- **Module Registration** with dependency management
- **Route Configuration** per module
- **Service Isolation** with clear boundaries
- **Component Export** for cross-module usage
### **Reactive Services** - State-Aware Infrastructure
- **Vue Reactivity** integrated into service layer
- **Computed Properties** for derived state
- **Watchers** for side-effect management
- **Event-Driven Communication** between services
## Development Workflow
### **Hot Module Replacement**
```bash
npm run dev # Start development with HMR
```
### **Production Build**
```bash
npm run build # TypeScript check + Vite build
npm run preview # Preview production build locally
```
### **Electron Development**
```bash
npm run electron:dev # Concurrent Vite + Electron
npm run electron:build # Package desktop application
```
## See Also
- **[[getting-started|Getting Started Guide]]** - Setup and development instructions
- **[[../01-architecture/index|Architecture Overview]]** - System design patterns
- **[[../04-development/coding-standards|Coding Standards]]** - Development guidelines
- **[[../02-modules/index|Module System]]** - Plugin architecture documentation
---
**Tags:** #technology #stack #vue #typescript #nostr #lightning
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,241 @@
# 🏗️ Architecture Overview
> **System design and patterns** powering Ario's modular, scalable, and maintainable architecture with dependency injection, plugin-based modules, and reactive services.
## Table of Contents
- [[#Architecture Principles]]
- [[#Core Systems]]
- [[#Service Architecture]]
- [[#Module System]]
- [[#Communication Patterns]]
- [[#Data Flow]]
## Architecture Principles
### **Modularity** - Plugin-Based Design
Every feature is implemented as a self-contained module that can be enabled, disabled, or configured independently. This approach ensures:
- **Separation of Concerns** - Each module owns its domain logic
- **Testability** - Modules can be tested in isolation
- **Maintainability** - Changes to one module don't affect others
- **Extensibility** - New features can be added as plugins
### **Loose Coupling** - Dependency Injection
Services communicate through well-defined interfaces using a dependency injection container:
- **Interface-Based Design** - Services depend on abstractions, not implementations
- **Runtime Configuration** - Service instances can be swapped for testing or different environments
- **Clear Dependencies** - Service relationships are explicit and manageable
- **Lifecycle Management** - Services have defined initialization and disposal phases
### **Reactive Architecture** - Vue-Powered Services
Services integrate Vue's reactivity system for state management:
- **Reactive State** - Services expose reactive refs and computed properties
- **Automatic Updates** - UI automatically updates when service state changes
- **Event-Driven Communication** - Services communicate via event bus patterns
- **Consistent Patterns** - Same reactivity patterns used throughout the application
## Core Systems
### **Dependency Injection Container**
Location: `src/core/di-container.ts`
Centralized service registry and resolution system:
```typescript
// Service registration (in modules)
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
// Service consumption (anywhere)
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
```
**Key Features:**
- Type-safe service tokens
- Singleton lifecycle management
- Runtime service validation
- Development-time debugging support
### **Plugin Manager**
Location: `src/core/plugin-manager.ts`
Orchestrates module loading and dependency resolution:
```typescript
class PluginManager {
async loadModule(plugin: ModulePlugin): Promise<void>
getDependencyGraph(): Map<string, string[]>
validateDependencies(modules: ModulePlugin[]): void
}
```
### **Base Service Architecture**
Location: `src/core/base/BaseService.ts`
Abstract foundation for all services with common patterns:
```typescript
abstract class BaseService {
protected isInitialized = ref(false)
protected isDisposed = ref(false)
abstract initialize(): Promise<void>
abstract dispose(): Promise<void>
}
```
## Service Architecture
### **Service Categories**
#### **Infrastructure Services** (Base Module)
- **RelayHub** - Nostr relay connection management
- **AuthService** - User authentication and key management
- **StorageService** - User-scoped local storage operations
- **ToastService** - User notifications and feedback
#### **Feature Services** (Module-Specific)
- **ChatService** - Encrypted messaging functionality
- **MarketService** - Marketplace operations
- **EventsService** - Event creation and ticketing
- **FeedService** - Social feed management
### **Service Communication Patterns**
#### **Direct Service Dependencies**
For tightly coupled operations:
```typescript
class ChatService extends BaseService {
constructor(
private relayHub = injectService(SERVICE_TOKENS.RELAY_HUB),
private auth = injectService(SERVICE_TOKENS.AUTH_SERVICE)
) {}
}
```
#### **Event Bus Communication**
For loose coupling between modules:
```typescript
// Publishing events
eventBus.emit('user:authenticated', { userId: user.pubkey })
// Subscribing to events
eventBus.on('payment:received', handlePaymentReceived)
```
## Module System
### **Module Plugin Structure**
Each module implements the `ModulePlugin` interface:
```typescript
interface ModulePlugin {
name: string
version: string
dependencies: string[]
install(app: App, options?: any): Promise<void>
routes?: RouteRecordRaw[]
components?: Record<string, Component>
}
```
### **Module Categories**
#### **Base Module** - Core Infrastructure
- **Purpose** - Provides shared services and infrastructure
- **Dependencies** - None (foundation module)
- **Exports** - RelayHub, AuthService, StorageService, ToastService
#### **Feature Modules** - Domain-Specific Logic
- **Chat Module** - Encrypted messaging with Nostr DMs
- **Events Module** - Event creation and Lightning ticketing
- **Market Module** - Nostr marketplace functionality
- **Nostr Feed Module** - Social feed and content discovery
### **Module Configuration**
Location: `src/app.config.ts`
```typescript
export const moduleConfigs = {
'base': { enabled: true },
'chat': { enabled: true, maxMessageLength: 1000 },
'events': { enabled: true, defaultCurrency: 'sat' },
'market': { enabled: true, allowedCategories: ['all'] },
'nostr-feed': { enabled: true, defaultRelays: [] }
}
```
## Communication Patterns
### **Service-to-Service Communication**
```typescript
// 1. Direct injection for required dependencies
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
// 2. Event bus for optional/cross-module communication
eventBus.emit('user:profile-updated', profileData)
// 3. Reactive refs for state sharing
const connectionStatus = computed(() => relayHub.connectionStatus.value)
```
### **Component-to-Service Communication**
```typescript
// 1. Composables wrap service access
const { publishNote, isPublishing } = useNoteComposer()
// 2. Direct service injection in components
const toastService = injectService(SERVICE_TOKENS.TOAST_SERVICE)
// 3. Store integration for component state
const authStore = useAuthStore()
```
### **Module-to-Module Communication**
```typescript
// ❌ Direct imports break modularity
import { chatService } from '../chat/services/chat-service'
// ✅ Dependency injection maintains loose coupling
const chatService = injectService(SERVICE_TOKENS.CHAT_SERVICE)
// ✅ Event bus for cross-module notifications
eventBus.emit('chat:message-received', messageData)
```
## Data Flow
### **Nostr Event Lifecycle**
1. **Event Creation** - User action triggers event creation
2. **Event Signing** - AuthService signs event with user's private key
3. **Relay Publishing** - RelayHub publishes to configured relays
4. **Event Storage** - StorageService caches event locally
5. **UI Updates** - Reactive properties trigger component re-renders
### **Lightning Payment Flow**
1. **Invoice Generation** - EventsService creates Lightning invoice
2. **QR Code Display** - UI presents payment interface
3. **Payment Detection** - WebLN or external wallet processes payment
4. **Verification** - Service confirms payment via Lightning backend
5. **State Updates** - Payment success triggers relevant service updates
### **Module Initialization Flow**
1. **Dependency Resolution** - PluginManager sorts modules by dependencies
2. **Service Registration** - Each module registers services in DI container
3. **Route Registration** - Module routes added to Vue Router
4. **Component Registration** - Global components made available
5. **Service Initialization** - Services initialize in dependency order
## See Also
### Deep Dive Documentation
- **[[modular-design|📦 Modular Design Patterns]]** - Plugin architecture details
- **[[dependency-injection|⚙️ Dependency Injection]]** - Service container system
- **[[relay-hub|🌐 Relay Hub Architecture]]** - Nostr relay management
- **[[event-bus|📡 Event Bus Communication]]** - Inter-module messaging
### Implementation References
- **[[../02-modules/index|📦 Module Documentation]]** - Individual module guides
- **[[../03-core-services/index|⚙️ Core Services]]** - Service implementations
- **[[../04-development/index|💻 Development Guide]]** - Architectural patterns in practice
---
**Tags:** #architecture #design-patterns #modularity #dependency-injection
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,356 @@
# Central Relay Hub Architecture
## Overview
The Ario application uses a centralized relay hub architecture to manage all Nostr relay connections. This design provides a single point of connection management, efficient resource sharing, and consistent relay behavior across all components.
## Architecture Components
### 1. Core Relay Hub (`relayHub.ts`)
The `RelayHub` class is the foundation of the relay management system, built on top of `nostr-tools` SimplePool.
**Key Features:**
- **Singleton Pattern**: Single instance manages all relay connections
- **Connection Pooling**: Efficiently manages multiple relay connections
- **Automatic Reconnection**: Handles connection failures and reconnection logic
- **Health Monitoring**: Regular health checks and status reporting
- **Mobile Optimization**: Handles mobile app visibility changes
- **Event Emission**: Provides event-driven architecture for status updates
**Location:** `src/modules/base/nostr/relay-hub.ts` *(moved from legacy lib/nostr location)*
### 2. Relay Hub Composable (`useRelayHub.ts`)
A Vue 3 composable that provides a reactive interface to the relay hub.
**Key Features:**
- **Reactive State**: `isConnected`, `connectionStatus`, `relayStatuses`
- **Connection Management**: `initialize()`, `connect()`, `disconnect()`
- **Subscription Management**: `subscribe()`, `unsubscribe()`
- **Event Publishing**: `publishEvent()`, `queryEvents()`
- **Health Monitoring**: Connection health percentages and status
**Location:** `src/composables/useRelayHub.ts`
### 3. Application Integration (`App.vue`)
The main application component initializes the relay hub at startup.
**Key Integration Points:**
```typescript
// Initialize relay hub
const relayHub = useRelayHub()
onMounted(async () => {
// Initialize relay hub
try {
await relayHub.initialize()
console.log('Relay hub initialized successfully')
} catch (error) {
console.error('Failed to initialize relay hub:', error)
}
})
```
## How Components Plug Into the Relay Hub
### 1. Direct Integration via `useRelayHub()`
Components can directly use the relay hub composable:
```typescript
import { useRelayHub } from '@/composables/useRelayHub'
export default {
setup() {
const relayHub = useRelayHub()
// Access reactive state
const isConnected = relayHub.isConnected
const connectionStatus = relayHub.connectionStatus
// Use connection methods
const connect = async () => {
await relayHub.connect()
}
// Subscribe to events
const unsubscribe = relayHub.subscribe({
id: 'my-subscription',
filters: [{ kinds: [1] }], // Text notes
onEvent: (event) => {
console.log('Received event:', event)
}
})
// Clean up on unmount
onUnmounted(() => {
unsubscribe()
})
return {
isConnected,
connectionStatus,
connect
}
}
}
```
### 2. Indirect Integration via Specialized Composables
Components can use specialized composables that internally use the relay hub:
#### Chat Integration (`useNostrChat.ts`)
The chat system integrates with the relay hub for message handling:
```typescript
// Inside useNostrChat.ts
const subscribeToPeer = async (peerPubkey: string) => {
// Check connection status
if (!relayHub.isConnected.value) {
await relayHub.connect()
}
// Subscribe to peer messages
const unsubscribe = relayHub.subscribe({
id: `peer-${peerPubkey}`,
filters: [
{
kinds: [4], // Direct messages
'#p': [currentUser.value.pubkey],
authors: [peerPubkey]
}
],
onEvent: (event) => handleIncomingMessage(event, peerPubkey)
})
return unsubscribe
}
```
#### Market Integration (`useMarket.ts`)
The market system can integrate similarly:
```typescript
// Example market integration
const subscribeToMarketEvents = async () => {
if (!relayHub.isConnected.value) {
await relayHub.connect()
}
return relayHub.subscribe({
id: 'market-events',
filters: [
{
kinds: [30000], // Custom market events
'#t': ['market']
}
],
onEvent: (event) => handleMarketEvent(event)
})
}
```
### 3. Configuration and Setup
#### Relay Configuration
Relays are configured in the application config:
```typescript
// src/lib/config.ts
export const config = {
nostr: {
relays: [
'wss://relay.ariege.io',
'wss://lnbits.ariege.io/nostrrelay/benac'
]
}
}
```
#### Initialization Order
1. **App.vue** initializes the relay hub
2. **Components** can use the relay hub immediately after initialization
3. **Specialized composables** handle their own relay hub integration
## Benefits of the Central Relay Hub Architecture
### 1. **Resource Efficiency**
- Single connection pool for all relay connections
- Shared connection management across components
- Reduced memory usage and connection overhead
### 2. **Consistent Behavior**
- Unified connection status across the application
- Consistent error handling and reconnection logic
- Standardized relay health monitoring
### 3. **Scalability**
- Easy to add new relay endpoints
- Centralized configuration management
- Simple to extend with new relay features
### 4. **Maintainability**
- Single point of truth for relay logic
- Easier debugging and monitoring
- Centralized relay-related bug fixes
## Best Practices for Component Integration
### 1. **Always Check Connection Status**
```typescript
const myFunction = async () => {
if (!relayHub.isConnected.value) {
await relayHub.connect()
}
// Proceed with relay operations
}
```
### 2. **Use Subscription IDs**
```typescript
const subscriptionId = `my-component-${Date.now()}`
const unsubscribe = relayHub.subscribe({
id: subscriptionId,
filters: [...],
onEvent: handleEvent
})
```
### 3. **Clean Up Subscriptions**
```typescript
onUnmounted(() => {
if (unsubscribe) {
unsubscribe()
}
})
```
### 4. **Handle Connection Errors**
```typescript
const connect = async () => {
try {
await relayHub.connect()
} catch (error) {
console.error('Failed to connect:', error)
// Handle error appropriately
}
}
```
### 5. **Monitor Connection Health**
```typescript
const connectionHealth = computed(() => relayHub.connectionHealth)
const isHealthy = computed(() => connectionHealth.value > 50)
```
## Example: Creating a New Relay-Enabled Component
Here's a complete example of a new component that uses the relay hub:
```vue
<template>
<div>
<div>Connection Status: {{ connectionStatus }}</div>
<div>Connected Relays: {{ connectedRelayCount }}/{{ totalRelayCount }}</div>
<button @click="subscribeToEvents" :disabled="!isConnected">
Subscribe to Events
</button>
<div v-if="events.length">
<h3>Received Events:</h3>
<div v-for="event in events" :key="event.id">
{{ event.content }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useRelayHub } from '@/composables/useRelayHub'
const relayHub = useRelayHub()
const events = ref<any[]>([])
let unsubscribe: (() => void) | null = null
const { isConnected, connectionStatus, connectedRelayCount, totalRelayCount } = relayHub
const subscribeToEvents = async () => {
if (!relayHub.isConnected.value) {
await relayHub.connect()
}
unsubscribe = relayHub.subscribe({
id: 'my-component-events',
filters: [
{
kinds: [1], // Text notes
limit: 10
}
],
onEvent: (event) => {
events.value.push(event)
}
})
}
onUnmounted(() => {
if (unsubscribe) {
unsubscribe()
}
})
</script>
```
## Troubleshooting
### Common Issues
1. **Connection Not Established**
- Check if relay hub is initialized in App.vue
- Verify relay URLs in configuration
- Check browser console for connection errors
2. **Subscriptions Not Working**
- Ensure connection is established before subscribing
- Verify subscription filters are correct
- Check if unsubscribe function is called prematurely
3. **Performance Issues**
- Monitor number of active subscriptions
- Use appropriate filter limits
- Clean up unused subscriptions
### Debug Tools
The relay hub provides several debugging capabilities:
```typescript
// Check connection status
console.log('Connected:', relayHub.isConnected.value)
console.log('Relay count:', relayHub.totalRelayCount)
// Monitor relay health
console.log('Health:', relayHub.connectionHealth.value)
// Check specific relay status
const status = relayHub.getRelayStatus('wss://relay.example.com')
console.log('Relay status:', status)
```
## Conclusion
The central relay hub architecture provides a robust, efficient, and maintainable way to manage Nostr relay connections across the Ario application. By following the integration patterns outlined in this document, components can easily leverage the relay hub's capabilities while maintaining clean separation of concerns and optimal performance.
For questions or issues with relay hub integration, refer to the existing implementations in `useNostrChat.ts` and `useMarket.ts` as reference examples.

View file

@ -0,0 +1,187 @@
# Nostr Chat Integration for Web-App
This document describes the Nostr chat integration that allows LNBits users to chat with each other using Nostr relays.
## Overview
The chat system integrates with the LNBits user system and Nostr relays to provide encrypted messaging between users. Each user has a Nostr keypair (stored in `pubkey` and `prvkey` fields) that enables secure communication.
## Navigation Integration
The chat feature is accessible through the main navigation menu:
- **Desktop**: Chat link appears in the top navigation bar with a message icon
- **Mobile**: Chat link appears in the mobile menu with a message icon
- **Route**: `/chat` - Accessible to authenticated users only
## Components
### 1. ChatComponent.vue
**Location**: `src/components/nostr/ChatComponent.vue`
A Vue component that provides the chat interface with:
- Peer list populated from LNBits users
- Real-time messaging using Nostr relays
- Encrypted message exchange
- Connection status indicators
### 2. useNostrChat.ts
**Location**: `src/composables/useNostrChat.ts`
A composable that handles:
- Nostr relay connections
- Message encryption/decryption
- User authentication with LNBits
- Real-time message subscription
### 3. ChatPage.vue
**Location**: `src/pages/ChatPage.vue`
A page that integrates the chat component into the web-app.
### 4. Navigation Integration
**Location**: `src/components/layout/Navbar.vue`
The chat link has been added to the main navigation with:
- Message icon for visual identification
- Internationalization support (English, Spanish, French)
- Responsive design for desktop and mobile
## API Endpoints
### Get Current User
```bash
GET /users/api/v1/user/me
Authorization: Bearer <admin_token>
Response:
{
"id": "user_id",
"username": "username",
"email": "email@example.com",
"pubkey": "nostr_public_key",
"prvkey": "nostr_private_key_hex",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
```
### Get All User Public Keys
```bash
GET /users/api/v1/nostr/pubkeys
Authorization: Bearer <admin_token>
Response:
[
{
"user_id": "user_id",
"username": "username",
"pubkey": "nostr_public_key"
}
]
```
## Features
### 1. User Integration
- Automatically loads peers from LNBits user database
- Uses existing `pubkey` and `prvkey` fields
- Admin-only access to private keys for messaging
### 2. Nostr Relay Integration
- Connects to multiple Nostr relays for redundancy
- Real-time message delivery
- Encrypted end-to-end messaging
### 3. UI Features
- Peer list with user avatars and names
- Real-time message display
- Connection status indicators
- Message timestamps
- Auto-scroll to latest messages
### 4. Mobile-Responsive Design
- **Mobile-first approach**: Optimized for touch interactions
- **Peer list view**: Shows only peers list on mobile until a peer is selected
- **Full-width chat view**: When a peer is selected, switches to full-width chat
- **Back button**: Easy navigation back to peers list
- **Touch-friendly**: Larger touch targets and proper touch feedback
- **Responsive avatars**: Larger avatars on mobile for better visibility
- **Message bubbles**: Optimized width (75% max) for mobile readability
- **Keyboard-friendly**: Input stays visible when keyboard appears
### 5. Navigation Features
- Integrated into main navigation menu
- Message icon for easy identification
- Multi-language support
- Responsive design for all devices
## Security
1. **Encryption**: All messages are encrypted using NIP-04 (Nostr encrypted direct messages)
2. **Private Key Access**: Only admin users can access private keys for messaging
3. **Relay Security**: Messages are distributed across multiple relays for redundancy
4. **User Authentication**: Requires LNBits authentication to access chat
## Setup Requirements
1. **NostrTools**: The web-app needs NostrTools loaded globally
2. **Admin Access**: Users need admin privileges to access private keys
3. **Relay Configuration**: Default relays are configured in the composable
4. **LNBits Integration**: Requires the updated LNBits API endpoints
## Usage
1. Navigate to `/chat` in the web-app (or click "Chat" in the navigation)
2. The system will automatically load peers from LNBits
3. Select a peer to start chatting
4. Messages are encrypted and sent via Nostr relays
## Configuration
### Default Relays
The system connects to these relays by default:
- `wss://nostr.atitlan.io`
- `wss://relay.damus.io`
- `wss://nos.lol`
### Relay Configuration
You can modify the relays in `useNostrChat.ts`:
```typescript
const DEFAULT_RELAYS: NostrRelayConfig[] = [
{ url: 'wss://your-relay.com', read: true, write: true }
]
```
## Future Enhancements
1. **Message Persistence**: Store messages locally for offline access
2. **File Sharing**: Support for encrypted file sharing
3. **Group Chats**: Multi-user encrypted conversations
4. **Message Search**: Search through conversation history
5. **Push Notifications**: Real-time notifications for new messages
6. **Profile Integration**: Display user profiles and avatars
7. **Message Reactions**: Support for message reactions and replies
## Troubleshooting
### Common Issues
1. **Connection Failed**: Check relay availability and network connectivity
2. **Messages Not Sending**: Verify user has admin privileges and private key access
3. **Peers Not Loading**: Check LNBits API endpoint and authentication
4. **Encryption Errors**: Ensure NostrTools is properly loaded
### Debug Information
The chat component logs detailed information to the console:
- Connection status
- Message encryption/decryption
- Relay connection attempts
- API call results
## Dependencies
- **NostrTools**: For Nostr protocol implementation
- **Vue 3**: For reactive UI components
- **LNBits API**: For user management and authentication
- **Nostr Relays**: For message distribution

386
docs/02-modules/index.md Normal file
View file

@ -0,0 +1,386 @@
# 📦 Module System Overview
> **Modular architecture** enabling feature-based development with plugin-based modules, dependency injection, and clean separation of concerns.
## Table of Contents
- [[#Module Architecture]]
- [[#Available Modules]]
- [[#Module Development]]
- [[#Module Configuration]]
- [[#Inter-Module Communication]]
- [[#Module Lifecycle]]
## Module Architecture
### **Plugin-Based Design**
Ario uses a plugin-based architecture where each feature is implemented as a self-contained module:
- **Independent Development** - Modules can be developed, tested, and deployed separately
- **Optional Features** - Modules can be enabled or disabled via configuration
- **Clear Boundaries** - Each module owns its domain logic and UI components
- **Extensible** - New modules can be added without modifying existing code
### **Module Structure**
Each module follows a consistent directory structure:
```
src/modules/[module-name]/
├── index.ts # Module plugin definition
├── components/ # Module-specific UI components
├── composables/ # Module composables and hooks
├── services/ # Business logic and API services
├── stores/ # Module-specific Pinia stores
├── types/ # TypeScript type definitions
└── views/ # Page components and routes
```
### **Module Plugin Interface**
All modules implement the `ModulePlugin` interface:
```typescript
interface ModulePlugin {
name: string // Unique module identifier
version: string // Semantic version
dependencies: string[] // Required module dependencies
// Installation lifecycle
install(app: App, options?: ModuleConfig): Promise<void>
// Optional exports
routes?: RouteRecordRaw[] // Vue Router routes
components?: Record<string, any> // Global components
composables?: Record<string, any> // Exported composables
}
```
## Available Modules
### **Base Module** 🏗️
**Purpose:** Core infrastructure and shared services
**Location:** `src/modules/base/`
**Dependencies:** None (foundation module)
**Provides:**
- **Authentication Service** - User identity management and Nostr key handling
- **Relay Hub** - Centralized Nostr relay connection management
- **Storage Service** - User-scoped localStorage operations
- **Toast Service** - Application-wide notifications and feedback
- **PWA Features** - Service worker and offline capabilities
**Key Components:**
- Identity management UI (key generation, import/export)
- Connection status indicators
- Theme and language switching
- Authentication guards and utilities
**See:** [[base-module/index|📖 Base Module Documentation]]
### **Nostr Feed Module** 📰
**Purpose:** Social feed and content discovery
**Location:** `src/modules/nostr-feed/`
**Dependencies:** `['base']`
**Features:**
- **Social Feed** - Timeline of Nostr events (kind 1 notes)
- **Admin Announcements** - Highlighted posts from configured admin pubkeys
- **Content Filtering** - Filter by author, content type, or keywords
- **Real-time Updates** - Live feed updates via Nostr subscriptions
- **Engagement** - Like, repost, and reply to posts
**Key Components:**
- FeedComponent with infinite scroll
- NoteCard for individual posts
- AdminBadge for announcement highlighting
- Content filtering and search
**See:** [[nostr-feed-module/index|📖 Nostr Feed Documentation]]
### **Chat Module** 💬
**Purpose:** Encrypted direct messaging
**Location:** `src/modules/chat/`
**Dependencies:** `['base']`
**Features:**
- **Encrypted Messages** - NIP-04 encrypted direct messages
- **Contact Management** - Add and manage chat contacts
- **Real-time Chat** - Live message delivery via Nostr relays
- **Message History** - Persistent chat history with local storage
- **Typing Indicators** - Real-time typing status (when supported)
**Key Components:**
- ChatComponent with message bubbles
- ContactList for chat participants
- MessageInput with encryption handling
- Chat history management
**See:** [[chat-module/index|📖 Chat Module Documentation]]
### **Events Module** 🎟️
**Purpose:** Event ticketing and management
**Location:** `src/modules/events/`
**Dependencies:** `['base']`
**Features:**
- **Event Creation** - Create and publish events to Nostr
- **Lightning Tickets** - Paid event tickets using Lightning invoices
- **Event Discovery** - Browse and search upcoming events
- **Ticket Management** - Purchase, transfer, and validate tickets
- **Event Check-in** - QR code-based event entry system
**Key Components:**
- EventCard for event display
- TicketPurchase with Lightning payment flow
- EventCreation form with rich editing
- QR code generation and scanning
**See:** [[events-module/index|📖 Events Module Documentation]]
### **Market Module** 🛒
**Purpose:** Nostr marketplace functionality
**Location:** `src/modules/market/`
**Dependencies:** `['base']`
**Features:**
- **Product Listings** - Create and browse marketplace items
- **Lightning Payments** - Bitcoin payments for products
- **Vendor Profiles** - Seller reputation and product history
- **Order Management** - Track purchases and sales
- **Product Search** - Filter and search marketplace items
**Key Components:**
- ProductCard for item display
- ProductListing creation form
- OrderHistory and transaction tracking
- Vendor dashboard and analytics
**See:** [[market-module/index|📖 Market Module Documentation]]
## Module Development
### **Creating a New Module**
#### 1. Module Structure Setup
```bash
mkdir src/modules/my-module
cd src/modules/my-module
# Create module directories
mkdir components composables services stores types views
touch index.ts
```
#### 2. Module Plugin Definition
```typescript
// src/modules/my-module/index.ts
import type { App } from 'vue'
import type { ModulePlugin } from '@/core/types'
export const myModule: ModulePlugin = {
name: 'my-module',
version: '1.0.0',
dependencies: ['base'], // Always depend on base for core services
async install(app: App, options?: MyModuleConfig) {
// Register module components
// Initialize module services
// Set up module routes
},
routes: [
{
path: '/my-feature',
component: () => import('./views/MyFeatureView.vue')
}
]
}
```
#### 3. Service Implementation
```typescript
// src/modules/my-module/services/my-service.ts
import { BaseService } from '@/core/base/BaseService'
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
export class MyService extends BaseService {
constructor(
private relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
) {
super()
}
async initialize(): Promise<void> {
// Service initialization logic
this.isInitialized.value = true
}
async dispose(): Promise<void> {
// Cleanup logic
this.isDisposed.value = true
}
}
```
#### 4. Module Registration
```typescript
// src/app.config.ts
export const moduleConfigs = {
// ... existing modules
'my-module': {
enabled: true,
customOption: 'value'
}
}
```
### **Module Development Best Practices**
#### **Dependency Management**
- Always declare module dependencies explicitly
- Use dependency injection for cross-module service access
- Avoid direct imports between modules
#### **Service Architecture**
- Extend `BaseService` for consistent lifecycle management
- Register services in the DI container during module installation
- Use reactive properties for state that affects UI
#### **Component Patterns**
- Export reusable components for other modules
- Use module-specific component naming (e.g., `ChatMessage`, `EventCard`)
- Follow the existing UI component patterns with Shadcn/ui
## Module Configuration
### **Configuration Schema**
Modules can be configured via `src/app.config.ts`:
```typescript
interface ModuleConfig {
enabled: boolean // Enable/disable module
[key: string]: any // Module-specific configuration
}
export const moduleConfigs: Record<string, ModuleConfig> = {
'base': { enabled: true },
'chat': {
enabled: true,
maxMessageLength: 1000,
enableTypingIndicators: true
},
'events': {
enabled: true,
defaultCurrency: 'sat',
allowedEventTypes: ['meetup', 'conference', 'workshop']
}
}
```
### **Runtime Configuration**
Configuration is passed to modules during installation:
```typescript
async install(app: App, options?: ChatModuleConfig) {
const config = options || defaultConfig
// Use configuration to customize module behavior
this.messageService.setMaxLength(config.maxMessageLength)
}
```
## Inter-Module Communication
### **Service Dependencies**
For required functionality between modules:
```typescript
// ✅ Correct: Use dependency injection
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
// ❌ Wrong: Direct import breaks modularity
import { relayHub } from '../base/services/relay-hub'
```
### **Event Bus Communication**
For optional cross-module notifications:
```typescript
// Publishing events
eventBus.emit('user:authenticated', { userId: user.pubkey })
eventBus.emit('payment:received', { amount: 1000, invoiceId: 'abc123' })
// Subscribing to events
eventBus.on('chat:message-received', (message) => {
// Handle message in events module
})
```
### **Shared Components**
Modules can export components for use by other modules:
```typescript
// In module plugin definition
export const chatModule: ModulePlugin = {
// ...
components: {
'ChatAvatar': () => import('./components/ChatAvatar.vue'),
'MessageBubble': () => import('./components/MessageBubble.vue')
}
}
// Usage in other modules
<ChatAvatar :pubkey="user.pubkey" :size="32" />
```
## Module Lifecycle
### **Initialization Order**
1. **Dependency Resolution** - PluginManager sorts modules by dependencies
2. **Service Registration** - Modules register services in DI container
3. **Component Registration** - Global components made available
4. **Route Registration** - Module routes added to Vue Router
5. **Service Initialization** - Services initialize in dependency order
### **Module Installation Process**
```typescript
async install(app: App, options?: ModuleConfig) {
// 1. Register services
container.provide(SERVICE_TOKENS.MY_SERVICE, new MyService())
// 2. Register global components
app.component('MyGlobalComponent', MyGlobalComponent)
// 3. Initialize module-specific logic
await this.initializeModule(options)
}
```
### **Service Lifecycle Management**
```typescript
// Service initialization (called automatically)
async initialize(): Promise<void> {
await this.setupEventListeners()
await this.loadUserData()
this.isInitialized.value = true
}
// Service disposal (called on app unmount)
async dispose(): Promise<void> {
this.removeEventListeners()
await this.saveUserData()
this.isDisposed.value = true
}
```
## See Also
### Module Documentation
- **[[base-module/index|🏗️ Base Module]]** - Core infrastructure services
- **[[chat-module/index|💬 Chat Module]]** - Encrypted messaging system
- **[[events-module/index|🎟️ Events Module]]** - Lightning event ticketing
- **[[market-module/index|🛒 Market Module]]** - Nostr marketplace
- **[[nostr-feed-module/index|📰 Nostr Feed]]** - Social feed functionality
### Architecture References
- **[[../01-architecture/modular-design|🔧 Modular Design Patterns]]** - Architecture principles
- **[[../01-architecture/dependency-injection|⚙️ Dependency Injection]]** - Service container system
- **[[../04-development/index|💻 Development Guide]]** - Module development workflows
---
**Tags:** #modules #architecture #plugin-system #development
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,921 @@
# Order Management & Fulfillment Workflows
This document provides comprehensive coverage of the complete order lifecycle, from initial placement through payment processing to final fulfillment and shipping management. It includes detailed analysis of both merchant and customer interfaces, database operations, and automated fulfillment processes.
## Overview: Order Lifecycle Management
The marketplace implements a **comprehensive order management system** with dual interfaces for merchants and customers, supporting complete order tracking from placement to fulfillment with automated inventory management and payment processing.
### Order States and Transitions
```mermaid
graph LR
A[Order Created] --> B[Invoice Generated]
B --> C[Payment Request Sent]
C --> D{Payment Status}
D -->|Paid| E[Payment Confirmed]
D -->|Unpaid| F[Invoice Expired/Reissued]
F --> C
E --> G[Inventory Updated]
G --> H{Fulfillment}
H -->|Ready| I[Shipped Status]
H -->|Issue| J[Error/Refund]
I --> K[Order Complete]
```
## Core Order Data Models
### 1. Order Schema (`models.py:400-485`)
#### Order Structure Hierarchy
```python
class OrderItem(BaseModel):
product_id: str # Product identifier from order
quantity: int # Quantity ordered
class OrderContact(BaseModel):
nostr: Optional[str] = None # Customer's nostr pubkey
phone: Optional[str] = None # Customer phone number
email: Optional[str] = None # Customer email address
class OrderExtra(BaseModel):
products: List[ProductOverview] # Snapshot of products at time of order
currency: str # Pricing currency (USD, EUR, sat, etc.)
btc_price: str # Exchange rate at time of order
shipping_cost: float = 0 # Shipping cost in currency
shipping_cost_sat: float = 0 # Shipping cost in satoshis
fail_message: Optional[str] = None # Error message if order failed
```
#### Complete Order Model
```python
class Order(PartialOrder):
stall_id: str # Associated stall identifier
invoice_id: str # Lightning invoice payment hash
total: float # Total amount in satoshis
paid: bool = False # Payment status
shipped: bool = False # Shipping/fulfillment status
time: Optional[int] = None # Completion timestamp
extra: OrderExtra # Additional order metadata
```
### 2. Order Status Models (`models.py:467-485`)
#### Status Update Structure
```python
class OrderStatusUpdate(BaseModel):
id: str # Order identifier
message: Optional[str] = None # Status update message
paid: Optional[bool] = False # Payment status
shipped: Optional[bool] = None # Shipping status
class OrderReissue(BaseModel):
id: str # Order identifier to reissue
shipping_id: Optional[str] = None # Updated shipping zone
class PaymentRequest(BaseModel):
id: str # Order identifier
message: Optional[str] = None # Response message
payment_options: List[PaymentOption] # Available payment methods
```
### 3. Database Schema (`migrations.py:110-130`)
#### Order Table Structure
```sql
CREATE TABLE nostrmarket.orders (
merchant_id TEXT NOT NULL, -- Merchant who owns this order
id TEXT PRIMARY KEY, -- Unique order identifier (UUID)
event_id TEXT, -- Nostr event ID for order placement
event_created_at INTEGER NOT NULL, -- Unix timestamp of order creation
public_key TEXT NOT NULL, -- Customer's public key
merchant_public_key TEXT NOT NULL, -- Merchant's public key
contact_data TEXT NOT NULL DEFAULT '{}', -- JSON contact information
extra_data TEXT NOT NULL DEFAULT '{}', -- JSON extra metadata
order_items TEXT NOT NULL, -- JSON array of ordered items
address TEXT, -- Shipping address (deprecated)
total REAL NOT NULL, -- Total amount in satoshis
shipping_id TEXT NOT NULL, -- Shipping zone identifier
stall_id TEXT NOT NULL, -- Associated stall identifier
invoice_id TEXT NOT NULL, -- Lightning invoice payment hash
paid BOOLEAN NOT NULL DEFAULT false, -- Payment confirmation
shipped BOOLEAN NOT NULL DEFAULT false, -- Fulfillment status
time INTEGER -- Completion timestamp
);
```
## Merchant Order Management Interface
### 1. Order List Component (`order-list.js`)
#### Component Structure and Properties
```javascript
window.app.component('order-list', {
name: 'order-list',
props: ['stall-id', 'customer-pubkey-filter', 'adminkey', 'inkey'],
template: '#order-list',
delimiters: ['${', '}'],
```
#### Advanced Search and Filtering (`order-list.js:15-49`)
```javascript
data: function () {
return {
orders: [],
selectedOrder: null,
search: {
publicKey: null, // Filter by customer public key
isPaid: {label: 'All', id: null}, // Payment status filter
isShipped: {label: 'All', id: null}, // Shipping status filter
},
ternaryOptions: [
{label: 'All', id: null}, // Show all orders
{label: 'Yes', id: 'true'}, // Filter for paid/shipped = true
{label: 'No', id: 'false'} // Filter for paid/shipped = false
]
}
}
```
#### Dynamic Order Fetching (`order-list.js:156-181`)
```javascript
getOrders: async function () {
try {
// Support both stall-specific and merchant-wide queries
const ordersPath = this.stallId
? `stall/order/${this.stallId}` // Orders for specific stall
: 'order' // All orders for merchant
// Build query parameters for filtering
const query = []
if (this.search.publicKey) {
query.push(`pubkey=${this.search.publicKey}`)
}
if (this.search.isPaid.id) {
query.push(`paid=${this.search.isPaid.id}`)
}
if (this.search.isShipped.id) {
query.push(`shipped=${this.search.isShipped.id}`)
}
const {data} = await LNbits.api.request(
'GET',
`/nostrmarket/api/v1/${ordersPath}?${query.join('&')}`,
this.inkey
)
this.orders = data.map(s => ({...s, expanded: false}))
} catch (error) {
LNbits.utils.notifyApiError(error)
}
}
```
### 2. Order Display and Calculations (`order-list.js:119-155`)
#### Product Information Retrieval
```javascript
productName: function (order, productId) {
product = order.extra.products.find(p => p.id === productId)
if (product) {
return product.name
}
return ''
},
productPrice: function (order, productId) {
product = order.extra.products.find(p => p.id === productId)
if (product) {
return `${product.price} ${order.extra.currency}`
}
return ''
},
orderTotal: function (order) {
// Calculate total from individual product costs + shipping
const productCost = order.items.reduce((t, item) => {
product = order.extra.products.find(p => p.id === item.product_id)
return t + item.quantity * product.price
}, 0)
return productCost + order.extra.shipping_cost
}
```
### 3. Shipping Status Management (`order-list.js:259-280`)
#### Shipping Status Updates
```javascript
updateOrderShipped: async function () {
this.selectedOrder.shipped = !this.selectedOrder.shipped
try {
await LNbits.api.request(
'PATCH',
`/nostrmarket/api/v1/order/${this.selectedOrder.id}`,
this.adminkey,
{
id: this.selectedOrder.id,
message: this.shippingMessage, // Custom message to customer
shipped: this.selectedOrder.shipped // New shipping status
}
)
this.$q.notify({
type: 'positive',
message: 'Order updated!'
})
} catch (error) {
LNbits.utils.notifyApiError(error)
}
this.showShipDialog = false
}
```
#### Shipping Dialog Interface (`order-list.js:356-365`)
```javascript
showShipOrderDialog: function (order) {
this.selectedOrder = order
this.shippingMessage = order.shipped
? 'The order has been shipped!'
: 'The order has NOT yet been shipped!'
// Toggle status (will be confirmed on dialog submit)
this.selectedOrder.shipped = !order.shipped
this.showShipDialog = true
}
```
### 4. Order Recovery and Restoration (`order-list.js:194-233`)
#### Individual Order Restoration
```javascript
restoreOrder: async function (eventId) {
try {
this.search.restoring = true
const {data} = await LNbits.api.request(
'PUT',
`/nostrmarket/api/v1/order/restore/${eventId}`, // Restore from DM event
this.adminkey
)
await this.getOrders() // Refresh order list
this.$q.notify({
type: 'positive',
message: 'Order restored!'
})
return data
} catch (error) {
LNbits.utils.notifyApiError(error)
} finally {
this.search.restoring = false
}
}
```
#### Bulk Order Restoration
```javascript
restoreOrders: async function () {
try {
this.search.restoring = true
await LNbits.api.request(
'PUT',
`/nostrmarket/api/v1/orders/restore`, // Restore all from DMs
this.adminkey
)
await this.getOrders()
this.$q.notify({
type: 'positive',
message: 'Orders restored!'
})
} catch (error) {
LNbits.utils.notifyApiError(error)
}
}
```
### 5. Invoice Management (`order-list.js:234-258`)
#### Invoice Reissuance
```javascript
reissueOrderInvoice: async function (order) {
try {
const {data} = await LNbits.api.request(
'PUT',
`/nostrmarket/api/v1/order/reissue`,
this.adminkey,
{
id: order.id,
shipping_id: order.shipping_id // Optional shipping zone update
}
)
this.$q.notify({
type: 'positive',
message: 'Order invoice reissued!'
})
// Update order in local state
data.expanded = order.expanded
const i = this.orders.map(o => o.id).indexOf(order.id)
if (i !== -1) {
this.orders[i] = {...this.orders[i], ...data}
}
} catch (error) {
LNbits.utils.notifyApiError(error)
}
}
```
## Customer Order Interface
### 1. Customer Orders Component (`CustomerOrders.vue`)
#### Order Display Structure
```vue
<div v-for="merchant in merchantOrders" :key="merchant.id">
<q-card bordered class="q-mb-md">
<q-item>
<user-profile <!-- Merchant identity -->
:pubkey="merchant.pubkey"
:profiles="profiles"
></user-profile>
</q-item>
<q-list>
<div v-for="order in merchant.orders" :key="order.id">
<q-expansion-item dense expand-separator>
<template v-slot:header>
<q-item-section>
<q-item-label>
<strong><span v-text="order.stallName"></span></strong>
<q-badge <!-- Total amount -->
v-if="order.invoice?.human_readable_part?.amount"
color="orange"
>
<span v-text="formatCurrency(order.invoice.human_readable_part.amount / 1000, 'sat')"></span>
</q-badge>
</q-item-label>
</q-item-section>
<q-item-section side>
<q-badge :color="order.paid ? 'green' : 'grey'"> <!-- Payment status -->
<span v-text="order.paid ? 'Paid' : 'Not Paid'"></span>
</q-badge>
<q-badge :color="order.shipped ? 'green' : 'grey'"> <!-- Shipping status -->
<span v-text="order.shipped ? 'Shipped' : 'Not Shipped'"></span>
</q-badge>
</q-item-section>
</template>
</q-expansion-item>
</div>
</q-list>
</q-card>
</div>
```
### 2. Order Data Enrichment (`CustomerOrders.vue:208-220`)
#### Order Enhancement Pipeline
```javascript
enrichOrder: function (order) {
const stall = this.stallForOrder(order);
return {
...order,
stallName: stall?.name || "Stall", // Stall name for display
shippingZone: stall?.shipping?.find( // Shipping zone details
(s) => s.id === order.shipping_id
) || { id: order.shipping_id, name: order.shipping_id },
invoice: this.invoiceForOrder(order), // Parsed Lightning invoice
products: this.getProductsForOrder(order), // Product details with quantities
};
}
```
#### Stall Association (`CustomerOrders.vue:221-233`)
```javascript
stallForOrder: function (order) {
try {
const productId = order.items && order.items[0]?.product_id;
if (!productId) return;
const product = this.products.find((p) => p.id === productId);
if (!product) return;
const stall = this.stalls.find((s) => s.id === product.stall_id);
if (!stall) return;
return stall;
} catch (error) {
console.log(error);
}
}
```
### 3. Invoice Processing (`CustomerOrders.vue:234-244`)
#### Lightning Invoice Decoding
```javascript
invoiceForOrder: function (order) {
try {
const lnPaymentOption = order?.payment_options?.find(
(p) => p.type === "ln" // Find Lightning payment option
);
if (!lnPaymentOption?.link) return;
return decode(lnPaymentOption.link); // Decode BOLT11 invoice
} catch (error) {
console.warn(error);
}
}
```
### 4. Product Aggregation (`CustomerOrders.vue:246-259`)
#### Order Item Processing
```javascript
getProductsForOrder: function (order) {
if (!order?.items?.length) return [];
return order.items.map((i) => {
const product = this.products.find((p) => p.id === i.product_id) || {
id: i.product_id,
name: i.product_id, // Fallback if product not found
};
return {
...product,
orderedQuantity: i.quantity, // Add ordered quantity to product
};
});
}
```
## Backend Order Operations
### 1. Order Creation (`services.py:84-133`)
#### Order Build Pipeline
```python
async def build_order_with_payment(merchant_id, merchant_public_key, data):
# 1. Validate products and calculate costs
products = await get_products_by_ids(merchant_id, [p.product_id for p in data.items])
data.validate_order_items(products) # Ensure products exist and have stock
shipping_zone = await get_zone(merchant_id, data.shipping_id)
product_cost_sat, shipping_cost_sat = await data.costs_in_sats(
products, shipping_zone.id, shipping_zone.cost
)
# 2. Check inventory availability
success, _, message = await compute_products_new_quantity(
merchant_id, [i.product_id for i in data.items], data.items
)
if not success:
raise ValueError(message) # Insufficient inventory
# 3. Create Lightning invoice via LNbits
payment = await create_invoice(
wallet_id=wallet_id,
amount=round(product_cost_sat + shipping_cost_sat),
memo=f"Order '{data.id}' for pubkey '{data.public_key}'",
extra={
"tag": "nostrmarket", # Tags invoice as marketplace
"order_id": data.id,
"merchant_pubkey": merchant_public_key,
},
)
# 4. Create order record
order = Order(
**data.dict(),
stall_id=products[0].stall_id,
invoice_id=payment.payment_hash,
total=product_cost_sat + shipping_cost_sat,
extra=extra,
)
return order, payment.bolt11, receipt
```
### 2. Order Retrieval API (`views_api.py:540-577`)
#### Multi-filter Order Queries
```python
@nostrmarket_ext.get("/api/v1/stall/order/{stall_id}")
async def api_get_orders_for_stall(
stall_id: str,
paid: Optional[bool] = None, # Filter by payment status
shipped: Optional[bool] = None, # Filter by shipping status
pubkey: Optional[str] = None, # Filter by customer pubkey
wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> List[Order]:
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"
orders = await get_orders_for_stall(
merchant.id, stall_id, paid=paid, shipped=shipped, public_key=pubkey
)
return orders
except AssertionError as ex:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail=str(ex)
) from ex
```
### 3. Order Status Updates (`views_api.py:625-641`)
#### Shipping Status API
```python
@nostrmarket_ext.patch("/api/v1/order/{order_id}")
async def api_update_order_status(
data: OrderStatusUpdate,
wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Order:
try:
assert data.shipped is not None, "Shipped value is required for order"
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found for order {data.id}"
# Update shipping status in database
order = await update_order_shipped_status(merchant.id, data.id, data.shipped)
assert order, "Cannot find updated order"
# Send status update to customer via DM
data.paid = order.paid # Include current payment status
dm_content = json.dumps(
{"type": DirectMessageType.ORDER_PAID_OR_SHIPPED.value, **data.dict()},
separators=(",", ":"),
ensure_ascii=False,
)
await reply_to_structured_dm(
merchant, order.public_key, DirectMessageType.ORDER_PAID_OR_SHIPPED.value, dm_content
)
return order
except AssertionError as ex:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail=str(ex)
) from ex
```
### 4. Invoice Reissuance (`views_api.py:710-740`)
#### Payment Request Regeneration
```python
@nostrmarket_ext.put("/api/v1/order/reissue")
async def api_reissue_order_invoice(
reissue_data: OrderReissue,
wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Order:
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"
# Get existing order
data = await get_order(merchant.id, reissue_data.id)
assert data, "Order cannot be found"
# Update shipping zone if provided
if reissue_data.shipping_id:
data.shipping_id = reissue_data.shipping_id
# Generate new payment request
payment_req, order = await build_order_with_payment(
merchant.id, merchant.public_key, data
)
# Update order with new invoice details
order_update = {
"total": payment_req.total,
"invoice_id": order.invoice_id, # New payment hash
"extra_data": json.dumps(order.extra.dict()),
}
await update_order(
merchant.id,
order.id,
**order_update,
)
# Send new payment request to customer
payment_req = PaymentRequest(
id=order.id,
message="Updated payment request",
payment_options=[PaymentOption(type="ln", link=order.bolt11)],
)
dm_content = json.dumps(
{"type": DirectMessageType.PAYMENT_REQUEST.value, **payment_req.dict()},
)
await reply_to_structured_dm(
merchant, order.public_key, DirectMessageType.PAYMENT_REQUEST.value, dm_content
)
return await get_order(merchant.id, reissue_data.id)
except Exception as ex:
logger.warning(ex)
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Cannot reissue order invoice",
) from ex
```
## Order Restoration System
### 1. Order Recovery from Direct Messages (`services.py:645-690`)
#### DM-based Order Restoration
```python
async def create_or_update_order_from_dm(
merchant_id: str, merchant_pubkey: str, dm: DirectMessage
):
type_, json_data = PartialDirectMessage.parse_message(dm.message)
if not json_data or "id" not in json_data:
return
if type_ == DirectMessageType.CUSTOMER_ORDER:
# Restore customer order from DM
order, _ = await extract_customer_order_from_dm(
merchant_id, merchant_pubkey, dm, json_data
)
new_order = await create_order(merchant_id, order)
# Handle stall association updates
if new_order.stall_id == "None" and order.stall_id != "None":
await update_order(
merchant_id,
order.id,
**{
"stall_id": order.stall_id,
"extra_data": json.dumps(order.extra.dict()),
},
)
return
if type_ == DirectMessageType.PAYMENT_REQUEST:
# Update order with payment request details
payment_request = PaymentRequest(**json_data)
pr = payment_request.payment_options[0].link
invoice = decode(pr)
total = invoice.amount_msat / 1000 if invoice.amount_msat else 0
await update_order(
merchant_id,
payment_request.id,
**{"total": total, "invoice_id": invoice.payment_hash},
)
return
if type_ == DirectMessageType.ORDER_PAID_OR_SHIPPED:
# Update order status from status messages
order_update = OrderStatusUpdate(**json_data)
if order_update.paid:
await update_order_paid_status(order_update.id, True)
if order_update.shipped:
await update_order_shipped_status(merchant_id, order_update.id, True)
```
### 2. Bulk Restoration API (`views_api.py:580-595`)
#### Complete Order Recovery
```python
@nostrmarket_ext.put("/api/v1/orders/restore")
async def api_restore_orders_from_dms(
wallet: WalletTypeInfo = Depends(require_admin_key),
):
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"
# Get all order-related direct messages
dms = await get_orders_from_direct_messages(merchant.id)
for dm in dms:
try:
# Attempt to restore/update each order from DM history
await create_or_update_order_from_dm(
merchant.id, merchant.public_key, dm
)
except Exception as e:
logger.debug(
f"Failed to restore order from event '{dm.event_id}': '{e!s}'."
)
continue
return {"status": "Orders restoration completed!"}
except AssertionError as ex:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail=str(ex)
) from ex
```
## Real-time Order Updates
### 1. WebSocket Order Notifications (`order-list.js:281-296`)
#### Live Order Addition
```javascript
addOrder: async function (data) {
if (
!this.search.publicKey ||
this.search.publicKey === data.customerPubkey // Filter matches current view
) {
const orderData = JSON.parse(data.dm.message)
const i = this.orders.map(o => o.id).indexOf(orderData.id)
if (i === -1) { // Prevent duplicates
const order = await this.getOrder(orderData.id) // Fetch complete order data
this.orders.unshift(order) // Add to top of list
}
}
}
```
### 2. Payment Status Updates (`order-list.js:391-396`)
#### Real-time Payment Confirmation
```javascript
orderPaid: function (orderId) {
const order = this.orders.find(o => o.id === orderId)
if (order) {
order.paid = true // Update payment status immediately
}
}
```
## Advanced Order Management Features
### 1. Order Selection and Deep Linking (`order-list.js:294-315`)
#### Order Detail Navigation
```javascript
orderSelected: async function (orderId, eventId) {
const order = await this.getOrder(orderId)
if (!order) {
// Order missing - offer restoration from DM
LNbits.utils
.confirmDialog(
'Order could not be found. Do you want to restore it from this direct message?'
)
.onOk(async () => {
const restoredOrder = await this.restoreOrder(eventId)
if (restoredOrder) {
restoredOrder.expanded = true
restoredOrder.isNew = false
this.orders = [restoredOrder] // Show only restored order
}
})
return
}
// Show order details
order.expanded = true
order.isNew = false
this.orders = [order] // Focus on single order
}
```
### 2. Customer Association and Filtering
#### Customer Management Integration
```javascript
computed: {
customerOptions: function () {
const options = this.customers.map(c => ({
label: this.buildCustomerLabel(c), // Include unread message counts
value: c.public_key
}))
options.unshift({label: 'All', value: null, id: null}) // All customers option
return options
}
}
```
### 3. Shipping Zone Integration (`order-list.js:348-355`)
#### Dynamic Shipping Options
```javascript
getStallZones: function (stallId) {
const stall = this.stalls.find(s => s.id === stallId)
if (!stall) return []
return this.zoneOptions.filter(z =>
stall.shipping_zones.find(s => s.id === z.id) // Only zones supported by stall
)
}
```
## Database Operations
### 1. Order CRUD Operations (`crud.py`)
#### Order Creation
```python
async def create_order(merchant_id: str, o: Order) -> Order:
await db.execute(
"""
INSERT INTO nostrmarket.orders (
merchant_id, id, event_id, event_created_at, public_key,
merchant_public_key, contact_data, extra_data, order_items,
address, total, shipping_id, stall_id, invoice_id, paid, shipped
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
merchant_id, o.id, o.event_id, o.event_created_at, o.public_key,
o.merchant_public_key, json.dumps(o.contact.dict()),
json.dumps(o.extra.dict()), json.dumps([i.dict() for i in o.items]),
o.address, o.total, o.shipping_id, o.stall_id, o.invoice_id,
o.paid, o.shipped,
),
)
return o
```
#### Flexible Order Queries
```python
async def get_orders(merchant_id: str, **kwargs) -> List[Order]:
# Build dynamic WHERE clause from keyword arguments
q = " AND ".join(
[
f"{field[0]} = :{field[0]}"
for field in kwargs.items()
if field[1] is not None
]
)
rows: list[dict] = await db.fetchall(
f"SELECT * FROM nostrmarket.orders WHERE merchant_id = :merchant_id "
f"{' AND ' + q if q else ''} ORDER BY event_created_at DESC",
{"merchant_id": merchant_id, **kwargs},
)
return [Order.from_row(row) for row in rows]
```
### 2. Status Update Operations
#### Payment Status Updates
```python
async def update_order_paid_status(order_id: str, paid: bool) -> Optional[Order]:
await db.execute(
"UPDATE nostrmarket.orders SET paid = :paid WHERE id = :id",
{"paid": paid, "id": order_id},
)
row: dict = await db.fetchone(
"SELECT * FROM nostrmarket.orders WHERE id = :id", {"id": order_id}
)
return Order.from_row(row) if row else None
```
#### Shipping Status Updates
```python
async def update_order_shipped_status(
merchant_id: str, order_id: str, shipped: bool
) -> Optional[Order]:
await db.execute(
"""
UPDATE nostrmarket.orders
SET shipped = :shipped
WHERE merchant_id = :merchant_id AND id = :id
""",
{"shipped": shipped, "merchant_id": merchant_id, "id": order_id},
)
row: dict = await db.fetchone(
"SELECT * FROM nostrmarket.orders WHERE merchant_id = :merchant_id AND id = :id",
{"merchant_id": merchant_id, "id": order_id},
)
return Order.from_row(row) if row else None
```
## Error Handling and Edge Cases
### 1. Order Restoration Failures
- **Missing Products**: Orders reference products that no longer exist
- **Invalid Stall Association**: Product moved between stalls after order creation
- **Corrupted DM Data**: JSON parsing errors in message restoration
- **Payment Hash Conflicts**: Duplicate invoice IDs from reissuance
### 2. Payment Processing Issues
- **Invoice Expiration**: Lightning invoices expire after timeout
- **Partial Payments**: Underpayment or overpayment scenarios
- **Payment Verification**: Webhook delays or failures
- **Double Payment**: Multiple payments for same order
### 3. Inventory Synchronization
- **Race Conditions**: Multiple orders for limited stock
- **Negative Inventory**: Orders processed despite insufficient stock
- **Product Updates**: Price or availability changes after order placement
- **Stall Deactivation**: Orders for disabled stalls or products
## Integration Points
### 1. Payment System Integration
- **LNbits Invoice Creation**: Automatic Lightning invoice generation
- **Payment Monitoring**: Real-time payment confirmation via webhooks
- **Refund Processing**: Automated refunds for failed orders
- **Multi-currency Support**: Fiat pricing with BTC conversion
### 2. Inventory Management Integration
- **Stock Validation**: Pre-order inventory checking
- **Automatic Deduction**: Post-payment inventory updates
- **Backorder Handling**: Out-of-stock order management
- **Restock Notifications**: Customer alerts for inventory replenishment
### 3. Communication System Integration
- **Status Updates**: Automated customer notifications
- **Order Confirmations**: Receipt and tracking information
- **Shipping Notifications**: Fulfillment status updates
- **Support Integration**: Customer service ticket creation
This comprehensive order management system provides complete lifecycle tracking from initial order placement through final fulfillment, with robust error handling, real-time updates, and flexible merchant tools for efficient order processing and customer communication.

View file

@ -0,0 +1,109 @@
# Authentication System
This web application now uses LNBits username/password authentication instead of Nostr keypairs.
## Overview
The authentication system has been completely replaced with a traditional username/password system that integrates with LNBits. Users can now:
- Register new accounts with username and password
- Login with username/email and password
- Manage their profile information
- Logout securely
## Configuration
### Environment Variables
Create a `.env` file in the `web-app` directory with the following variables:
```env
# LNBits Base URL Configuration
# Set this to your LNBits instance base URL
# Example: http://localhost:5000 or https://your-lnbits-instance.com
VITE_LNBITS_BASE_URL=http://localhost:5000
# Enable debug logging for LNBits API calls
VITE_LNBITS_DEBUG=false
# App Configuration
VITE_APP_TITLE=Ario
VITE_APP_DESCRIPTION=Your secure platform for events and community management
```
### LNBits Setup
1. Ensure your LNBits instance is running and accessible
2. Make sure the username/password authentication method is enabled in LNBits
3. Configure CORS if your LNBits instance is on a different domain
## API Endpoints
The application uses the following LNBits API endpoints:
- `POST /api/v1/auth` - Login
- `POST /api/v1/auth/register` - Register new user
- `POST /api/v1/auth/logout` - Logout
- `GET /api/v1/auth` - Get current user
- `PUT /api/v1/auth/password` - Update password
- `PUT /api/v1/auth/update` - Update profile
## Components
### New Components
- `LoginDialog.vue` - Modal dialog for login/register
- `UserProfile.vue` - Display user information and logout
- `Login.vue` - Full-page login/register form
### Updated Components
- `App.vue` - Now uses new authentication system
- `Navbar.vue` - Shows user status and logout option
- `Home.vue` - Displays welcome message and user profile
## Authentication Flow
1. **App Initialization**: The app checks for existing authentication token on startup
2. **Route Protection**: Routes with `requiresAuth: true` redirect to login if not authenticated
3. **Login/Register**: Users can create accounts or login with existing credentials
4. **Token Management**: Access tokens are stored in localStorage and automatically included in API requests
5. **Logout**: Clears tokens and redirects to login page
## Security Features
- JWT tokens for session management
- Secure password handling (handled by LNBits)
- Automatic token refresh
- Route protection for authenticated pages
- Secure logout with token cleanup
## Migration from Nostr
The following components have been removed or replaced:
- `useIdentity.ts``useAuth.ts`
- `IdentityDialog.vue``LoginDialog.vue`
- `PasswordDialog.vue` → Integrated into `LoginDialog.vue`
- Nostr connection status → User authentication status
## Development
To run the application with the new authentication system:
1. Set up your LNBits instance
2. Configure the environment variables
3. Run the development server: `npm run dev`
4. Access the application and test login/register functionality
## Troubleshooting
### Common Issues
1. **CORS Errors**: Ensure your LNBits instance allows requests from your frontend domain
2. **Authentication Failures**: Check that username/password auth is enabled in LNBits
3. **API Connection**: Verify the `VITE_LNBITS_BASE_URL` is correct and points to your LNBits instance (without /api/v1)
### Debug Mode
Enable debug logging by setting `VITE_LNBITS_DEBUG=true` to see detailed API request/response information in the browser console.

View file

@ -0,0 +1,496 @@
# ⚙️ Core Services Overview
> **Shared infrastructure services** providing foundational functionality across all modules with reactive architecture, dependency injection, and lifecycle management.
## Table of Contents
- [[#Service Architecture]]
- [[#Available Services]]
- [[#Service Lifecycle]]
- [[#Dependency Injection]]
- [[#Service Development]]
- [[#Testing Services]]
## Service Architecture
### **BaseService Foundation**
All core services extend the `BaseService` abstract class which provides:
- **Reactive State Management** - Integration with Vue's reactivity system
- **Lifecycle Management** - Standardized initialization and disposal
- **Error Handling** - Consistent error patterns across services
- **Type Safety** - Full TypeScript support with strict typing
```typescript
abstract class BaseService {
protected isInitialized = ref(false)
protected isDisposed = ref(false)
abstract initialize(): Promise<void>
abstract dispose(): Promise<void>
// Reactive state helpers
protected createReactiveState<T>(initialValue: T): Ref<T>
protected createComputedState<T>(getter: () => T): ComputedRef<T>
}
```
### **Service Registration Pattern**
Services are registered in the dependency injection container during module installation:
```typescript
// Service registration (in base module)
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
container.provide(SERVICE_TOKENS.AUTH_SERVICE, authService)
container.provide(SERVICE_TOKENS.STORAGE_SERVICE, storageService)
// Service consumption (anywhere in app)
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
const auth = injectService(SERVICE_TOKENS.AUTH_SERVICE)
```
## Available Services
### **AuthService** 🔐
**Purpose:** User authentication and identity management
**Location:** `src/modules/base/auth/auth-service.ts`
**Token:** `SERVICE_TOKENS.AUTH_SERVICE`
**Key Features:**
- **Key Management** - Secure generation, import, and storage of Nostr keys
- **User Sessions** - Persistent authentication with encrypted storage
- **Profile Management** - User profile creation and updates
- **Security** - Client-side key handling with no server storage
**Reactive State:**
```typescript
interface AuthService {
user: Ref<NostrUser | null> // Current authenticated user
isAuthenticated: ComputedRef<boolean> // Authentication status
isLoading: Ref<boolean> // Loading state
loginError: Ref<string | null> // Login error message
}
```
**Key Methods:**
- `generateKeyPair()` - Create new Nostr key pair
- `loginWithPrivateKey(privateKey: string)` - Authenticate with existing key
- `logout()` - Clear session and user data
- `updateProfile(profile: UserMetadata)` - Update user profile
**See:** [[authentication|📖 Authentication Service Documentation]]
### **RelayHub** 🌐
**Purpose:** Centralized Nostr relay connection management
**Location:** `src/modules/base/nostr/relay-hub.ts`
**Token:** `SERVICE_TOKENS.RELAY_HUB`
**Key Features:**
- **Connection Management** - Automatic connection, reconnection, and failover
- **Event Publishing** - Reliable event publishing across multiple relays
- **Subscription Management** - Efficient event subscriptions with deduplication
- **Performance Monitoring** - Relay latency and success rate tracking
**Reactive State:**
```typescript
interface RelayHub {
connectedRelays: Ref<string[]> // Currently connected relays
connectionStatus: ComputedRef<ConnectionStatus> // Overall connection status
relayStats: Ref<Map<string, RelayStats>> // Per-relay statistics
isConnecting: Ref<boolean> // Connection in progress
}
```
**Key Methods:**
- `connect(relays: string[])` - Connect to relay URLs
- `publishEvent(event: NostrEvent)` - Publish event to all connected relays
- `subscribe(filters: Filter[], callback: EventCallback)` - Subscribe to events
- `getRelayInfo(url: string)` - Get relay connection information
**See:** [[../01-architecture/relay-hub|📖 Relay Hub Architecture Documentation]]
### **StorageService** 💾
**Purpose:** User-scoped local storage operations
**Location:** `src/core/services/StorageService.ts`
**Token:** `SERVICE_TOKENS.STORAGE_SERVICE`
**Key Features:**
- **User-Scoped Storage** - Automatic key prefixing per authenticated user
- **Type-Safe Operations** - Strongly typed get/set operations with JSON serialization
- **Reactive Updates** - Optional reactive storage with Vue refs
- **Migration Support** - Data migration between storage schema versions
**Key Methods:**
```typescript
interface StorageService {
setUserData<T>(key: string, data: T): void
getUserData<T>(key: string, defaultValue?: T): T | undefined
removeUserData(key: string): void
clearUserData(): void
// Reactive variants
getReactiveUserData<T>(key: string, defaultValue: T): Ref<T>
setReactiveUserData<T>(key: string, ref: Ref<T>): void
}
```
**Storage Patterns:**
- User-specific keys: `user:{pubkey}:settings`
- Global application keys: `app:theme`
- Module-specific keys: `user:{pubkey}:chat:contacts`
**See:** [[storage-service|📖 Storage Service Documentation]]
### **ToastService** 📢
**Purpose:** Application-wide notifications and user feedback
**Location:** `src/core/services/ToastService.ts`
**Token:** `SERVICE_TOKENS.TOAST_SERVICE`
**Key Features:**
- **Context-Specific Methods** - Pre-configured toasts for common scenarios
- **Consistent Messaging** - Standardized success, error, and info messages
- **Accessibility** - Screen reader compatible notifications
- **Customizable** - Support for custom toast content and actions
**Organized by Context:**
```typescript
interface ToastService {
// Authentication context
auth: {
loginSuccess(): void
loginError(error?: string): void
logoutSuccess(): void
keyGenerated(): void
}
// Payment context
payment: {
invoiceCreated(): void
paymentReceived(): void
paymentFailed(error?: string): void
}
// Clipboard operations
clipboard: {
copied(item?: string): void
copyFailed(): void
}
// General operations
operation: {
success(message: string): void
error(message: string): void
info(message: string): void
}
}
```
**See:** [[toast-service|📖 Toast Service Documentation]]
### **EventBus** 📡
**Purpose:** Inter-module communication and event coordination
**Location:** `src/core/services/EventBus.ts`
**Token:** `SERVICE_TOKENS.EVENT_BUS`
**Key Features:**
- **Type-Safe Events** - Strongly typed event payloads
- **Module Isolation** - Clean communication between modules
- **Event Namespacing** - Organized event names by domain
- **Subscription Management** - Easy subscribe/unsubscribe patterns
**Event Categories:**
```typescript
interface EventBusEvents {
// User events
'user:authenticated': { userId: string, profile: UserMetadata }
'user:profile-updated': { userId: string, changes: Partial<UserMetadata> }
'user:logout': { userId: string }
// Chat events
'chat:message-received': { messageId: string, from: string, content: string }
'chat:typing-start': { from: string, chatId: string }
// Payment events
'payment:invoice-created': { invoiceId: string, amount: number }
'payment:received': { invoiceId: string, amount: number }
// Relay events
'relay:connected': { url: string }
'relay:disconnected': { url: string, reason?: string }
}
```
**See:** [[../01-architecture/event-bus|📖 Event Bus Communication Documentation]]
## Service Lifecycle
### **Initialization Phase**
Services are initialized in dependency order during application startup:
```typescript
// 1. Base services (no dependencies)
await authService.initialize()
await storageService.initialize()
// 2. Infrastructure services (depend on base services)
await relayHub.initialize()
await toastService.initialize()
// 3. Feature services (depend on infrastructure)
await chatService.initialize()
await eventsService.initialize()
```
### **Service Dependencies**
Services declare their dependencies through constructor injection:
```typescript
class ChatService extends BaseService {
constructor(
private auth = injectService(SERVICE_TOKENS.AUTH_SERVICE),
private relayHub = injectService(SERVICE_TOKENS.RELAY_HUB),
private storage = injectService(SERVICE_TOKENS.STORAGE_SERVICE),
private eventBus = injectService(SERVICE_TOKENS.EVENT_BUS)
) {
super()
}
}
```
### **Disposal Phase**
Services are disposed in reverse dependency order during application shutdown:
```typescript
async dispose(): Promise<void> {
// Clean up subscriptions
this.subscriptions.forEach(sub => sub.close())
// Save persistent state
await this.storage.setUserData('chat:messages', this.messages.value)
// Mark as disposed
this.isDisposed.value = true
}
```
## Dependency Injection
### **Service Tokens**
Type-safe service tokens prevent runtime errors and enable proper TypeScript inference:
```typescript
export const SERVICE_TOKENS = {
AUTH_SERVICE: Symbol('AUTH_SERVICE') as InjectionKey<AuthService>,
RELAY_HUB: Symbol('RELAY_HUB') as InjectionKey<RelayHub>,
STORAGE_SERVICE: Symbol('STORAGE_SERVICE') as InjectionKey<StorageService>,
TOAST_SERVICE: Symbol('TOAST_SERVICE') as InjectionKey<ToastService>,
} as const
```
### **Service Registration**
Services are registered during module installation:
```typescript
// In base module installation
export async function installBaseModule(app: App) {
// Create service instances
const authService = new AuthService()
const relayHub = new RelayHub()
const storageService = new StorageService()
// Register in container
container.provide(SERVICE_TOKENS.AUTH_SERVICE, authService)
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
container.provide(SERVICE_TOKENS.STORAGE_SERVICE, storageService)
// Initialize services
await authService.initialize()
await relayHub.initialize()
}
```
### **Service Consumption**
Services are injected where needed using type-safe injection:
```typescript
// In composables
export function useAuth() {
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE)
return {
user: authService.user,
login: authService.login,
logout: authService.logout
}
}
// In components
<script setup>
const toast = injectService(SERVICE_TOKENS.TOAST_SERVICE)
const handleSuccess = () => toast.operation.success('Action completed!')
</script>
```
## Service Development
### **Creating a New Service**
#### 1. Service Class Implementation
```typescript
// src/core/services/MyService.ts
export class MyService extends BaseService {
// Reactive state
private readonly _data = ref<MyData[]>([])
private readonly _isLoading = ref(false)
// Public readonly access to state
public readonly data = readonly(this._data)
public readonly isLoading = readonly(this._isLoading)
constructor(
private dependency = injectService(SERVICE_TOKENS.DEPENDENCY)
) {
super()
}
async initialize(): Promise<void> {
// Initialization logic
await this.loadInitialData()
this.isInitialized.value = true
}
async dispose(): Promise<void> {
// Cleanup logic
this._data.value = []
this.isDisposed.value = true
}
// Public methods
async createItem(item: CreateItemRequest): Promise<MyData> {
this._isLoading.value = true
try {
const newItem = await this.dependency.create(item)
this._data.value.push(newItem)
return newItem
} finally {
this._isLoading.value = false
}
}
}
```
#### 2. Service Token Registration
```typescript
// Add to SERVICE_TOKENS
export const SERVICE_TOKENS = {
// ... existing tokens
MY_SERVICE: Symbol('MY_SERVICE') as InjectionKey<MyService>,
} as const
```
#### 3. Service Registration in Module
```typescript
// In module installation
const myService = new MyService()
container.provide(SERVICE_TOKENS.MY_SERVICE, myService)
await myService.initialize()
```
### **Service Best Practices**
#### **Reactive State Management**
- Use `ref()` for mutable state, `readonly()` for public access
- Provide computed properties for derived state
- Use `watch()` and `watchEffect()` for side effects
#### **Error Handling**
- Throw descriptive errors with proper types
- Use try/catch blocks with proper cleanup
- Log errors appropriately for debugging
#### **Performance Optimization**
- Implement proper subscription cleanup in `dispose()`
- Use debouncing for frequent operations
- Cache expensive computations with `computed()`
## Testing Services
### **Service Unit Tests**
```typescript
// tests/unit/services/MyService.test.ts
describe('MyService', () => {
let service: MyService
let mockDependency: MockType<DependencyService>
beforeEach(() => {
// Setup mocks
mockDependency = createMockService()
// Create service with mocks
service = new MyService(mockDependency)
})
afterEach(async () => {
await service.dispose()
})
it('should initialize correctly', async () => {
await service.initialize()
expect(service.isInitialized.value).toBe(true)
expect(service.data.value).toEqual([])
})
it('should create items', async () => {
await service.initialize()
const item = await service.createItem({ name: 'test' })
expect(service.data.value).toContain(item)
expect(mockDependency.create).toHaveBeenCalledWith({ name: 'test' })
})
})
```
### **Integration Tests**
```typescript
// tests/integration/services/ServiceIntegration.test.ts
describe('Service Integration', () => {
let container: DIContainer
beforeEach(async () => {
container = createTestContainer()
await installTestServices(container)
})
it('should handle cross-service communication', async () => {
const authService = container.get(SERVICE_TOKENS.AUTH_SERVICE)
const chatService = container.get(SERVICE_TOKENS.CHAT_SERVICE)
await authService.login('test-key')
const message = await chatService.sendMessage('Hello')
expect(message.author).toBe(authService.user.value?.pubkey)
})
})
```
## See Also
### Service Documentation
- **[[authentication|🔐 Authentication Service]]** - User identity and session management
- **[[storage-service|💾 Storage Service]]** - User-scoped data persistence
- **[[toast-service|📢 Toast Service]]** - User notifications and feedback
- **[[visibility-service|👁️ Visibility Service]]** - Dynamic UI component control
### Architecture References
- **[[../01-architecture/dependency-injection|⚙️ Dependency Injection]]** - DI container system
- **[[../01-architecture/event-bus|📡 Event Bus Communication]]** - Inter-service messaging
- **[[../02-modules/index|📦 Module System]]** - How services integrate with modules
- **[[../04-development/testing|🧪 Testing Guide]]** - Service testing patterns
---
**Tags:** #services #architecture #dependency-injection #reactive-state
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,169 @@
# Project Improvements
## Current Strengths 💪
### Project Structure
- Clean, modular organization with separate directories for components, pages, i18n, etc.
- Follows Vue 3 best practices with Composition API
- Good separation of concerns between layout components and pages
### Internationalization
- Type-safe i18n system
- Lazy loading of language files
- Persistent language preferences
- Clean language switcher UI with Shadcn components
### Theming
- Dark/light theme implementation with smooth transitions
- Consistent theme colors using Tailwind's color system
- Theme persistence
- Proper theme-aware components
### Components
- Shadcn components properly integrated
- Reusable UI components in dedicated directories
- TypeScript interfaces for props
- Responsive design patterns
### Performance
- PWA support with periodic updates
- Code splitting configuration
- Image optimization
- Lazy loading implementation
## Planned Improvements 🎯
### 1. Testing Implementation
- [ ] Set up Vitest for unit testing
- [ ] Set up Cypress for E2E testing
- [ ] Add @testing-library/vue for component testing
- [ ] Write tests for critical components
- [ ] Set up CI/CD pipeline for automated testing
### 2. Error Handling
- [ ] Implement global error boundary
```typescript
app.config.errorHandler = (err, instance, info) => {
// Log error to service
console.error(err)
}
```
- [ ] Add error states for async operations
- [ ] Implement error logging service
- [ ] Add retry mechanisms for failed API calls
### 3. Performance Monitoring
- [ ] Implement Web Vitals monitoring
```typescript
import { onCLS, onFID, onLCP } from 'web-vitals'
function sendToAnalytics({ name, delta, id }) {
// Send metrics to analytics
}
onCLS(sendToAnalytics)
onFID(sendToAnalytics)
onLCP(sendToAnalytics)
```
- [ ] Set up performance budgets
- [ ] Implement automated performance testing
- [ ] Add bundle size monitoring
### 4. Documentation
- [ ] Set up Storybook for component documentation
- [ ] Implement TypeDoc for API documentation
- [ ] Create comprehensive README
- [ ] Add contribution guidelines
- [ ] Document component usage examples
- [ ] Add inline code documentation
### 5. SEO Optimization
- [ ] Implement meta tags management
- [ ] Add OpenGraph tags
- [ ] Generate sitemap
- [ ] Implement structured data
- [ ] Add robots.txt configuration
### 6. Accessibility
- [ ] Add missing ARIA labels
- [ ] Implement keyboard navigation
- [ ] Add skip links
- [ ] Ensure proper color contrast
- [ ] Add screen reader announcements
- [ ] Test with screen readers
### 7. Type Safety Enhancements
- [ ] Add comprehensive TypeScript interfaces for store state
- [ ] Implement strict prop types for all components
- [ ] Add runtime type checking for API responses
- [ ] Enhance type safety in route definitions
### 8. Security
- [ ] Implement CSP (Content Security Policy)
- [ ] Add XSS protection
- [ ] Set up security headers
- [ ] Implement rate limiting
- [ ] Add input sanitization
### 9. Build & Development
- [ ] Optimize build configuration
- [ ] Add development tools and utilities
- [ ] Implement git hooks for code quality
- [ ] Set up automated dependency updates
- [ ] Add development environment documentation
## Priority Order 📋
1. **High Priority**
- Testing Implementation
- Error Handling
- Security Improvements
2. **Medium Priority**
- Documentation
- Type Safety Enhancements
- Accessibility Improvements
3. **Lower Priority**
- Performance Monitoring
- SEO Optimization
- Build & Development Optimizations
## Getting Started 🚀
To begin implementing these improvements:
1. Start with testing setup:
```bash
# Install testing dependencies
npm install -D vitest @testing-library/vue cypress
```
2. Add error handling:
```bash
# Install error tracking
npm install @sentry/vue
```
3. Set up documentation:
```bash
# Install Storybook
npx storybook@latest init
```
## Contributing 🤝
When implementing these improvements:
1. Create a feature branch
2. Follow the existing code style
3. Add tests for new features
4. Update documentation
5. Submit a PR with a clear description
## Notes 📝
- Keep track of completed items by checking them off
- Add new improvements as they are identified
- Update priorities based on project needs
- Document any technical decisions made during implementation

View file

@ -0,0 +1,571 @@
# 💻 Development Guide
> **Development workflows and standards** for contributing to Ario's modular Vue 3 + TypeScript application with Nostr and Lightning Network integration.
## Table of Contents
- [[#Development Environment]]
- [[#Development Workflow]]
- [[#Code Standards]]
- [[#Testing Strategy]]
- [[#Build & Deployment]]
- [[#Debugging Guide]]
## Development Environment
### **Prerequisites**
- **Node.js 18+** and npm for package management
- **Git** for version control
- **VS Code** (recommended) with Vue and TypeScript extensions
- **Basic understanding** of Vue 3, TypeScript, and Nostr protocol
### **Quick Setup**
```bash
# Navigate to web app directory
cd web-app/
# Install dependencies
npm install
# Start development server with hot reload
npm run dev
# For desktop development
npm run electron:dev
```
### **Environment Configuration**
```bash
# Copy environment template
cp .env.example .env
# Essential configuration
VITE_NOSTR_RELAYS='["wss://relay.damus.io","wss://nos.lol"]'
VITE_ADMIN_PUBKEYS='["admin_pubkey_hex"]'
# Optional configuration
VITE_DEBUG=true
VITE_APP_NAME="Ario Development"
```
### **Development Tools**
#### **Recommended VS Code Extensions**
- **Vue Language Features (Volar)** - Vue 3 support
- **TypeScript Vue Plugin (Volar)** - TypeScript integration
- **Tailwind CSS IntelliSense** - CSS class completion
- **ESLint** - Code linting
- **Prettier** - Code formatting
- **Auto Rename Tag** - HTML tag synchronization
#### **Browser Extensions**
- **Vue.js devtools** - Component inspection and debugging
- **Lightning Network Browser Extension** - WebLN testing
### **Project Structure Overview**
```
web-app/
├── src/
│ ├── components/ # Reusable UI components
│ ├── composables/ # Vue composables and hooks
│ ├── core/ # Core architecture (DI, services)
│ ├── lib/ # Utility functions and helpers
│ ├── modules/ # Feature modules (plugin-based)
│ ├── pages/ # Route pages
│ ├── stores/ # Pinia state management
│ └── types/ # TypeScript type definitions
├── docs/ # Documentation (Obsidian-style)
├── public/ # Static assets
├── electron/ # Electron main process
└── tests/ # Test files
```
## Development Workflow
### **Feature Development Process**
#### **1. Planning & Design**
- Review existing modules and services for reusable functionality
- Design module interfaces and dependencies
- Create or update relevant documentation
#### **2. Implementation**
```bash
# Create feature branch
git checkout -b feature/my-new-feature
# Implement feature following modular architecture
# - Create module plugin if needed
# - Implement services with BaseService pattern
# - Use dependency injection for service access
# - Follow TypeScript and Vue 3 best practices
# Test implementation
npm run dev
npm run test:unit # If tests exist
```
#### **3. Code Review & Testing**
```bash
# Run quality checks
npm run build # Ensure TypeScript compilation
npm run lint # Check code style
npm run format # Auto-format code
# Test across platforms
npm run preview # Test production build
npm run electron:dev # Test desktop version
```
#### **4. Documentation & Commit**
```bash
# Update relevant documentation
# Add JSDoc comments for public APIs
# Update CHANGELOG.md if applicable
# Commit with conventional commits
git add .
git commit -m "feat: add new feature functionality"
```
### **Daily Development Tasks**
#### **Starting Development Session**
```bash
cd web-app/
npm run dev # Starts Vite dev server on http://localhost:5173
```
#### **Development Server Features**
- **Hot Module Replacement (HMR)** - Instant updates without page reload
- **TypeScript Checking** - Compile-time error detection
- **Import Analysis** - Automatic dependency resolution
- **Source Maps** - Debug original TypeScript code in browser
#### **Common Development Commands**
```bash
# Development
npm run dev # Start dev server
npm run dev:host # Dev server accessible on network
# Building
npm run build # Production build with TypeScript check
npm run preview # Preview production build locally
# Code Quality
npm run lint # ESLint checking
npm run lint:fix # Auto-fix linting issues
npm run format # Prettier formatting
# Electron
npm run electron:dev # Concurrent Vite + Electron development
npm run electron:build # Package desktop application
# Analysis
npm run analyze # Bundle analyzer
```
## Code Standards
### **TypeScript Guidelines**
#### **Strict Type Checking**
```typescript
// ✅ Use explicit types for public APIs
interface UserProfile {
pubkey: string
name?: string
about?: string
}
// ✅ Use type guards for runtime validation
function isNostrEvent(obj: unknown): obj is NostrEvent {
return typeof obj === 'object' && obj !== null && 'kind' in obj
}
// ❌ Avoid 'any' type
function processData(data: any): any { }
// ✅ Use proper generic constraints
function processData<T extends NostrEvent>(data: T): T { }
```
#### **Interface vs Type Preferences**
```typescript
// ✅ Use interfaces for extensible objects
interface ModulePlugin {
name: string
install(app: App): Promise<void>
}
interface MyModulePlugin extends ModulePlugin {
customConfig?: MyConfig
}
// ✅ Use type aliases for unions and computations
type EventKind = 0 | 1 | 3 | 4 | 5 | 6 | 7
type ServiceToken = keyof typeof SERVICE_TOKENS
```
### **Vue 3 Patterns**
#### **Composition API with Script Setup**
```vue
<script setup lang="ts">
// ✅ Use <script setup> syntax
import { ref, computed, onMounted } from 'vue'
// Props with TypeScript
interface Props {
userId: string
initialData?: UserData
}
const props = defineProps<Props>()
// Reactive state
const data = ref<UserData[]>([])
const loading = ref(false)
// Computed properties
const filteredData = computed(() =>
data.value.filter(item => item.userId === props.userId)
)
// Lifecycle hooks
onMounted(async () => {
await loadUserData()
})
</script>
```
#### **Service Integration**
```vue
<script setup lang="ts">
// ✅ Use dependency injection for services
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE)
const toast = injectService(SERVICE_TOKENS.TOAST_SERVICE)
// ✅ Destructure reactive properties
const { user, isAuthenticated } = authService
// ✅ Handle async operations properly
const handleLogin = async () => {
try {
await authService.login(privateKey.value)
toast.auth.loginSuccess()
} catch (error) {
toast.auth.loginError(error.message)
}
}
</script>
```
### **Module Development Standards**
#### **Module Structure**
```typescript
// ✅ Proper module plugin implementation
export const myModule: ModulePlugin = {
name: 'my-module',
version: '1.0.0',
dependencies: ['base'], // Always depend on base
async install(app: App, options?: MyModuleConfig) {
// Register services
const myService = new MyService()
container.provide(SERVICE_TOKENS.MY_SERVICE, myService)
// Register components
app.component('MyComponent', MyComponent)
// Initialize
await myService.initialize()
},
routes: [
{
path: '/my-feature',
component: () => import('./views/MyFeatureView.vue')
}
]
}
```
#### **Service Development**
```typescript
// ✅ Extend BaseService for consistency
export class MyService extends BaseService {
private readonly _data = ref<MyData[]>([])
public readonly data = readonly(this._data)
constructor(
private auth = injectService(SERVICE_TOKENS.AUTH_SERVICE)
) {
super()
}
async initialize(): Promise<void> {
// Initialization logic
this.isInitialized.value = true
}
async dispose(): Promise<void> {
// Cleanup logic
this.isDisposed.value = true
}
}
```
### **Naming Conventions**
#### **Files and Directories**
```
# ✅ Use kebab-case for files and directories
my-component.vue
my-service.ts
my-module-config.ts
# ✅ Component files use PascalCase base name
UserProfileCard.vue
EventTicketPurchase.vue
```
#### **Variables and Functions**
```typescript
// ✅ Use camelCase for variables and functions
const userName = ref('')
const isAuthenticated = computed(() => !!user.value)
async function handleUserLogin(): Promise<void> { }
// ✅ Use PascalCase for classes and interfaces
class AuthService extends BaseService { }
interface UserProfile { }
```
#### **Constants and Types**
```typescript
// ✅ Use SCREAMING_SNAKE_CASE for constants
const MAX_MESSAGE_LENGTH = 1000
const DEFAULT_RELAY_URLS = ['wss://relay.example.com']
// ✅ Use PascalCase for types and enums
type NostrEventKind = 0 | 1 | 3 | 4
enum ConnectionStatus { Connected, Connecting, Disconnected }
```
## Testing Strategy
### **Testing Philosophy**
- **Unit Tests** - Test individual components and services in isolation
- **Integration Tests** - Test service interactions and module integration
- **E2E Tests** - Test complete user workflows (planned)
- **Manual Testing** - Cross-platform testing and user experience validation
### **Unit Testing Setup**
```typescript
// tests/unit/services/MyService.test.ts
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { MyService } from '@/services/MyService'
import { createMockDIContainer } from '@/tests/helpers'
describe('MyService', () => {
let service: MyService
let mockAuth: MockAuthService
beforeEach(() => {
const container = createMockDIContainer()
mockAuth = container.get(SERVICE_TOKENS.AUTH_SERVICE)
service = new MyService()
})
afterEach(async () => {
await service.dispose()
})
it('should initialize correctly', async () => {
await service.initialize()
expect(service.isInitialized.value).toBe(true)
})
})
```
### **Component Testing**
```typescript
// tests/unit/components/MyComponent.test.ts
import { mount } from '@vue/test-utils'
import { createMockDIContainer } from '@/tests/helpers'
import MyComponent from '@/components/MyComponent.vue'
describe('MyComponent', () => {
it('should render correctly', () => {
const wrapper = mount(MyComponent, {
props: { userId: 'test-user' },
global: {
plugins: [createMockDIContainer()]
}
})
expect(wrapper.text()).toContain('test-user')
})
})
```
## Build & Deployment
### **Development Builds**
```bash
# Development server (with HMR)
npm run dev
# Development build (for debugging)
npm run build:dev
```
### **Production Builds**
```bash
# Full production build
npm run build
# Preview production build locally
npm run preview
# Bundle analysis
npm run analyze
```
### **Electron Builds**
```bash
# Development
npm run electron:dev
# Production packaging
npm run electron:build
# Platform-specific builds
npm run build:win # Windows
npm run build:mac # macOS
npm run build:linux # Linux
```
### **Build Configuration**
#### **Vite Configuration** (`vite.config.ts`)
- **TypeScript compilation** with vue-tsc
- **Bundle optimization** with manual chunking
- **PWA integration** with service worker
- **Asset optimization** with image processing
#### **Electron Configuration** (`forge.config.js`)
- **Cross-platform packaging** with Electron Forge
- **Auto-updater integration** for seamless updates
- **Code signing** for distribution (production)
## Debugging Guide
### **Development Debugging**
#### **Vue DevTools**
- Install Vue DevTools browser extension
- Inspect component state and props
- Monitor Pinia store mutations
- Track component lifecycle events
#### **Browser Developer Tools**
- Use TypeScript source maps for original code debugging
- Network tab for Nostr relay communication
- Console tab for service logging
- Application tab for localStorage inspection
#### **Service Debugging**
```typescript
// Add debug logging to services
export class MyService extends BaseService {
private debug = (message: string, ...args: unknown[]) => {
if (import.meta.env.VITE_DEBUG) {
console.log(`[MyService] ${message}`, ...args)
}
}
async performAction(): Promise<void> {
this.debug('Performing action...')
// Implementation
this.debug('Action completed')
}
}
```
### **Nostr Debugging**
#### **Relay Communication**
```typescript
// Log Nostr events for debugging
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
relayHub.subscribe(filters, (event) => {
console.log('Received Nostr event:', event)
})
// Debug relay connections
console.log('Connected relays:', relayHub.connectedRelays.value)
```
#### **Event Publishing**
```typescript
// Debug event publishing
try {
await relayHub.publishEvent(event)
console.log('Event published successfully:', event)
} catch (error) {
console.error('Failed to publish event:', error)
}
```
### **Common Issues & Solutions**
#### **TypeScript Errors**
```bash
# Clear TypeScript cache
rm -rf node_modules/.cache
rm -rf dist
# Restart TypeScript language server in VS Code
Cmd/Ctrl + Shift + P > "TypeScript: Restart TS Server"
```
#### **Module Import Issues**
```typescript
// ❌ Incorrect relative import
import { MyService } from '../../services/MyService'
// ✅ Use absolute imports with @ alias
import { MyService } from '@/services/MyService'
```
#### **Dependency Injection Issues**
```typescript
// ❌ Service not registered
const service = injectService(SERVICE_TOKENS.MY_SERVICE) // Error!
// ✅ Ensure service is registered in module installation
container.provide(SERVICE_TOKENS.MY_SERVICE, new MyService())
```
## See Also
### Development Documentation
- **[[setup|🛠️ Environment Setup]]** - Detailed setup instructions
- **[[coding-standards|📋 Coding Standards]]** - Comprehensive style guide
- **[[testing|🧪 Testing Guide]]** - Testing frameworks and patterns
- **[[debugging|🐛 Debugging Techniques]]** - Advanced debugging strategies
### Architecture References
- **[[../02-modules/index|📦 Module System]]** - Plugin-based architecture
- **[[../03-core-services/index|⚙️ Core Services]]** - Service development patterns
- **[[../01-architecture/dependency-injection|⚙️ Dependency Injection]]** - DI container usage
---
**Tags:** #development #workflow #standards #testing #debugging
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,444 @@
# Mobile Relay Connection Management Evaluation
## Current Implementation Analysis
### ✅ **Strengths - What We're Doing Well**
#### 1. **Basic Mobile Visibility Handling**
- **Page Visibility API**: Uses `document.visibilitychange` to detect when app goes to background/foreground
- **Network Status Monitoring**: Listens for `online`/`offline` events
- **Health Check Integration**: Performs health checks when page becomes visible
#### 2. **Automatic Reconnection Logic**
- **Exponential Backoff**: Implements reconnection attempts with delays
- **Max Attempts**: Prevents infinite reconnection loops (max 5 attempts)
- **Health Monitoring**: 30-second intervals for connection health checks
#### 3. **Connection Pool Management**
- **SimplePool Integration**: Uses `nostr-tools` SimplePool for efficient relay management
- **Priority-based Connection**: Connects to relays in priority order
- **Graceful Degradation**: Continues working with partial relay connections
### ⚠️ **Areas for Improvement - Mobile-Specific Issues**
#### 1. **WebSocket Lifecycle Management**
```typescript
// Current implementation in relayHub.ts:424-456
private setupMobileVisibilityHandling(): void {
if (typeof document !== 'undefined') {
this.mobileVisibilityHandler = () => {
if (document.hidden) {
console.log('Page hidden, maintaining WebSocket connections')
// ❌ PROBLEM: Just logs, doesn't optimize connections
} else {
console.log('Page visible, resuming normal WebSocket activity')
this.performHealthCheck() // ✅ Good: Checks health on resume
}
}
}
}
```
**Issues:**
- **No Connection Optimization**: When app goes to background, WebSockets remain fully active
- **Battery Drain**: Maintains all subscriptions and connections in background
- **No Smart Throttling**: Doesn't reduce activity when not visible
#### 2. **Mobile App State Handling**
- **Missing PWA Support**: No handling for `beforeinstallprompt`, `appinstalled` events
- **No Service Worker Integration**: Missing offline/background sync capabilities
- **Limited Mobile-Specific Events**: Doesn't handle `pagehide`/`pageshow` for better mobile detection
#### 3. **Connection Resilience**
- **Health Check Limitations**: Current health check only verifies relay presence, not actual WebSocket health
- **No Ping/Pong**: Missing WebSocket-level health verification
- **Subscription Recovery**: Subscriptions aren't automatically restored after reconnection
## 🔧 **Recommended Improvements**
### 1. **Enhanced Mobile Visibility Handling**
```typescript
private setupMobileVisibilityHandling(): void {
if (typeof document !== 'undefined') {
this.mobileVisibilityHandler = () => {
if (document.hidden) {
console.log('Page hidden, optimizing WebSocket connections...')
this.optimizeForBackground()
} else {
console.log('Page visible, restoring full WebSocket activity...')
this.restoreFullActivity()
}
}
// Add mobile-specific events
document.addEventListener('visibilitychange', this.mobileVisibilityHandler)
window.addEventListener('pagehide', this.handlePageHide)
window.addEventListener('pageshow', this.handlePageShow)
}
// Handle network changes
if (typeof window !== 'undefined') {
window.addEventListener('online', this.handleNetworkOnline)
window.addEventListener('offline', this.handleNetworkOffline)
// Add connection quality monitoring
if ('connection' in navigator) {
navigator.connection?.addEventListener('change', this.handleConnectionChange)
}
}
}
private optimizeForBackground(): void {
// Reduce WebSocket activity in background
this.backgroundMode = true
// Keep only essential connections
this.maintainEssentialConnections()
// Reduce health check frequency
this.adjustHealthCheckInterval(60000) // 1 minute instead of 30 seconds
// Suspend non-critical subscriptions
this.suspendNonCriticalSubscriptions()
}
private restoreFullActivity(): void {
this.backgroundMode = false
// Restore all connections
this.restoreAllConnections()
// Resume normal health check frequency
this.adjustHealthCheckInterval(30000)
// Restore all subscriptions
this.restoreAllSubscriptions()
// Perform immediate health check
this.performHealthCheck()
}
```
### 2. **Smart Connection Management**
```typescript
interface ConnectionStrategy {
mode: 'foreground' | 'background' | 'critical'
maxConnections: number
healthCheckInterval: number
subscriptionLimit: number
}
private connectionStrategies: Map<string, ConnectionStrategy> = new Map([
['foreground', { mode: 'foreground', maxConnections: 5, healthCheckInterval: 30000, subscriptionLimit: 20 }],
['background', { mode: 'background', maxConnections: 2, healthCheckInterval: 60000, subscriptionLimit: 5 }],
['critical', { mode: 'critical', maxConnections: 1, healthCheckInterval: 120000, subscriptionLimit: 2 }]
])
private maintainEssentialConnections(): void {
const strategy = this.connectionStrategies.get('background')!
// Keep only the most reliable relays
const essentialRelays = this.getMostReliableRelays(strategy.maxConnections)
// Disconnect from non-essential relays
this.disconnectNonEssentialRelays(essentialRelays)
// Maintain only critical subscriptions
this.maintainCriticalSubscriptions(strategy.subscriptionLimit)
}
```
### 3. **Enhanced Health Checking**
```typescript
private async performHealthCheck(): Promise<void> {
if (!this._isConnected) return
console.log('Performing enhanced relay health check...')
const disconnectedRelays: string[] = []
const unhealthyRelays: string[] = []
for (const [url, relay] of this.connectedRelays) {
try {
// Test actual WebSocket health with ping/pong
const isHealthy = await this.testRelayHealth(relay)
if (!isHealthy) {
unhealthyRelays.push(url)
}
// Update relay status
this.updateRelayStatus(url, {
lastSeen: Date.now(),
latency: await this.measureRelayLatency(relay)
})
} catch (error) {
console.warn(`Health check failed for relay ${url}:`, error)
disconnectedRelays.push(url)
}
}
// Handle unhealthy relays
unhealthyRelays.forEach(url => {
console.log(`Relay ${url} is unhealthy, attempting reconnection...`)
this.reconnectRelay(url)
})
// Handle disconnected relays
this.handleDisconnectedRelays(disconnectedRelays)
}
private async testRelayHealth(relay: Relay): Promise<boolean> {
try {
// Send a ping event to test WebSocket health
const pingEvent = { kind: 1, content: 'ping', created_at: Math.floor(Date.now() / 1000) }
await relay.send(pingEvent)
// Wait for response or timeout
return new Promise((resolve) => {
const timeout = setTimeout(() => resolve(false), 5000)
const onMessage = (event: any) => {
if (event.kind === 1 && event.content === 'pong') {
clearTimeout(timeout)
relay.off('message', onMessage)
resolve(true)
}
}
relay.on('message', onMessage)
})
} catch (error) {
return false
}
}
```
### 4. **Subscription Recovery System**
```typescript
interface SubscriptionState {
id: string
filters: Filter[]
relays?: string[]
onEvent?: (event: Event) => void
onEose?: () => void
onClose?: () => void
isActive: boolean
lastActivity: number
}
private subscriptionStates: Map<string, SubscriptionState> = new Map()
subscribe(config: SubscriptionConfig): () => void {
// Store subscription state for recovery
this.subscriptionStates.set(config.id, {
...config,
isActive: true,
lastActivity: Date.now()
})
// Perform actual subscription
const unsubscribe = this.performSubscription(config)
return () => {
this.subscriptionStates.get(config.id)!.isActive = false
unsubscribe()
}
}
private restoreAllSubscriptions(): void {
console.log('Restoring all subscriptions...')
for (const [id, state] of this.subscriptionStates) {
if (state.isActive) {
console.log(`Restoring subscription: ${id}`)
this.performSubscription(state)
}
}
}
```
### 5. **Mobile-Specific Event Handling**
```typescript
private handlePageHide = (event: PageTransitionEvent): void => {
// Handle mobile app backgrounding
if (event.persisted) {
console.log('Page is being cached (mobile background)')
this.optimizeForBackground()
} else {
console.log('Page is being unloaded')
this.prepareForUnload()
}
}
private handlePageShow = (event: PageTransitionEvent): void => {
console.log('Page is being shown (mobile foreground)')
// Check if we need to reconnect
if (!this._isConnected) {
console.log('Reconnecting after page show...')
this.connect()
} else {
this.restoreFullActivity()
}
}
private handleConnectionChange = (event: Event): void => {
const connection = (event.target as any) as NetworkInformation
console.log(`Connection type changed: ${connection.effectiveType}`)
// Adjust connection strategy based on network quality
if (connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g') {
this.setConnectionStrategy('critical')
} else if (connection.effectiveType === '3g') {
this.setConnectionStrategy('background')
} else {
this.setConnectionStrategy('foreground')
}
}
```
## 📱 **Mobile-Specific Best Practices Implementation**
### 1. **Battery-Aware Connection Management**
```typescript
private isLowBattery = false
private setupBatteryMonitoring(): void {
if ('getBattery' in navigator) {
navigator.getBattery().then(battery => {
battery.addEventListener('levelchange', () => {
this.isLowBattery = battery.level < 0.2
this.adjustConnectionStrategy()
})
battery.addEventListener('chargingchange', () => {
this.adjustConnectionStrategy()
})
})
}
}
private adjustConnectionStrategy(): void {
if (this.isLowBattery && !this.isCharging) {
this.setConnectionStrategy('critical')
} else {
this.setConnectionStrategy(this.backgroundMode ? 'background' : 'foreground')
}
}
```
### 2. **Progressive Web App Support**
```typescript
private setupPWASupport(): void {
// Handle app installation
window.addEventListener('beforeinstallprompt', (event) => {
console.log('App can be installed')
this.installPrompt = event
})
// Handle app installation completion
window.addEventListener('appinstalled', () => {
console.log('App was installed')
this.isInstalled = true
})
// Handle app launch from installed state
if (window.matchMedia('(display-mode: standalone)').matches) {
console.log('App is running in standalone mode')
this.isStandalone = true
}
}
```
### 3. **Service Worker Integration**
```typescript
// In service worker
self.addEventListener('sync', (event) => {
if (event.tag === 'relay-reconnect') {
event.waitUntil(reconnectToRelays())
}
})
// In relay hub
private scheduleBackgroundReconnect(): void {
if ('serviceWorker' in navigator && 'sync' in window.ServiceWorkerRegistration.prototype) {
navigator.serviceWorker.ready.then(registration => {
registration.sync.register('relay-reconnect')
})
}
}
```
## 🧪 **Testing Recommendations**
### 1. **Mobile Device Testing**
- Test on actual iOS/Android devices
- Test background/foreground transitions
- Test network switching (WiFi ↔ Cellular)
- Test low battery scenarios
### 2. **Simulation Testing**
```typescript
// Test visibility changes
document.dispatchEvent(new Event('visibilitychange'))
// Test network changes
window.dispatchEvent(new Event('offline'))
window.dispatchEvent(new Event('online'))
// Test page transitions
window.dispatchEvent(new PageTransitionEvent('pagehide', { persisted: true }))
window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: false }))
```
### 3. **Performance Monitoring**
```typescript
// Monitor connection efficiency
const connectionMetrics = {
connectionAttempts: 0,
successfulConnections: 0,
failedConnections: 0,
averageLatency: 0,
batteryImpact: 'low' // 'low' | 'medium' | 'high'
}
```
## 📊 **Current Implementation Score**
| Category | Score | Notes |
|----------|-------|-------|
| **Basic Mobile Support** | 6/10 | Has visibility API but limited optimization |
| **Connection Resilience** | 7/10 | Good reconnection logic, needs better health checks |
| **Battery Optimization** | 3/10 | No battery-aware connection management |
| **Network Adaptation** | 5/10 | Basic online/offline handling |
| **Subscription Recovery** | 4/10 | Subscriptions lost on reconnection |
| **Mobile-Specific Events** | 4/10 | Missing key mobile lifecycle events |
**Overall Score: 4.8/10**
## 🎯 **Priority Implementation Order**
1. **High Priority**: Enhanced health checking and subscription recovery
2. **Medium Priority**: Smart background/foreground connection management
3. **Low Priority**: Battery monitoring and PWA support
## 🚀 **Next Steps**
1. Implement enhanced health checking with ping/pong
2. Add subscription state persistence and recovery
3. Implement smart connection optimization for background mode
4. Add mobile-specific event handling
5. Test thoroughly on mobile devices
6. Monitor battery impact and connection efficiency
The current implementation provides a solid foundation but needs significant enhancement to meet mobile best practices for WebSocket management, battery optimization, and connection resilience.

View file

@ -0,0 +1,10 @@
● You're absolutely correct! The backend functionality is already there. We just need to add the UI components to interact with
the existing social functions.
The main missing pieces are:
1. Reply UI - buttons and forms to reply to notes
2. Reaction buttons - like/emoji buttons on each note
3. Note composition - form to write and publish new notes
Would you like me to start implementing these UI components? We could begin with adding reply and reaction buttons to the
existing NostrFeed component since the backend functions are already available.

View file

@ -0,0 +1,479 @@
# 📡 API Reference
> **External integrations and protocol implementations** powering Ario's decentralized architecture with Nostr protocol, Lightning Network, and third-party service APIs.
## Table of Contents
- [[#Protocol Implementations]]
- [[#External APIs]]
- [[#Service Integrations]]
- [[#SDK References]]
- [[#Authentication & Security]]
- [[#Rate Limits & Best Practices]]
## Protocol Implementations
### **Nostr Protocol**
**Specification:** [NIPs (Nostr Implementation Possibilities)](https://github.com/nostr-protocol/nips)
**Client Library:** `nostr-tools` v2.x
**Implementation:** Custom RelayHub service with connection pooling
**Supported NIPs:**
- **NIP-01** - Basic protocol flow and event structure
- **NIP-02** - Contact list and petnames
- **NIP-04** - Encrypted direct messages
- **NIP-05** - Mapping Nostr keys to DNS-based internet identifiers
- **NIP-09** - Event deletion
- **NIP-10** - Conventions for clients' use of `e` and `p` tags
- **NIP-19** - bech32-encoded entities
- **NIP-25** - Reactions
**Core Event Types:**
```typescript
interface NostrEvent {
id: string // Event ID (32-bytes hex)
pubkey: string // Author public key (32-bytes hex)
created_at: number // Unix timestamp in seconds
kind: number // Event kind (0=metadata, 1=text, 3=contacts, etc.)
tags: string[][] // Tags array
content: string // Event content
sig: string // Event signature (64-bytes hex)
}
// Common event kinds
enum EventKind {
Metadata = 0, // User profile information
TextNote = 1, // Short text note
RecommendRelay = 2, // Recommend relay
Contacts = 3, // Contact list
EncryptedDM = 4, // Encrypted direct message
EventDeletion = 5, // Event deletion
Repost = 6, // Repost/boost
Reaction = 7, // Like/reaction
BadgeAward = 8, // Badge award
GroupChatMessage = 9, // Group chat message
}
```
**See:** [[nostr-protocol|📖 Nostr Protocol Implementation]]
### **Lightning Network**
**Protocol:** BOLT specifications for Lightning Network
**Browser Integration:** WebLN API for wallet communication
**Backend Integration:** LNbits for invoice generation and payment processing
**WebLN API Integration:**
```typescript
interface WebLN {
enable(): Promise<void>
getInfo(): Promise<GetInfoResponse>
sendPayment(paymentRequest: string): Promise<SendPaymentResponse>
makeInvoice(args: RequestInvoiceArgs): Promise<RequestInvoiceResponse>
signMessage(message: string): Promise<SignMessageResponse>
}
// Payment workflow
async function processLightningPayment(invoice: string): Promise<PaymentResult> {
if (!window.webln) {
throw new Error('WebLN not available')
}
await window.webln.enable()
const result = await window.webln.sendPayment(invoice)
return {
preimage: result.preimage,
paymentHash: result.paymentHash,
paid: true
}
}
```
**Invoice Generation:**
```typescript
interface LightningInvoice {
payment_request: string // BOLT11 invoice string
payment_hash: string // Payment hash (32-bytes hex)
amount: number // Amount in millisats
description: string // Invoice description
expires_at: number // Expiration timestamp
created_at: number // Creation timestamp
}
```
**See:** [[lightning-integration|📖 Lightning Network Integration]]
## External APIs
### **LNbits API**
**Purpose:** Lightning wallet backend and invoice management
**Documentation:** [LNbits API Docs](https://legend.lnbits.com/docs)
**Base URL:** Configurable via environment variables
**Key Endpoints:**
#### **Wallet Management**
```typescript
// Get wallet information
GET /api/v1/wallet
Authorization: X-Api-Key: {admin_key}
interface WalletInfo {
id: string
name: string
balance: number // Balance in millisats
}
```
#### **Invoice Operations**
```typescript
// Create invoice
POST /api/v1/payments
Content-Type: application/json
Authorization: X-Api-Key: {invoice_key}
interface CreateInvoiceRequest {
out: false // Incoming payment
amount: number // Amount in sats
memo: string // Invoice description
expiry?: number // Expiry in seconds
}
interface CreateInvoiceResponse {
payment_hash: string
payment_request: string // BOLT11 invoice
checking_id: string
}
```
#### **Payment Verification**
```typescript
// Check payment status
GET /api/v1/payments/{checking_id}
Authorization: X-Api-Key: {invoice_key}
interface PaymentStatus {
paid: boolean
checking_id: string
amount: number
fee: number
memo: string
time: number
}
```
**See:** [[lnbits-api|📖 LNbits API Integration]]
### **Nostr Relay APIs**
**Protocol:** WebSocket-based communication following Nostr specification
**Connection Management:** Custom RelayHub with connection pooling and failover
**Relay Communication Protocol:**
```typescript
// Client to relay messages
type ClientMessage =
| ['EVENT', NostrEvent] // Publish event
| ['REQ', string, ...Filter[]] // Request events
| ['CLOSE', string] // Close subscription
// Relay to client messages
type RelayMessage =
| ['EVENT', string, NostrEvent] // Event received
| ['OK', string, boolean, string] // Event acceptance
| ['EOSE', string] // End of stored events
| ['NOTICE', string] // Relay notice
```
**Event Filters:**
```typescript
interface Filter {
ids?: string[] // Event IDs
authors?: string[] // Author pubkeys
kinds?: number[] // Event kinds
since?: number // Events after timestamp
until?: number // Events before timestamp
limit?: number // Maximum number of events
search?: string // Text search (if supported)
[key: string]: any // Tag filters (#e, #p, etc.)
}
```
**Relay Information (NIP-11):**
```typescript
// GET https://relay.example.com (Accept: application/nostr+json)
interface RelayInformation {
name: string
description: string
pubkey?: string
contact?: string
supported_nips?: number[]
software?: string
version?: string
limitation?: {
max_message_length?: number
max_subscriptions?: number
max_filters?: number
max_subid_length?: number
min_pow_difficulty?: number
auth_required?: boolean
payment_required?: boolean
}
}
```
**See:** [[relay-communication|📖 Relay Communication Protocol]]
## Service Integrations
### **QR Code Generation**
**Library:** `qrcode` for QR code generation
**Use Cases:** Lightning invoices, contact sharing, event tickets
```typescript
interface QRCodeOptions {
width?: number
margin?: number
color?: {
dark?: string
light?: string
}
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'
}
async function generateQRCode(data: string, options?: QRCodeOptions): Promise<string> {
return QRCode.toDataURL(data, {
width: options?.width || 256,
margin: options?.margin || 2,
color: {
dark: options?.color?.dark || '#000000',
light: options?.color?.light || '#FFFFFF'
},
errorCorrectionLevel: options?.errorCorrectionLevel || 'M'
})
}
```
### **Cryptography Services**
**Library:** `nostr-tools` cryptographic functions
**Use Cases:** Key generation, event signing, message encryption
```typescript
// Key generation
function generateKeyPair(): { privateKey: string; publicKey: string } {
const privateKey = generatePrivateKey()
const publicKey = getPublicKey(privateKey)
return { privateKey, publicKey }
}
// Event signing
function signEvent(event: UnsignedEvent, privateKey: string): NostrEvent {
const id = getEventHash(event)
const sig = getSignature(id, privateKey)
return { ...event, id, sig }
}
// Message encryption (NIP-04)
async function encryptMessage(
message: string,
recipientPubkey: string,
senderPrivkey: string
): Promise<string> {
return encrypt(senderPrivkey, recipientPubkey, message)
}
```
### **Storage Services**
**Browser APIs:** localStorage, IndexedDB
**Abstraction:** StorageService with user-scoped keys
```typescript
interface StorageService {
// User-scoped operations
setUserData<T>(key: string, data: T): void
getUserData<T>(key: string, defaultValue?: T): T | undefined
removeUserData(key: string): void
clearUserData(): void
// Global application data
setAppData<T>(key: string, data: T): void
getAppData<T>(key: string, defaultValue?: T): T | undefined
}
```
## SDK References
### **Nostr-tools Reference**
**Documentation:** [nostr-tools GitHub](https://github.com/nbd-wtf/nostr-tools)
**Version:** 2.x
**Key Functions:**
```typescript
import {
generatePrivateKey,
getPublicKey,
getEventHash,
getSignature,
validateEvent,
verifySignature,
SimplePool,
Filter,
Event as NostrEvent,
nip04,
nip19
} from 'nostr-tools'
// Pool management
const pool = new SimplePool()
const events = await pool.list(relays, [filter])
const sub = pool.sub(relays, [filter])
```
### **Vue 3 Integration**
**Framework:** Vue 3 Composition API
**State Management:** Pinia stores
**Reactivity:** Vue reactive primitives
```typescript
// Reactive Nostr client integration
export function useNostrClient() {
const isConnected = ref(false)
const connectedRelays = ref<string[]>([])
const connect = async (relays: string[]) => {
// Connection logic
isConnected.value = true
connectedRelays.value = relays
}
return {
isConnected: readonly(isConnected),
connectedRelays: readonly(connectedRelays),
connect
}
}
```
## Authentication & Security
### **Key Management**
**Security Model:** Client-side key generation and storage
**Storage:** Encrypted localStorage with user passphrase
**No Server Storage:** Private keys never leave the client
```typescript
interface AuthenticationFlow {
// Key generation
generateNewUser(): Promise<{ privateKey: string; publicKey: string }>
// Import existing key
importUser(privateKey: string): Promise<UserProfile>
// Session management
login(privateKey: string, remember?: boolean): Promise<void>
logout(): Promise<void>
// Key security
encryptPrivateKey(privateKey: string, passphrase: string): string
decryptPrivateKey(encrypted: string, passphrase: string): string
}
```
### **Event Validation**
**Signature Verification:** All received events are cryptographically verified
**Content Filtering:** Optional content moderation and filtering
**Spam Prevention:** Rate limiting and reputation-based filtering
```typescript
function validateNostrEvent(event: NostrEvent): ValidationResult {
// 1. Check event structure
if (!event.id || !event.pubkey || !event.sig) {
return { valid: false, error: 'Missing required fields' }
}
// 2. Verify event ID
const expectedId = getEventHash(event)
if (event.id !== expectedId) {
return { valid: false, error: 'Invalid event ID' }
}
// 3. Verify signature
const signatureValid = verifySignature(event)
if (!signatureValid) {
return { valid: false, error: 'Invalid signature' }
}
return { valid: true }
}
```
## Rate Limits & Best Practices
### **Relay Communication**
- **Connection Limits** - Maximum 10 concurrent relay connections
- **Subscription Limits** - Maximum 20 active subscriptions per relay
- **Event Publishing** - Rate limited to prevent spam (1 event/second per kind)
- **Reconnection Strategy** - Exponential backoff with maximum 30-second intervals
### **Lightning Payments**
- **Invoice Expiry** - Default 1 hour expiry for generated invoices
- **Payment Verification** - Poll payment status every 5 seconds for 5 minutes
- **Amount Limits** - Configurable minimum/maximum payment amounts
- **Error Handling** - Graceful handling of payment failures and timeouts
### **API Usage Guidelines**
#### **LNbits Integration**
```typescript
const API_CONFIG = {
baseUrl: process.env.VITE_LNBITS_URL,
timeout: 30000, // 30 second timeout
retryAttempts: 3, // Retry failed requests
retryDelay: 1000, // 1 second between retries
rateLimits: {
invoiceCreation: 10, // Max 10 invoices per minute
paymentCheck: 60, // Max 60 payment checks per minute
}
}
```
#### **Nostr Relay Usage**
```typescript
const RELAY_CONFIG = {
maxConnections: 10, // Maximum concurrent connections
maxSubscriptions: 20, // Maximum active subscriptions
connectionTimeout: 10000, // 10 second connection timeout
publishLimits: {
textNote: 5, // Max 5 text notes per minute
reaction: 30, // Max 30 reactions per minute
directMessage: 10, // Max 10 DMs per minute
},
reconnection: {
attempts: 5, // Maximum reconnection attempts
delay: 1000, // Initial delay (1 second)
backoffMultiplier: 2, // Exponential backoff
maxDelay: 30000, // Maximum delay (30 seconds)
}
}
```
## See Also
### Protocol Documentation
- **[[nostr-protocol|📡 Nostr Protocol Implementation]]** - Detailed NIP implementation
- **[[lightning-integration|⚡ Lightning Network Integration]]** - WebLN and payment processing
- **[[lnbits-api|🔗 LNbits API Reference]]** - Lightning backend integration
- **[[events-api|📅 Events System API]]** - Event ticketing API reference
### Implementation Guides
- **[[../03-core-services/index|⚙️ Core Services]]** - Service implementations using these APIs
- **[[../02-modules/index|📦 Module System]]** - How modules integrate with external APIs
- **[[../04-development/index|💻 Development Guide]]** - Testing and debugging API integrations
---
**Tags:** #api #integration #nostr #lightning #protocols
**Last Updated:** 2025-09-06
**Author:** Development Team

View file

@ -0,0 +1,268 @@
# Protocols
## Finance
Layer 1: Bitcoin on-chain
Layer 2: Bitcoin Lightning
Layer 2+: LNBits
## Communication
NOSTR
### Resources
https://nostr.how
https://github.com/nostr-protocol/nips
## Notes on integration
NOSTR and Bitcoin lightning are closely integrated. Each NOSTR profile will have a lightning "zap" address that is likely to be `<username>@<domain>.<ext>`. We want users to be easily able to "zap" each other
### resources
[What are Zaps](https://nostr.how/en/zaps)
# Objective
The objective of this project is to make an app for a social network that serve people in a close physical community. This core tenets of this app are that it be made with FOSS so that it may easily be reproduced.
This app will be able to function on a LAN that is not connected to WAN.
We want this app to foster a strong social network that allows for easy exchange of value. On the server that hosts the application will be a NOSTR Relay which will be the database that stores any content.
## Components
- LNBits Server
- LNBits User Web app
- Front end app designed for enhanced UX/UI
The vast majority of the backend will be handled by the LNBits stack. LNBits includes both a server that facilitates the exchange of value compatible with the lightning network. Technically, this would be described as layer 2+ in the context of Bitcoin. While it is fully compatible with the lightning network, it can also have internal payments that occur mostly at the database level.
LNBits has a collection of extensions that we will leverage in order to super-charge the user experience. When we make a user for a person, we will be creating an lnbits user. A user has access to their lnbits settings through the lnbits front end that is run in tandem with the lnbits server.
We will also create a UI that provides a streamlined experience for less technical users who do not need the level of control that the lnbits quasar web app provides. For this, we will be using Vue3, shadcn-vue (implying tailwind v4), vite, etc. This UI will focus on displaying the content in a visually pleasing way that also provides the ability to interact through their nostr profile. E.g., they can send messages to friends, purchase items from the nostr marketplace, exchange money with friends, etc
### LNBits Server + Extensions
- NOSTR Relay
- NOSTR Client
- NOSTR Marketplace
# Features
- Newsfeed page with filters. E.g., Important Announcements, GenPop, Event notifications, etc
- Communal calendar page
- Communal Marketplace
- Events Page (already present)
# User Experience Details
## Authentication & User Management
- User registration creates both LNBits user and NOSTR key-pair via LNBits API
- Keys stored securely, minimal technical exposure to users
- Simplified onboarding for non-technical users
## User Profiles
- **Displayed Info**: Avatar, username, display name, bio
- **Zap Address**: `<username>@<hostname>` format
- **Wallet Integration**: Show LNBits wallet balance
- **Future Features**: Badges and gamification elements
## Content & Interactions
- **Primary Content**: Moderator posts and automated system posts (e.g., event creation notifications)
- **User Actions**: Reply and react to existing content (limited user-generated content initially)
- **Real-time Updates**: WebSocket-based NOSTR client for live feed updates
- **Data Persistence**: Local storage for offline capability and improved UX
## Technical Architecture
- **PWA Deployment**: Mobile-optimized progressive web app
- **Network**: Functions on LAN without WAN dependency
- **API Integration**: All transactions through LNBits API
- **User Roles**: Admin/moderator/user permissions handled via LNBits
## Development Phases
**Phase 1 (Current Focus)**: Newsfeed and Events
**Phase 2 (Future)**: Marketplace integration
**Phase 3 (Future)**: Location-aware features and maps
# Technical Integration Specifications
## Environment Variables
```
VITE_LNBITS_BASE_URL=http://localhost:5000 # LNBits server URL
VITE_NOSTR_RELAY_URL=ws://localhost:5000/nostrrelay/<relay-id> # NOSTR relay WebSocket
VITE_HOSTNAME=localhost # For zap addresses (username@hostname)
```
## LNBits API Integration Points
### User Management
- **Create User**: `POST /api/v1/user` - Creates LNBits user with NOSTR key-pair
- **Get User**: `GET /api/v1/user/{user_id}` - Retrieve user profile data
- **Update User**: `PUT /api/v1/user/{user_id}` - Update profile (avatar, username, display name, bio)
### Wallet Operations
- **Get Wallet Balance**: `GET /api/v1/wallet` - Retrieve current wallet balance
- **Create Payment**: `POST /api/v1/payments` - Handle zap transactions
- **Payment Status**: `GET /api/v1/payments/{payment_hash}` - Check payment status
### NOSTR Integration
- **NOSTR Client Extension**: `/extensions/nostrclient` - Manages NOSTR connections and messaging
- **NOSTR Relay Extension**: `/extensions/nostrrelay` - Local relay for community content
- **WebSocket Connection**: Real-time event streaming via relay WebSocket
## UI/UX Component Architecture
### Main Navigation
- **Newsfeed** (tabs: All, Important, Events, General)
- **Events** (existing page, integrate with newsfeed notifications)
- **Profile** (modal: user info, wallet balance, settings)
### Newsfeed Features
- Real-time WebSocket updates from NOSTR relay
- Visual priority system for Important Announcements
- Reply and reaction capabilities (zap, like, comment)
- Local storage for offline content persistence
### Zapping Interface
- Quick zap buttons (preset amounts: 10, 50, 100 sats)
- Custom amount modal for larger transactions
- Visual feedback for successful zaps
- Integration with LNBits payment API
### Profile Management
- Editable: avatar, username, display name, bio
- Read-only: zap address (username@hostname), wallet balance
- Future: badges and gamification elements
## Data Flow
1. **User Registration**: Vue app → LNBits API → NOSTR key-pair generation
2. **Content Publishing**: Moderator/System → NOSTR relay → WebSocket → Vue app
3. **User Interactions**: Vue app → LNBits API (payments) + NOSTR relay (social)
4. **Real-time Updates**: NOSTR relay WebSocket → Vue app state management
# NOSTR Event Types for Implementation
Based on NOSTR Improvement Proposals (NIPs), the following event types are relevant for our community app:
## Core Event Types
### User Profile (Kind 0 - NIP-01)
```json
{
"kind": 0,
"content": "{\"name\":\"username\", \"about\":\"bio text\", \"picture\":\"avatar_url\"}"
}
```
- **Usage**: User profile management
- **UI Components**: Profile modal, user avatar/name display
### Text Notes (Kind 1 - NIP-10)
```json
{
"kind": 1,
"content": "Post content text",
"tags": [
["p", "pubkey", "relay_url"], // mentions
["e", "event_id", "relay_url", "marker"] // replies/threads
]
}
```
- **Usage**: Main newsfeed content, moderator announcements
- **UI Components**: Post display, reply threads
### Reactions (Kind 7 - NIP-25)
```json
{
"kind": 7,
"content": "+", // or "-", or emoji
"tags": [
["e", "target_event_id", "relay_url", "target_pubkey"],
["p", "target_pubkey", "relay_url"],
["k", "target_event_kind"]
]
}
```
- **Usage**: Like/dislike posts, emoji reactions
- **UI Components**: Reaction buttons, reaction counts
### Calendar Events (Kind 31922/31923 - NIP-52)
```json
{
"kind": 31922, // date-based events
"content": "Event description",
"tags": [
["d", "unique_identifier"],
["title", "Event Title"],
["start", "YYYY-MM-DD"],
["end", "YYYY-MM-DD"],
["location", "Event location"],
["p", "participant_pubkey", "relay_url", "role"]
]
}
```
- **Usage**: Community calendar, event announcements
- **UI Components**: Event cards, calendar integration, automatic newsfeed posts
## Lightning/Payment Events
### Zap Requests (Kind 9734 - NIP-57)
```json
{
"kind": 9734,
"content": "Zap message",
"tags": [
["relays", "relay1", "relay2"],
["amount", "21000"], // millisats
["p", "recipient_pubkey"],
["e", "target_event_id"] // if zapping a post
]
}
```
- **Usage**: Lightning zap payments
- **UI Components**: Zap buttons, amount selection, payment confirmation
### Zap Receipts (Kind 9735 - NIP-57)
```json
{
"kind": 9735,
"content": "",
"tags": [
["bolt11", "lightning_invoice"],
["description", "zap_request_json"],
["p", "recipient_pubkey"],
["e", "target_event_id"]
]
}
```
- **Usage**: Payment confirmation display
- **UI Components**: Zap success indicators, payment history
## Content Types for Future Implementation
### Long-form Content (Kind 30023 - NIP-23)
- **Usage**: Community announcements, detailed posts
- **Tags**: `title`, `summary`, `image`, `published_at`
### Marketplace Events (NIP-15)
- **Usage**: Community marketplace integration (Phase 2)
## Implementation Notes
### Event Filtering for Newsfeed Tabs
- **All**: Subscribe to kinds [0, 1, 7, 31922, 31923, 9735]
- **Important**: Filter by specific pubkeys (moderators) or tags
- **Events**: Filter specifically for kinds [31922, 31923]
- **General**: Filter for kind 1 excluding moderator pubkeys
### WebSocket Subscription Filters
```json
{
"kinds": [1, 7, 31922, 31923, 9735],
"since": <timestamp>,
"limit": 50
}
```
### Local Storage Schema
- Events by kind and timestamp
- User profiles by pubkey
- Reaction counts by event_id
- Zap totals by event_id and recipient

663
docs/06-deployment/index.md Normal file
View file

@ -0,0 +1,663 @@
# 🚀 Deployment Guide
> **Production deployment and operations** for Ario's web application, desktop app, and Progressive Web App across multiple platforms and environments.
## Table of Contents
- [[#Deployment Options]]
- [[#Production Build Process]]
- [[#Environment Configuration]]
- [[#Platform-Specific Deployment]]
- [[#Monitoring & Maintenance]]
- [[#Security Considerations]]
## Deployment Options
### **Web Application (SPA)**
- **Static Hosting** - Deploy to CDN or static hosting service
- **Traditional Web Server** - Nginx, Apache, or similar
- **Cloud Platforms** - Vercel, Netlify, Cloudflare Pages
- **Container Deployment** - Docker with web server
### **Progressive Web App (PWA)**
- **Service Worker** - Offline capabilities and caching
- **App Manifest** - Installation and app-like experience
- **Push Notifications** - Real-time updates (future feature)
- **Background Sync** - Offline data synchronization
### **Desktop Application**
- **Electron** - Cross-platform desktop packaging
- **Platform-Specific** - Windows MSI, macOS DMG, Linux AppImage/Snap
- **Auto-Update** - Seamless application updates
- **Code Signing** - Security and trust verification
### **Self-Hosted**
- **Full Control** - Complete control over infrastructure
- **Privacy** - No third-party hosting dependencies
- **Custom Configuration** - Tailored environment variables and settings
- **Local Development** - Internal network deployment
## Production Build Process
### **Build Commands**
```bash
# Standard web build
npm run build
# Build with analysis
npm run analyze
# Electron desktop build
npm run electron:build
# Platform-specific builds
npm run build:win # Windows
npm run build:mac # macOS
npm run build:linux # Linux
```
### **Build Configuration**
#### **Vite Production Config**
```typescript
// vite.config.ts - Production optimizations
export default defineConfig({
build: {
target: 'es2020',
minify: 'terser',
sourcemap: false, // Disable in production
chunkSizeWarningLimit: 600,
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'ui-vendor': ['@headlessui/vue', '@heroicons/vue'],
'nostr-vendor': ['nostr-tools'],
'crypto-vendor': ['crypto-js']
}
}
}
}
})
```
#### **Build Optimization**
- **Tree Shaking** - Remove unused code
- **Code Splitting** - Lazy load modules and routes
- **Asset Optimization** - Compress images and fonts
- **Bundle Analysis** - Monitor bundle size and dependencies
### **Build Output Structure**
```
dist/
├── index.html # Entry point
├── assets/ # Static assets with hashed names
│ ├── index-[hash].js # Main application bundle
│ ├── vendor-[hash].js # Vendor dependencies
│ └── [module]-[hash].js # Module-specific bundles
├── icons/ # PWA icons
├── manifest.json # PWA manifest
└── sw.js # Service worker
```
## Environment Configuration
### **Environment Variables**
#### **Production Environment (.env.production)**
```bash
# Application Configuration
VITE_APP_NAME="Ario"
VITE_APP_VERSION="1.0.0"
VITE_BASE_URL="/"
# Nostr Configuration
VITE_NOSTR_RELAYS='["wss://relay.damus.io","wss://nos.lol","wss://relay.snort.social"]'
VITE_ADMIN_PUBKEYS='["admin_pubkey_1","admin_pubkey_2"]'
# Lightning Configuration (if using LNbits)
VITE_LNBITS_URL="https://your-lnbits-instance.com"
VITE_LNBITS_ADMIN_KEY="" # Only for invoice creation
# Security & Performance
VITE_DEBUG=false
VITE_ENABLE_PWA=true
VITE_ENABLE_ANALYTICS=true
# Optional Features
VITE_ENABLE_NOTIFICATIONS=true
VITE_MAX_RELAY_CONNECTIONS=10
VITE_EVENT_CACHE_SIZE=1000
```
#### **Configuration Validation**
```typescript
// src/config/production.ts
export const productionConfig = {
app: {
name: import.meta.env.VITE_APP_NAME || 'Ario',
version: import.meta.env.VITE_APP_VERSION || '1.0.0',
baseUrl: import.meta.env.VITE_BASE_URL || '/',
},
nostr: {
relays: JSON.parse(import.meta.env.VITE_NOSTR_RELAYS || '[]'),
adminPubkeys: JSON.parse(import.meta.env.VITE_ADMIN_PUBKEYS || '[]'),
maxConnections: Number(import.meta.env.VITE_MAX_RELAY_CONNECTIONS) || 10,
},
features: {
debug: import.meta.env.VITE_DEBUG === 'true',
pwa: import.meta.env.VITE_ENABLE_PWA === 'true',
notifications: import.meta.env.VITE_ENABLE_NOTIFICATIONS === 'true',
}
}
// Validate required configuration
if (!productionConfig.nostr.relays.length) {
throw new Error('VITE_NOSTR_RELAYS must be configured')
}
```
### **Security Configuration**
#### **Content Security Policy (CSP)**
```html
<!-- In index.html for production -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' wss: ws: https:;
img-src 'self' data: https:;
font-src 'self' data:;
manifest-src 'self';
">
```
#### **Security Headers (nginx example)**
```nginx
# nginx.conf security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";
```
## Platform-Specific Deployment
### **Web Hosting - Vercel**
#### **vercel.json Configuration**
```json
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
{
"source": "/((?!api/).*)",
"destination": "/index.html"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-Content-Type-Options",
"value": "nosniff"
}
]
}
]
}
```
#### **Deployment Steps**
```bash
# 1. Install Vercel CLI
npm i -g vercel
# 2. Deploy to Vercel
vercel --prod
# 3. Set environment variables in Vercel dashboard
# - VITE_NOSTR_RELAYS
# - VITE_ADMIN_PUBKEYS
# - Other configuration variables
```
### **Web Hosting - Netlify**
#### **netlify.toml Configuration**
```toml
[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[build.environment]
NODE_VERSION = "18"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
```
### **Self-Hosted - Docker**
#### **Dockerfile**
```dockerfile
# Multi-stage build for production
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
# Copy built application
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration
COPY docker/nginx.conf /etc/nginx/nginx.conf
# Expose port
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
#### **Docker Compose**
```yaml
# docker-compose.yml
version: '3.8'
services:
ario-web:
build: .
ports:
- "80:80"
environment:
- VITE_NOSTR_RELAYS=["wss://relay.damus.io"]
- VITE_ADMIN_PUBKEYS=["your_admin_pubkey"]
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
restart: unless-stopped
```
### **Desktop Application**
#### **Electron Forge Configuration**
```javascript
// forge.config.js
module.exports = {
packagerConfig: {
name: 'Ario',
executableName: 'ario',
icon: './src/assets/icon',
asar: true,
// Code signing (production)
osxSign: {
identity: process.env.APPLE_IDENTITY,
'hardened-runtime': true,
entitlements: './entitlements.plist',
'entitlements-inherit': './entitlements.plist'
},
osxNotarize: {
tool: 'notarytool',
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_PASSWORD,
teamId: process.env.APPLE_TEAM_ID
}
},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
name: 'ario',
setupIcon: './src/assets/icon.ico'
}
},
{
name: '@electron-forge/maker-dmg',
config: {
format: 'ULFO',
icon: './src/assets/icon.icns'
}
},
{
name: '@electron-forge/maker-deb',
config: {
options: {
maintainer: 'Ario Team',
homepage: 'https://ario.app'
}
}
}
],
publishers: [
{
name: '@electron-forge/publisher-github',
config: {
repository: {
owner: 'your-org',
name: 'ario'
},
prerelease: false
}
}
]
}
```
#### **Auto-Update Configuration**
```typescript
// electron/main.ts - Auto-updater setup
import { autoUpdater } from 'electron-updater'
if (!app.isPackaged) {
autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml')
}
autoUpdater.checkForUpdatesAndNotify()
autoUpdater.on('update-available', () => {
dialog.showMessageBox(mainWindow, {
type: 'info',
title: 'Update Available',
message: 'A new version is available. It will be downloaded in the background.',
buttons: ['OK']
})
})
```
## Monitoring & Maintenance
### **Performance Monitoring**
#### **Web Vitals Tracking**
```typescript
// src/utils/analytics.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'
export function initPerformanceMonitoring() {
if (import.meta.env.VITE_ENABLE_ANALYTICS) {
getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getFCP(sendToAnalytics)
getLCP(sendToAnalytics)
getTTFB(sendToAnalytics)
}
}
function sendToAnalytics(metric: any) {
// Send to your analytics service
console.log('Performance metric:', metric)
}
```
#### **Application Health Monitoring**
```typescript
// src/services/HealthMonitor.ts
export class HealthMonitor extends BaseService {
private healthStatus = ref({
relayConnections: 0,
lastEventReceived: null as Date | null,
memoryUsage: 0,
errorCount: 0
})
async initialize(): Promise<void> {
setInterval(() => this.checkHealth(), 30000) // Every 30 seconds
}
private checkHealth(): void {
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
this.healthStatus.value = {
relayConnections: relayHub.connectedRelays.value.length,
lastEventReceived: this.getLastEventTime(),
memoryUsage: this.getMemoryUsage(),
errorCount: this.getErrorCount()
}
// Alert if critical issues detected
if (this.healthStatus.value.relayConnections === 0) {
console.warn('No relay connections available')
}
}
}
```
### **Error Tracking**
#### **Global Error Handler**
```typescript
// src/utils/errorHandler.ts
export function setupGlobalErrorHandler() {
window.addEventListener('error', (event) => {
console.error('Global error:', event.error)
// Send to error tracking service
if (import.meta.env.VITE_ERROR_TRACKING) {
sendErrorToService(event.error)
}
})
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason)
sendErrorToService(event.reason)
})
}
```
### **Logging Strategy**
#### **Production Logging**
```typescript
// src/utils/logger.ts
class Logger {
private shouldLog(level: LogLevel): boolean {
if (import.meta.env.PROD && level === 'debug') return false
return true
}
info(message: string, ...args: unknown[]): void {
if (this.shouldLog('info')) {
console.log(`[INFO] ${message}`, ...args)
}
}
error(message: string, error?: Error, ...args: unknown[]): void {
if (this.shouldLog('error')) {
console.error(`[ERROR] ${message}`, error, ...args)
// Send to error tracking in production
if (import.meta.env.PROD && error) {
this.sendToErrorTracking(message, error)
}
}
}
private sendToErrorTracking(message: string, error: Error): void {
// Implementation for error tracking service
}
}
export const logger = new Logger()
```
## Security Considerations
### **Client-Side Security**
#### **Key Storage Security**
```typescript
// src/utils/keyStorage.ts
export class SecureKeyStorage {
private static readonly STORAGE_KEY = 'ario_encrypted_keys'
static async storeEncryptedKey(privateKey: string, passphrase: string): Promise<void> {
const encrypted = await this.encrypt(privateKey, passphrase)
localStorage.setItem(this.STORAGE_KEY, encrypted)
}
static async retrieveDecryptedKey(passphrase: string): Promise<string | null> {
const encrypted = localStorage.getItem(this.STORAGE_KEY)
if (!encrypted) return null
try {
return await this.decrypt(encrypted, passphrase)
} catch {
return null // Invalid passphrase
}
}
private static async encrypt(data: string, passphrase: string): Promise<string> {
// Use Web Crypto API for encryption
const encoder = new TextEncoder()
const key = await window.crypto.subtle.importKey(
'raw',
encoder.encode(passphrase),
{ name: 'PBKDF2' },
false,
['deriveKey']
)
// Implementation details...
return encryptedData
}
}
```
#### **Input Validation**
```typescript
// src/utils/validation.ts
export const validators = {
nostrPublicKey: (pubkey: string): boolean => {
return /^[0-9a-f]{64}$/i.test(pubkey)
},
nostrPrivateKey: (privkey: string): boolean => {
return /^[0-9a-f]{64}$/i.test(privkey)
},
lightningInvoice: (invoice: string): boolean => {
return /^(lnbc|lntb|lnbcrt)[0-9]+[munp]?[0-9a-z]+$/i.test(invoice)
},
sanitizeContent: (content: string): string => {
// Sanitize user-generated content
return content
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/javascript:/gi, '')
.trim()
}
}
```
### **Deployment Security**
#### **HTTPS Enforcement**
```nginx
# nginx.conf - Force HTTPS
server {
listen 80;
server_name ario.app www.ario.app;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name ario.app www.ario.app;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/private.key;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
}
```
### **Privacy Protection**
#### **Data Minimization**
- **No Server Storage** - All user data stored client-side
- **Ephemeral Sessions** - No persistent server-side sessions
- **Optional Analytics** - Analytics can be disabled by users
- **Relay Privacy** - Users can configure their own relays
#### **User Privacy Controls**
```typescript
// src/services/PrivacyService.ts
export class PrivacyService extends BaseService {
private settings = ref({
shareAnalytics: false,
shareErrorReports: false,
allowLocationTracking: false,
publicProfile: false
})
updatePrivacySettings(updates: Partial<PrivacySettings>): void {
this.settings.value = { ...this.settings.value, ...updates }
// Apply privacy settings immediately
if (!this.settings.value.shareAnalytics) {
this.disableAnalytics()
}
if (!this.settings.value.shareErrorReports) {
this.disableErrorReporting()
}
}
}
```
## See Also
### Configuration Documentation
- **[[configuration|⚙️ Environment Configuration]]** - Detailed configuration options
- **[[pwa-setup|📱 PWA Configuration]]** - Progressive Web App setup
- **[[electron|🖥️ Desktop App Packaging]]** - Electron configuration and distribution
### Operations Documentation
- **[[../04-development/index|💻 Development Guide]]** - Development environment setup
- **[[../05-api-reference/index|📡 API Reference]]** - External service integrations
- **[[../01-architecture/index|🏗️ Architecture Overview]]** - System architecture principles
---
**Tags:** #deployment #production #security #monitoring #pwa #electron
**Last Updated:** 2025-09-06
**Author:** Development Team

175
docs/README.md Normal file
View file

@ -0,0 +1,175 @@
# 📚 Ario Web App Documentation
> **A comprehensive guide to the Ario Web Application** - A modular Vue 3 + TypeScript application with Nostr protocol integration and Lightning Network payments.
## 🗂️ Documentation Structure
This documentation follows an Obsidian-compatible structure with cross-linked markdown files organized by domain.
### Quick Navigation
- **[[00-overview/index|📖 Overview]]** - Start here for project introduction
- **[[01-architecture/index|🏗️ Architecture]]** - System design and patterns
- **[[02-modules/index|📦 Modules]]** - Feature module documentation
- **[[03-core-services/index|⚙️ Core Services]]** - Shared infrastructure
- **[[04-development/index|💻 Development]]** - Setup and guidelines
- **[[05-api-reference/index|📡 API Reference]]** - External integrations
- **[[06-deployment/index|🚀 Deployment]]** - Production setup
## 📂 Directory Structure
```
docs/
├── README.md # This file - Documentation hub
├── 00-overview/ # Project overview and introduction
│ ├── index.md # Overview index
│ ├── project-goals.md # Project objectives
│ ├── tech-stack.md # Technology choices
│ └── getting-started.md # Quick start guide
├── 01-architecture/ # System architecture
│ ├── index.md # Architecture overview
│ ├── modular-design.md # Modular architecture patterns
│ ├── dependency-injection.md # DI container system
│ ├── event-bus.md # Inter-module communication
│ └── relay-hub.md # Nostr relay management
├── 02-modules/ # Feature modules
│ ├── index.md # Module system overview
│ ├── base-module/ # Core infrastructure module
│ ├── market-module/ # Marketplace functionality
│ ├── chat-module/ # Encrypted chat system
│ ├── events-module/ # Event ticketing
│ └── nostr-feed-module/ # Social feed
├── 03-core-services/ # Shared services
│ ├── index.md # Services overview
│ ├── authentication.md # Auth service & LNbits
│ ├── payment-service.md # Lightning payments
│ ├── storage-service.md # User-scoped storage
│ ├── toast-service.md # Notifications
│ └── visibility-service.md # Component visibility
├── 04-development/ # Development guides
│ ├── index.md # Development overview
│ ├── setup.md # Environment setup
│ ├── coding-standards.md # Code conventions
│ ├── testing.md # Testing strategies
│ └── debugging.md # Debug techniques
├── 05-api-reference/ # External APIs
│ ├── index.md # API overview
│ ├── nostr-protocol.md # Nostr implementation
│ ├── lnbits-api.md # LNbits integration
│ └── events-api.md # Events system API
├── 06-deployment/ # Deployment & operations
│ ├── index.md # Deployment overview
│ ├── configuration.md # Environment variables
│ ├── pwa-setup.md # PWA configuration
│ └── electron.md # Desktop app packaging
└── archive/ # Deprecated documentation
├── legacy-architecture.md
└── old-relay-system.md
```
## 🔗 Key Documentation Files
### Essential Reading
1. **[[00-overview/getting-started|Getting Started]]** - Set up your development environment
2. **[[01-architecture/modular-design|Modular Architecture]]** - Understand the plugin system
3. **[[02-modules/index|Module Development]]** - Create new feature modules
4. **[[04-development/coding-standards|Coding Standards]]** - Maintain code quality
### Module Documentation
- **[[02-modules/base-module/index|Base Module]]** - Core infrastructure (Nostr, Auth, PWA)
- **[[02-modules/market-module/index|Market Module]]** - Nostr marketplace implementation
- **[[02-modules/chat-module/index|Chat Module]]** - Encrypted DM system
- **[[02-modules/events-module/index|Events Module]]** - Lightning ticketing system
- **[[02-modules/nostr-feed-module/index|Nostr Feed]]** - Social feed functionality
### Technical Deep Dives
- **[[01-architecture/relay-hub|Relay Hub Architecture]]** - Centralized Nostr relay management
- **[[01-architecture/dependency-injection|Dependency Injection]]** - Service container patterns
- **[[03-core-services/visibility-service|Visibility Service]]** - Dynamic UI control
## 🏷️ Documentation Tags
Documents are tagged for easy navigation:
- `#architecture` - System design documents
- `#module` - Feature module documentation
- `#service` - Service layer documentation
- `#api` - External API references
- `#guide` - How-to guides
- `#reference` - Technical references
- `#deprecated` - Outdated documentation
## 📝 Documentation Standards
### File Naming
- Use kebab-case for all files: `module-name.md`
- Index files for directories: `index.md`
- Prefix with numbers for ordering: `01-architecture/`
### Markdown Structure
```markdown
# Document Title
> Brief description or important note
## Overview
Introduction to the topic
## Table of Contents
- [[#Section 1]]
- [[#Section 2]]
## Section 1
Content...
## See Also
- [[related-document]]
- [[another-reference]]
---
**Tags:** #architecture #module
**Last Updated:** 2025-09-06
**Author:** Development Team
```
### Cross-Linking
- Use `[[filename]]` for internal links (Obsidian-style)
- Use `[[filename#section]]` for section links
- Use `[[filename|Display Text]]` for custom link text
## 🔄 Maintenance
### Keeping Documentation Current
1. Update documentation with code changes
2. Review quarterly for accuracy
3. Move deprecated docs to `archive/`
4. Tag documents with last update date
### Contributing
1. Follow the structure and standards above
2. Add appropriate tags to new documents
3. Update index files when adding new docs
4. Cross-link related documentation
## 📊 Documentation Coverage
| Module | Status | Coverage |
|--------|--------|----------|
| Base Module | ✅ Complete | 100% |
| Market Module | ✅ Complete | 100% |
| Chat Module | ✅ Complete | 100% |
| Events Module | ✅ Complete | 100% |
| Nostr Feed | ✅ Complete | 100% |
| Core Services | ✅ Complete | 100% |
| Development Guides | 🔄 In Progress | 80% |
| API Reference | 🔄 In Progress | 70% |
---
**Welcome to the Ario Web App documentation!** Start with the [[00-overview/index|Overview]] to begin your journey.

View file

@ -0,0 +1,380 @@
# Web App Architecture Analysis & Modularity Assessment
> **⚠️ OUTDATED DOCUMENT**
> **Updated Version**: `/docs/modular-architecture-analysis.md`
> **Status**: Print version of outdated September 4, 2025 analysis
**Date:** September 4, 2025
**Project:** Ario Web App (Vue 3 + Nostr + LNbits)
**Objective:** Evaluate current architecture for modular plugin-based development
---
## Executive Summary
The current web application demonstrates solid foundational architecture with excellent Nostr infrastructure (RelayHub/NostrclientHub) but lacks true modularity for plugin-based development.
**Overall Modularity Rating: 6.5/10**
The app successfully implements core Nostr functionality and component organization but requires significant architectural refactoring to achieve the stated objective of independent, pluggable feature modules.
---
## Current Architecture Assessment
### **Strengths (What's Working Well)**
#### 1. Strong Base Infrastructure (8/10)
- **RelayHub & NostrclientHub**: Excellent centralized Nostr infrastructure with robust connection management, event handling, and browser compatibility
- **Pinia Stores**: Clean state management separation (`nostr.ts`, `market.ts`) with reactive patterns
- **Composables Pattern**: Good reactive logic encapsulation (`useRelayHub`, `useNostrChat`, `useMarket`)
- **UI Component Library**: Well-structured Shadcn/ui components with consistent design system
#### 2. Component Organization (7/10)
- Components grouped by domain (`/market`, `/nostr`, `/events`, `/auth`)
- Page-level routing with lazy loading implementation
- Composables provide reusable business logic across components
- Clean separation of concerns between UI and business logic
#### 3. Service Layer (6/10)
- Clean service abstractions (`nostrmarketService`, `paymentMonitor`)
- API layer separation (`/lib/api`)
- Type-safe interfaces and proper error handling
### **Critical Issues (What's Missing)**
#### 1. No Plugin Architecture (3/10)
- **Hard-coded routes**: All features baked into main router configuration
- **Tight coupling**: Components directly import across modules without abstraction
- **No feature isolation**: Cannot develop/deploy components independently
- **No plugin registration system**: Missing infrastructure for dynamic feature loading
#### 2. Cross-Module Dependencies (4/10)
**Examples of problematic tight coupling:**
```typescript
// Payment monitor importing market types directly
src/lib/services/paymentMonitor.ts: import type { Order } from '@/stores/market'
// Navbar importing chat composable directly
src/components/layout/Navbar.vue: import { nostrChat } from '@/composables/useNostrChat'
// Services importing market store directly
src/lib/services/nostrmarketService.ts: import type { Stall, Product, Order } from '@/stores/market'
```
#### 3. Missing Module Boundaries (5/10)
- No clear module interfaces/contracts
- Shared types scattered across modules without centralized definition
- No dependency injection system for service management
- Missing module lifecycle management
---
## Required Changes for Modular Architecture
### Phase 1: Core Infrastructure Refactor
#### 1. Create Module System
```typescript
// /src/modules/base/types.ts
interface ModuleConfig {
name: string
routes: RouteConfig[]
components: ComponentConfig[]
services: ServiceConfig[]
dependencies?: string[]
}
interface ModulePlugin {
install(app: App, options?: any): void
uninstall(): void
}
```
#### 2. Plugin Registration System
```typescript
// /src/core/plugin-manager.ts
class PluginManager {
private plugins = new Map<string, ModulePlugin>()
register(module: ModulePlugin): void
unregister(name: string): void
getModule(name: string): ModulePlugin
getDependencies(name: string): string[]
}
```
### Phase 2: Module Extraction
#### 1. Base Module Structure
```
/src/modules/base/
nostr/
relayHub.ts # Centralized relay management
nostrclientHub.ts # Client connection handling
types.ts # Nostr-specific types
auth/
lnbits.ts # LNbits authentication
composables/
pwa/
install-prompt.ts
notifications.ts
ui/ # Shadcn components
```
#### 2. Feature Modules
```
/src/modules/
nostr-feed/
components/
NostrFeed.vue
composables/
useFeed.ts
services/
feedService.ts
index.ts # Module export
market/
components/
composables/
stores/
services/
index.ts
chat/
events/
```
#### 3. Module Interface Standards
```typescript
// Each module exports standardized interface
export interface NostrFeedModule extends ModulePlugin {
name: 'nostr-feed'
components: {
NostrFeed: Component
}
routes: RouteConfig[]
dependencies: ['base']
config?: NostrFeedConfig
}
```
### Phase 3: Event-Driven Communication
#### Replace Direct Imports with Event Bus
```typescript
// /src/core/event-bus.ts
interface ModuleEventBus {
emit(event: string, data: any): void
on(event: string, handler: Function): void
off(event: string, handler: Function): void
}
// Example usage:
// Market module: eventBus.emit('order:created', order)
// Chat module: eventBus.on('order:created', handleNewOrder)
```
#### Dependency Injection Container
```typescript
// /src/core/di-container.ts
class DIContainer {
provide<T>(token: string, instance: T): void
inject<T>(token: string): T
// Module-scoped injection
provideForModule(module: string, token: string, instance: any): void
}
```
---
## Recommended Ground-Up Architecture
If rebuilding from scratch, implement these architectural patterns:
### 1. Module-First Architecture
```
src/
core/ # Base app infrastructure
plugin-manager.ts # Plugin registration & lifecycle
di-container.ts # Dependency injection
event-bus.ts # Inter-module communication
module-registry.ts # Module discovery & loading
base-services/ # Core services
modules/
base/ # Core functionality (required)
nostr/ # RelayHub, NostrclientHub
auth/ # LNbits authentication
pwa/ # Progressive Web App features
ui/ # Shadcn component library
nostr-feed/ # Announcements & social feed
market/ # Marketplace functionality
chat/ # Nostr chat implementation
events/ # Event ticketing system
app.ts # Application composition & startup
```
### 2. Configuration-Driven Setup
```typescript
// /src/app.config.ts
export const appConfig = {
modules: {
base: {
required: true,
nostr: {
relays: process.env.VITE_NOSTR_RELAYS
}
},
'nostr-feed': {
enabled: true,
config: {
refreshInterval: 30000,
maxPosts: 100
}
},
market: {
enabled: true,
config: {
defaultCurrency: 'sats',
paymentTimeout: 300000
}
},
chat: {
enabled: false // Can be toggled via config
},
events: {
enabled: true,
config: {
ticketValidationEndpoint: '/api/tickets/validate'
}
}
},
features: {
pwa: true,
pushNotifications: true,
electronApp: true
}
}
```
### 3. Dynamic Module Loading
```typescript
// /src/core/module-loader.ts
class ModuleLoader {
async loadModule(name: string): Promise<ModulePlugin> {
const module = await import(`@/modules/${name}/index.ts`)
await this.resolveDependencies(module.dependencies || [])
return module.default
}
async unloadModule(name: string): Promise<void> {
const module = this.getModule(name)
if (module) {
await module.uninstall()
this.cleanupRoutes(name)
this.cleanupServices(name)
}
}
private async resolveDependencies(deps: string[]): Promise<void> {
for (const dep of deps) {
if (!this.isModuleLoaded(dep)) {
await this.loadModule(dep)
}
}
}
}
```
### 4. Module Development Workflow
```bash
# Develop single module in isolation
npm run dev:module market
# Test module independently
npm run test:module market
# Build specific modules only
npm run build:modules market,chat
# Hot reload module during development
npm run dev:hot-module nostr-feed
```
---
## Key Architectural Improvements
### 1. Dependency Inversion
- Modules depend on interfaces, not concrete implementations
- Services injected via DI container rather than direct imports
- Clear separation between module contracts and implementations
### 2. Event-Driven Communication
- Replace direct imports with event-based messaging
- Loose coupling between modules via standardized events
- Centralized event routing and handling
### 3. Module Lifecycle Management
- Proper setup and teardown hooks for each module
- Resource cleanup when modules are disabled/unloaded
- Dependency resolution during module loading
### 4. Configuration Over Code
- Enable/disable features via configuration files
- Runtime module toggling without code changes
- Environment-specific module configurations
### 5. Hot Module Replacement
- Develop modules independently without full app restart
- Live reloading of individual modules during development
- Isolated testing environments for each module
---
## Implementation Roadmap
### High Priority (Immediate)
1. **Extract RelayHub/NostrclientHub to base module** (mostly complete)
2. **Create plugin registration system** - Core infrastructure for module loading
3. **Convert nostr-feed to plugin pattern** - Proof of concept implementation
### Medium Priority (Phase 2)
4. **Implement event bus communication** - Replace direct imports between modules
5. **Add module lifecycle management** - Proper setup/teardown hooks
6. **Create development tooling** - Scripts for isolated module development
### Low Priority (Phase 3)
7. **Add module versioning support** - Handle different module versions
8. **Implement hot module replacement** - Live development workflow
9. **Add module marketplace** - Plugin discovery and installation
---
## Conclusion
The current web application has excellent technical foundations, particularly the Nostr infrastructure (RelayHub/NostrclientHub), but requires significant architectural refactoring to achieve true modularity.
**Key Strengths to Preserve:**
- Robust Nostr client implementation
- Clean component organization
- Solid TypeScript foundations
- PWA capabilities
**Critical Areas for Improvement:**
- Plugin architecture implementation
- Module boundary enforcement
- Event-driven communication
- Development workflow optimization
**Estimated Effort:**
- **Phase 1 (Core Infrastructure):** 2-3 weeks
- **Phase 2 (Module Extraction):** 3-4 weeks
- **Phase 3 (Advanced Features):** 2-3 weeks
The investment in modular architecture will enable independent development of features, easier testing, better code maintainability, and the ability to dynamically enable/disable functionality based on deployment requirements.
---
**Generated by:** Claude Code Architecture Analysis
**Last Updated:** September 4, 2025

View file

@ -0,0 +1,380 @@
# Web App Architecture Analysis & Modularity Assessment
> **⚠️ OUTDATED DOCUMENT**
> **Updated Version**: `/docs/modular-architecture-analysis.md`
> **Date**: This document from September 4, 2025 is outdated - significant architectural changes implemented September 6, 2025
**Date:** September 4, 2025
**Project:** Ario Web App (Vue 3 + Nostr + LNbits)
**Objective:** Evaluate current architecture for modular plugin-based development
---
## Executive Summary
The current web application demonstrates solid foundational architecture with excellent Nostr infrastructure (RelayHub/NostrclientHub) but lacks true modularity for plugin-based development.
**Overall Modularity Rating: 6.5/10**
The app successfully implements core Nostr functionality and component organization but requires significant architectural refactoring to achieve the stated objective of independent, pluggable feature modules.
---
## Current Architecture Assessment
### ✅ Strengths (What's Working Well)
#### 1. Strong Base Infrastructure (8/10)
- **RelayHub & NostrclientHub**: Excellent centralized Nostr infrastructure with robust connection management, event handling, and browser compatibility
- **Pinia Stores**: Clean state management separation (`nostr.ts`, `market.ts`) with reactive patterns
- **Composables Pattern**: Good reactive logic encapsulation (`useRelayHub`, `useNostrChat`, `useMarket`)
- **UI Component Library**: Well-structured Shadcn/ui components with consistent design system
#### 2. Component Organization (7/10)
- Components grouped by domain (`/market`, `/nostr`, `/events`, `/auth`)
- Page-level routing with lazy loading implementation
- Composables provide reusable business logic across components
- Clean separation of concerns between UI and business logic
#### 3. Service Layer (6/10)
- Clean service abstractions (`nostrmarketService`, `paymentMonitor`)
- API layer separation (`/lib/api`)
- Type-safe interfaces and proper error handling
### ❌ Critical Issues (What's Missing)
#### 1. No Plugin Architecture (3/10)
- **Hard-coded routes**: All features baked into main router configuration
- **Tight coupling**: Components directly import across modules without abstraction
- **No feature isolation**: Cannot develop/deploy components independently
- **No plugin registration system**: Missing infrastructure for dynamic feature loading
#### 2. Cross-Module Dependencies (4/10)
**Examples of problematic tight coupling:**
```typescript
// Payment monitor importing market types directly
src/lib/services/paymentMonitor.ts: import type { Order } from '@/stores/market'
// Navbar importing chat composable directly
src/components/layout/Navbar.vue: import { nostrChat } from '@/composables/useNostrChat'
// Services importing market store directly
src/lib/services/nostrmarketService.ts: import type { Stall, Product, Order } from '@/stores/market'
```
#### 3. Missing Module Boundaries (5/10)
- No clear module interfaces/contracts
- Shared types scattered across modules without centralized definition
- No dependency injection system for service management
- Missing module lifecycle management
---
## Required Changes for Modular Architecture
### Phase 1: Core Infrastructure Refactor
#### 1. Create Module System
```typescript
// /src/modules/base/types.ts
interface ModuleConfig {
name: string
routes: RouteConfig[]
components: ComponentConfig[]
services: ServiceConfig[]
dependencies?: string[]
}
interface ModulePlugin {
install(app: App, options?: any): void
uninstall(): void
}
```
#### 2. Plugin Registration System
```typescript
// /src/core/plugin-manager.ts
class PluginManager {
private plugins = new Map<string, ModulePlugin>()
register(module: ModulePlugin): void
unregister(name: string): void
getModule(name: string): ModulePlugin
getDependencies(name: string): string[]
}
```
### Phase 2: Module Extraction
#### 1. Base Module Structure
```
/src/modules/base/
├── nostr/
│ ├── relayHub.ts # Centralized relay management
│ ├── nostrclientHub.ts # Client connection handling
│ └── types.ts # Nostr-specific types
├── auth/
│ ├── lnbits.ts # LNbits authentication
│ └── composables/
├── pwa/
│ ├── install-prompt.ts
│ └── notifications.ts
└── ui/ # Shadcn components
```
#### 2. Feature Modules
```
/src/modules/
├── nostr-feed/
│ ├── components/
│ │ └── NostrFeed.vue
│ ├── composables/
│ │ └── useFeed.ts
│ ├── services/
│ │ └── feedService.ts
│ └── index.ts # Module export
├── market/
│ ├── components/
│ ├── composables/
│ ├── stores/
│ ├── services/
│ └── index.ts
├── chat/
└── events/
```
#### 3. Module Interface Standards
```typescript
// Each module exports standardized interface
export interface NostrFeedModule extends ModulePlugin {
name: 'nostr-feed'
components: {
NostrFeed: Component
}
routes: RouteConfig[]
dependencies: ['base']
config?: NostrFeedConfig
}
```
### Phase 3: Event-Driven Communication
#### Replace Direct Imports with Event Bus
```typescript
// /src/core/event-bus.ts
interface ModuleEventBus {
emit(event: string, data: any): void
on(event: string, handler: Function): void
off(event: string, handler: Function): void
}
// Example usage:
// Market module: eventBus.emit('order:created', order)
// Chat module: eventBus.on('order:created', handleNewOrder)
```
#### Dependency Injection Container
```typescript
// /src/core/di-container.ts
class DIContainer {
provide<T>(token: string, instance: T): void
inject<T>(token: string): T
// Module-scoped injection
provideForModule(module: string, token: string, instance: any): void
}
```
---
## Recommended Ground-Up Architecture
If rebuilding from scratch, implement these architectural patterns:
### 1. Module-First Architecture
```
src/
├── core/ # Base app infrastructure
│ ├── plugin-manager.ts # Plugin registration & lifecycle
│ ├── di-container.ts # Dependency injection
│ ├── event-bus.ts # Inter-module communication
│ ├── module-registry.ts # Module discovery & loading
│ └── base-services/ # Core services
├── modules/
│ ├── base/ # Core functionality (required)
│ │ ├── nostr/ # RelayHub, NostrclientHub
│ │ ├── auth/ # LNbits authentication
│ │ ├── pwa/ # Progressive Web App features
│ │ └── ui/ # Shadcn component library
│ ├── nostr-feed/ # Announcements & social feed
│ ├── market/ # Marketplace functionality
│ ├── chat/ # Nostr chat implementation
│ └── events/ # Event ticketing system
└── app.ts # Application composition & startup
```
### 2. Configuration-Driven Setup
```typescript
// /src/app.config.ts
export const appConfig = {
modules: {
base: {
required: true,
nostr: {
relays: process.env.VITE_NOSTR_RELAYS
}
},
'nostr-feed': {
enabled: true,
config: {
refreshInterval: 30000,
maxPosts: 100
}
},
market: {
enabled: true,
config: {
defaultCurrency: 'sats',
paymentTimeout: 300000
}
},
chat: {
enabled: false // Can be toggled via config
},
events: {
enabled: true,
config: {
ticketValidationEndpoint: '/api/tickets/validate'
}
}
},
features: {
pwa: true,
pushNotifications: true,
electronApp: true
}
}
```
### 3. Dynamic Module Loading
```typescript
// /src/core/module-loader.ts
class ModuleLoader {
async loadModule(name: string): Promise<ModulePlugin> {
const module = await import(`@/modules/${name}/index.ts`)
await this.resolveDependencies(module.dependencies || [])
return module.default
}
async unloadModule(name: string): Promise<void> {
const module = this.getModule(name)
if (module) {
await module.uninstall()
this.cleanupRoutes(name)
this.cleanupServices(name)
}
}
private async resolveDependencies(deps: string[]): Promise<void> {
for (const dep of deps) {
if (!this.isModuleLoaded(dep)) {
await this.loadModule(dep)
}
}
}
}
```
### 4. Module Development Workflow
```bash
# Develop single module in isolation
npm run dev:module market
# Test module independently
npm run test:module market
# Build specific modules only
npm run build:modules market,chat
# Hot reload module during development
npm run dev:hot-module nostr-feed
```
---
## Key Architectural Improvements
### 1. Dependency Inversion
- Modules depend on interfaces, not concrete implementations
- Services injected via DI container rather than direct imports
- Clear separation between module contracts and implementations
### 2. Event-Driven Communication
- Replace direct imports with event-based messaging
- Loose coupling between modules via standardized events
- Centralized event routing and handling
### 3. Module Lifecycle Management
- Proper setup and teardown hooks for each module
- Resource cleanup when modules are disabled/unloaded
- Dependency resolution during module loading
### 4. Configuration Over Code
- Enable/disable features via configuration files
- Runtime module toggling without code changes
- Environment-specific module configurations
### 5. Hot Module Replacement
- Develop modules independently without full app restart
- Live reloading of individual modules during development
- Isolated testing environments for each module
---
## Implementation Roadmap
### High Priority (Immediate)
1. ✅ **Extract RelayHub/NostrclientHub to base module** (mostly complete)
2. **Create plugin registration system** - Core infrastructure for module loading
3. **Convert nostr-feed to plugin pattern** - Proof of concept implementation
### Medium Priority (Phase 2)
4. **Implement event bus communication** - Replace direct imports between modules
5. **Add module lifecycle management** - Proper setup/teardown hooks
6. **Create development tooling** - Scripts for isolated module development
### Low Priority (Phase 3)
7. **Add module versioning support** - Handle different module versions
8. **Implement hot module replacement** - Live development workflow
9. **Add module marketplace** - Plugin discovery and installation
---
## Conclusion
The current web application has excellent technical foundations, particularly the Nostr infrastructure (RelayHub/NostrclientHub), but requires significant architectural refactoring to achieve true modularity.
**Key Strengths to Preserve:**
- Robust Nostr client implementation
- Clean component organization
- Solid TypeScript foundations
- PWA capabilities
**Critical Areas for Improvement:**
- Plugin architecture implementation
- Module boundary enforcement
- Event-driven communication
- Development workflow optimization
**Estimated Effort:**
- **Phase 1 (Core Infrastructure):** 2-3 weeks
- **Phase 2 (Module Extraction):** 3-4 weeks
- **Phase 3 (Advanced Features):** 2-3 weeks
The investment in modular architecture will enable independent development of features, easier testing, better code maintainability, and the ability to dynamically enable/disable functionality based on deployment requirements.
---
**Generated by:** Claude Code Architecture Analysis
**Last Updated:** September 4, 2025

File diff suppressed because it is too large Load diff