Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,16 @@ _UpgradeReport*
*.swp
TestResult.*
.xake
.fake/
.vs/
.ionide/
.idea/
.ionide/

CHAT_DATA/

**/public/bundle.js*

# NuGet Packages Directory
paket-files/
packages/

# Node
node_modules/
project.lock.json
paket.local
fable_modules/

src/**/*.fs.js
src/Client/public/
61 changes: 0 additions & 61 deletions .idea/.idea.fschat/.idea/contentModel.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/.idea.fschat/.idea/modules.xml

This file was deleted.

7 changes: 0 additions & 7 deletions .idea/.idea.fschat/riderModule.iml

This file was deleted.

1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
461 changes: 0 additions & 461 deletions .paket/Paket.Restore.targets

This file was deleted.

Binary file removed .paket/paket.exe
Binary file not shown.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "coreclr",
"request": "launch",
// "preLaunchTask": "build",
"program": "${workspaceFolder}/src/Server/bin/Debug/netcoreapp2.0/fschathost.dll",
"program": "${workspaceFolder}/src/Server/bin/Debug/net8.0/fschathost.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
Expand Down
114 changes: 114 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

SAFE-Chat is a real-time chat application built with F#, .NET, Akka.NET, and Fable. The architecture follows a client-server model where:

- **Server**: F# with Akka.NET actors for concurrent chat handling, Giraffe for HTTP/WebSocket serving
- **Client**: F# compiled to JavaScript via Fable, using Elmish (MVU pattern) and React
- **Shared**: Common protocol definitions between client and server

## Development Commands

### Prerequisites
- Switch to proper node version: `nvm use`
- Install dependencies: `yarn`

### Building Production
```bash
# Full production build
./build.cmd

# Manual steps:
cd src/Client
dotnet fable webpack -- -p
cd ../Server
dotnet build
```

### Development Workflow
```bash
# Start server with hot reload
./dev-server.cmd
# or manually: cd src/Server && dotnet watch run

# Start client dev server (separate terminal)
./dev-cli.cmd
# or manually: cd src/Client && dotnet fable webpack-dev-server
```

- Server runs on `http://localhost:8083`
- Client dev server runs on `http://localhost:8080` with HMR
- Client proxies API calls to server

### Testing
```bash
# E2E tests (Windows only, requires server running)
cd test/e2e
dotnet restore
dotnet run
```

## Architecture

### Core Communication Flow
1. **WebSocket Protocol**: All real-time communication uses WebSockets after authentication
2. **Shared Protocol**: `src/Shared/ChatProtocol.fs` defines all message types between client/server
3. **Actor System**: Server uses Akka.NET actors for concurrent message handling

### Key Server Components
- **ChatServer.fs**: Main server actor managing channels and user sessions
- **GroupChatChannelActor.fs**: Individual channel actors handling chat messages
- **UserSessionFlow.fs**: Manages individual user WebSocket connections
- **SocketFlow.fs**: WebSocket connection wrapper

### Key Client Components
- **App.fs**: Main Elmish application entry point with routing
- **State.fs**: Global application state management
- **Channel/**: Channel-specific UI components and state
- **Chat/**: Chat message handling and state

### Authentication
- Supports anonymous users
- OAuth is not restored yet

### Data Persistence
- Akka.NET persistence for chat channels using event sourcing
- Custom JSON event adapter in `AkkaStuff.fs`
- Journal database in `CHAT_DATA/journal.db/`

## File Structure
```
src/
├── Client/ # Fable F# → JavaScript client
│ ├── webpack.config.js
│ ├── public/ # Static assets and generated bundle
│ └── sass/ # Styling
├── Server/ # F# server with Akka.NET
├── Shared/ # Shared protocol definitions
└── Dockerfile # Container configuration
test/e2e/ # Canopy-based integration tests
```

## Development Notes

### Client Development
- Uses Fable compiler with Webpack for bundling
- Elmish MVU (Model-View-Update) architecture
- Hot module reloading enabled in development
- React components via Fable.React

### Server Development
- Akka.NET actors for concurrency
- Event sourcing for persistence
- Giraffe for HTTP/WebSocket handling
- `dotnet watch run` provides hot reload

### Protocol Changes
When modifying `src/Client/src/Shared/ChatProtocol.fs`:
1. Update both client and server message handlers
2. Consider backward compatibility for persistent data
3. Test with both OAuth and anonymous authentication flows
2 changes: 1 addition & 1 deletion build.cmd
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fake build
dotnet fsi build.fsx
44 changes: 17 additions & 27 deletions build.fsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#r "paket:
nuget Xake ~> 1.1 prerelease //"
// xake build file

#r "nuget: Xake, 2.3.0"

#load ".fake/build.fsx/intellisense.fsx"

// Notice: this is not a traditional FAKE script.
// Instead it uses Xake module to define build targets and rules in CMAKE fashion.
Expand All @@ -10,57 +10,47 @@
open Xake
open Xake.Tasks

let clientBundle = "src/Client/public/bundle.js"
let serverDllRel = "bin/Debug/netcoreapp2.0/fschathost.dll"
let clientBundle = "src/Client/public/index.html"
let serverDllRel = "bin/Debug/net8.0/fschathost.dll"
let serverDll = "src/Server/" + serverDllRel

let shellx cmd folder =
let command::arglist | OtherwiseFail(command,arglist) = (cmd: string).Split(' ') |> List.ofArray
shell {
cmd command
args arglist
workdir folder
failonerror
} |> Ignore

do xakeScript {
consolelog Diag

rules [
// main (default) target is to sequentially restore deps and build
"main" <<< [ "restore"; "build" ]

// cleans the build artifacts
"clean" => recipe {
do! rm {file "src/Client/bundle.*"}
do! rm {dir "src/Client/public"}
do! rm {file "src/Client/**/*.fs.js"}
do! rm {dir "src/*/bin/*"; verbose }
do! rm {dir "src/*/obj/*"; verbose }
}

// restores packages and node modules
"restore" => recipe {
do! "src/Client" |> shellx "yarn"
do! "src/Server" |> shellx "dotnet restore"
do! sh "yarn" { workdir "src/Client" }
do! sh "dotnet restore" { workdir "src/Server" }
}

// build the client bundle
clientBundle ..> recipe {
// record dependencies so that Xake will track the changes
let! files = getFiles (fileset {
do! dependsOn (fileset {
basedir "src/Client"
includes "**/*.fs"
includes "**/*.*css"
includes "webpack.config.js"
includes "vite.config.js"
includes "yarn.lock"
includes "client.fsproj"
})
do! needFiles files
do! "src/Client" |> shellx "yarn build"
do! sh "yarn build" { workdir "src/Client" }
}

serverDll ..> recipe {
do! need [clientBundle]
do! "src/Server" |> shellx "dotnet build"
do! sh "dotnet build" { workdir "src/Server" }
}

// build the application
Expand All @@ -74,10 +64,10 @@ do xakeScript {

"start:server" => recipe {
do! need ["build"]
do! "src/Server" |> shellx ("dotnet " + serverDllRel)
do! sh ("dotnet " + serverDllRel) { workdir "src/Server" }
}
"test-e2e" => shellx "dotnet run" "test/e2e"
// opens the application in browser, windows only
"start:browser" => shellx "start http://localhost:8083" "."
"test-e2e" => sh "dotnet run" { workdir "test/e2e" }
// opens the application in browser, macos only, for windows this has to be replaced to start http://...
"start:browser" => sh "open http://localhost:8083" { workdir "." }
]
}
26 changes: 0 additions & 26 deletions build.fsx.lock

This file was deleted.

6 changes: 6 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

# SAFE-Chat Build Script
# Runs the F# build script with dotnet fsi and passes through all arguments

dotnet fsi build.fsx -- -- "$@"
3 changes: 0 additions & 3 deletions dev-cli.cmd

This file was deleted.

3 changes: 3 additions & 0 deletions dev-client.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dotnet restore
cd src\Client
yarn start
7 changes: 0 additions & 7 deletions fake.cmd

This file was deleted.

Loading