···11+# Gleam Lustre Fullstack App
22+33+A fullstack demo application built with Gleam and Lustre. Users can authenticate via OAuth, view and edit their profiles.
44+55+## Features
66+77+- **OAuth 2.0 Authentication** - Secure authentication with PKCE flow for Bluesky/ATProto
88+- **Profile Management** - View and edit user profiles with display name, description, avatar, location, and interests
99+- **Avatar Upload** - Upload and preview profile images with decentralized blob storage
1010+- **Location Search** - Autocomplete location selection with H3 geohashing for geographic indexing
1111+- **Server-Side Rendering** - Fast initial page loads with prerendered profile data
1212+- **Client-Side Routing** - Smooth navigation with Modem routing library
1313+- **Session Management** - Secure cookie-based sessions with SQLite storage
1414+- **GraphQL Integration** - Direct integration with Slices network API for ATProto data
1515+1616+## Tech Stack
1717+1818+### Frontend (Client)
1919+- **Gleam** - Type-safe functional language compiled to JavaScript
2020+- **Lustre** - Elm-inspired web framework for reactive UIs
2121+- **Modem** - Client-side routing
2222+- **h3-js** - H3 geohashing for location indexing
2323+- **Tailwind CSS** - Utility-first CSS framework
2424+2525+### Backend (Server)
2626+- **Gleam** - Compiled to Erlang/BEAM
2727+- **Wisp** - Web framework for request handling
2828+- **Mist** - HTTP server runtime
2929+- **SQLight** - SQLite database driver
3030+- **Storail** - Session management
3131+- **Glow Auth** - OAuth utilities
3232+3333+### Shared
3434+- Monorepo structure with shared types and utilities between client and server
3535+3636+## Prerequisites
3737+3838+- [Gleam](https://gleam.run/) (latest version)
3939+- [Erlang/OTP](https://www.erlang.org/) (for server)
4040+- [Node.js](https://nodejs.org/) and npm (for client dependencies)
4141+4242+## Project Structure
4343+4444+```
4545+lustre-fullstack/
4646+├── client/ # Lustre frontend (SPA)
4747+│ ├── src/
4848+│ │ ├── client.gleam # Main entry point
4949+│ │ ├── pages/ # Page components
5050+│ │ └── ui/ # Reusable UI components
5151+│ ├── gleam.toml
5252+│ └── package.json
5353+├── server/ # Wisp backend
5454+│ ├── src/
5555+│ │ ├── server.gleam # Main server & routing
5656+│ │ ├── api/ # API handlers
5757+│ │ └── oauth/ # OAuth & session logic
5858+│ ├── gleam.toml
5959+│ ├── .env.example
6060+│ └── priv/static/ # Built client output
6161+└── shared/ # Shared code between client & server
6262+ └── src/shared/
6363+ └── profile.gleam # Profile types & codecs
6464+```
6565+6666+## Setup
6767+6868+### 1. Clone the Repository
6969+7070+```bash
7171+git clone <repository-url>
7272+cd conf-demo
7373+```
7474+7575+### 2. Set Up Server
7676+7777+```bash
7878+cd server
7979+8080+# Install Gleam dependencies
8181+gleam deps download
8282+8383+# Copy environment template
8484+cp .env.example .env
8585+8686+# Edit .env with your configuration
8787+# Generate SECRET_KEY_BASE with: openssl rand -base64 48
8888+```
8989+9090+### 3. Set Up Client
9191+9292+```bash
9393+cd client
9494+9595+# Install Gleam dependencies
9696+gleam deps download
9797+9898+# Install npm dependencies (h3-js)
9999+npm install
100100+```
101101+102102+### 4. Set Up Shared Package
103103+104104+```bash
105105+cd shared
106106+gleam deps download
107107+```
108108+109109+## Configuration
110110+111111+Edit `server/.env` with your OAuth and application settings:
112112+113113+```bash
114114+# Secret Key Base (generate with: openssl rand -base64 48)
115115+SECRET_KEY_BASE=your_random_64_character_string
116116+117117+# OAuth Configuration
118118+OAUTH_CLIENT_ID=your_oauth_client_id
119119+OAUTH_CLIENT_SECRET=your_oauth_client_secret
120120+OAUTH_REDIRECT_URI=http://localhost:3000/oauth/callback
121121+OAUTH_AUTH_URL=https://auth.slices.network
122122+```
123123+124124+## Development
125125+126126+### Build Client
127127+128128+The client builds directly into `server/priv/static` for serving:
129129+130130+```bash
131131+cd client
132132+gleam run
133133+```
134134+135135+This compiles the Lustre app with minification and outputs to `../server/priv/static`.
136136+137137+### Run Server
138138+139139+```bash
140140+cd server
141141+gleam run
142142+```
143143+144144+The server starts on `http://localhost:3000` and serves the built client from `priv/static`.
145145+146146+### Development Workflow
147147+148148+1. Make changes to client code in `client/src/`
149149+2. Rebuild client: `cd client && gleam run`
150150+3. Server automatically serves updated static files
151151+4. For server changes, restart the server
152152+153153+Alternatively, run both in separate terminals with auto-rebuild on file changes using your preferred file watcher.
154154+155155+## API Endpoints
156156+157157+### Public Routes
158158+- `GET /` - Home page
159159+- `GET /login` - Login page
160160+- `GET /profile/:handle` - View profile (with SSR)
161161+- `POST /oauth/authorize` - Initiate OAuth flow
162162+- `GET /oauth/callback` - OAuth callback
163163+164164+### Protected Routes (Require Authentication)
165165+- `GET /profile/:handle/edit` - Edit profile page
166166+- `POST /api/profile/:handle/update` - Update profile data
167167+- `POST /logout` - Logout
168168+169169+### API Routes
170170+- `GET /api/user/current` - Get current authenticated user
171171+- `GET /api/profile/:handle` - Fetch profile data (JSON)
172172+173173+## Database
174174+175175+The application uses SQLite for session storage:
176176+- Database file: `server/sessions.db` (auto-created on first run)
177177+- Schema managed by `server/src/oauth/session.gleam`
178178+179179+## Features in Detail
180180+181181+### OAuth with PKCE
182182+Implements OAuth 2.0 with Proof Key for Code Exchange (PKCE) for secure authentication without storing client secrets in the browser.
183183+184184+### H3 Geohashing
185185+Location data is indexed using Uber's H3 geospatial indexing system for efficient location queries and autocomplete.
186186+187187+### Server-Side Rendering
188188+Profile pages include prerendered data in the initial HTML response, embedded as JSON in a script tag for instant hydration.
189189+190190+### GraphQL Integration
191191+Direct queries and mutations to the Slices network API for ATProto profile data with access token support.
192192+193193+## Building for Production
194194+195195+### Client
196196+```bash
197197+cd client
198198+gleam run # Outputs minified bundle to ../server/priv/static
199199+```
200200+201201+### Server
202202+```bash
203203+cd server
204204+gleam build
205205+gleam run
206206+```
207207+208208+The server serves the built client from `priv/static` at runtime.
209209+210210+## Testing
211211+212212+```bash
213213+# Test client
214214+cd client
215215+gleam test
216216+217217+# Test server
218218+cd server
219219+gleam test
220220+221221+# Test shared
222222+cd shared
223223+gleam test
224224+```
225225+226226+## Contributing
227227+228228+1. Fork the repository
229229+2. Create a feature branch (`git checkout -b feature/amazing-feature`)
230230+3. Commit your changes (`git commit -m 'Add amazing feature'`)
231231+4. Push to the branch (`git push origin feature/amazing-feature`)
232232+5. Open a Pull Request
233233+234234+## License
235235+236236+Apache License, Version 2.0
237237+238238+## Acknowledgments
239239+240240+- Built with [Gleam](https://gleam.run/)
241241+- Frontend powered by [Lustre](https://github.com/lustre-labs/lustre)
242242+- Backend powered by [Wisp](https://github.com/gleam-wisp/wisp) and [Mist](https://github.com/rawhat/mist)
243243+- Location indexing with [H3](https://h3geo.org/)
+2-2
server/.env.example
···77# These values will be used if environment variables are not set
8899# OAuth Client ID (used in authorization request)
1010-OAUTH_CLIENT_ID=43d2e6e5-60fe-491c-b93e-536ac99b71f1
1010+OAUTH_CLIENT_ID=client-id
11111212# OAuth Client Secret (used in token exchange)
1313-OAUTH_CLIENT_SECRET=0elWjAl695lupMGMZ0IOo9xEy11dNiY96L08b6z_xZw
1313+OAUTH_CLIENT_SECRET=client-secret
14141515# OAuth Redirect URI (where the OAuth provider redirects after authorization)
1616OAUTH_REDIRECT_URI=http://localhost:3000/oauth/callback
+1
server/gleam.toml
···2828sqlight = ">= 1.0.2 and < 2.0.0"
2929envoy = ">= 1.0.2 and < 2.0.0"
3030dotenv_gleam = ">= 2.0.1 and < 3.0.0"
3131+birl = ">= 1.0.0 and < 2.0.0"
31323233[erlang]
3334extra_applications = ["inets", "ssl"]