diff --git a/docs/diagrams-out/architecture.svg b/docs/diagrams-out/architecture.svg
new file mode 100644
index 0000000..f7b8cdd
--- /dev/null
+++ b/docs/diagrams-out/architecture.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/diagrams-out/authentication-flow.svg b/docs/diagrams-out/authentication-flow.svg
new file mode 100644
index 0000000..178d020
--- /dev/null
+++ b/docs/diagrams-out/authentication-flow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/diagrams-out/database-schema.svg b/docs/diagrams-out/database-schema.svg
new file mode 100644
index 0000000..47169b0
--- /dev/null
+++ b/docs/diagrams-out/database-schema.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/diagrams-out/deployment.svg b/docs/diagrams-out/deployment.svg
new file mode 100644
index 0000000..4c28a85
--- /dev/null
+++ b/docs/diagrams-out/deployment.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/diagrams/architecture.puml b/docs/diagrams-src/architecture.puml
similarity index 100%
rename from docs/diagrams/architecture.puml
rename to docs/diagrams-src/architecture.puml
diff --git a/docs/diagrams/authentication-flow.puml b/docs/diagrams-src/authentication-flow.puml
similarity index 100%
rename from docs/diagrams/authentication-flow.puml
rename to docs/diagrams-src/authentication-flow.puml
diff --git a/docs/diagrams/database-schema.puml b/docs/diagrams-src/database-schema.puml
similarity index 100%
rename from docs/diagrams/database-schema.puml
rename to docs/diagrams-src/database-schema.puml
diff --git a/docs/diagrams/deployment.puml b/docs/diagrams-src/deployment.puml
similarity index 100%
rename from docs/diagrams/deployment.puml
rename to docs/diagrams-src/deployment.puml
diff --git a/docs/diagrams/class-diagram.puml b/docs/diagrams/class-diagram.puml
deleted file mode 100644
index 6817eaf..0000000
--- a/docs/diagrams/class-diagram.puml
+++ /dev/null
@@ -1,523 +0,0 @@
-@startuml class-diagram
-!theme plain
-skinparam backgroundColor #FFFFFF
-skinparam defaultFontName Arial
-skinparam classAttributeIconSize 0
-skinparam linetype ortho
-
-title Biergarten Application - Class Diagram
-
-' API Layer
-package "API.Core" <> #E3F2FD {
-
- class AuthController {
- - IRegisterService _registerService
- - ILoginService _loginService
- + <> Task Register(RegisterRequest)
- + <> Task Login(LoginRequest)
- }
-
- class UserController {
- - IUserService _userService
- + <> Task>> GetAll(int?, int?)
- + <> Task> GetById(Guid)
- }
-
- class GlobalExceptionFilter {
- - ILogger _logger
- + void OnException(ExceptionContext)
- }
-
- package "Contracts" {
- class RegisterRequest <> {
- + string Username
- + string FirstName
- + string LastName
- + string Email
- + DateTime DateOfBirth
- + string Password
- }
-
- class LoginRequest <> {
- + string Username
- + string Password
- }
-
- class RegisterRequestValidator {
- + RegisterRequestValidator()
- }
-
- class LoginRequestValidator {
- + LoginRequestValidator()
- }
-
- class "ResponseBody" <> {
- + string Message
- + T Payload
- }
-
- class LoginPayload <> {
- + Guid UserAccountId
- + string Username
- + string RefreshToken
- + string AccessToken
- }
-
- class RegistrationPayload <> {
- + Guid UserAccountId
- + string Username
- + string RefreshToken
- + string AccessToken
- + bool ConfirmationEmailSent
- }
- }
-}
-
-' Service Layer
-package "Service Layer" <> #C8E6C9 {
-
- package "Service.Auth" {
- interface IRegisterService {
- + <> Task RegisterAsync(UserAccount, string)
- }
-
- class RegisterService {
- - IAuthRepository _authRepo
- - IPasswordInfrastructure _passwordInfra
- - ITokenService _tokenService
- - IEmailService _emailService
- + <> Task RegisterAsync(UserAccount, string)
- - <> Task ValidateUserDoesNotExist(UserAccount)
- }
-
- interface ILoginService {
- + <> Task LoginAsync(string, string)
- }
-
- class LoginService {
- - IAuthRepository _authRepo
- - IPasswordInfrastructure _passwordInfra
- - ITokenService _tokenService
- + <> Task LoginAsync(string, string)
- }
-
- interface ITokenService {
- + string GenerateAccessToken(UserAccount)
- + string GenerateRefreshToken(UserAccount)
- }
-
- class TokenService {
- - ITokenInfrastructure _tokenInfrastructure
- + string GenerateAccessToken(UserAccount)
- + string GenerateRefreshToken(UserAccount)
- }
-
- class RegisterServiceReturn <> {
- + bool IsAuthenticated
- + bool EmailSent
- + UserAccount UserAccount
- + string AccessToken
- + string RefreshToken
- }
-
- class LoginServiceReturn <> {
- + UserAccount UserAccount
- + string RefreshToken
- + string AccessToken
- }
- }
-
- package "Service.UserManagement" {
- interface IUserService {
- + <> Task> GetAllAsync(int?, int?)
- + <> Task GetByIdAsync(Guid)
- + <> Task UpdateAsync(UserAccount)
- }
-
- class UserService {
- - IUserAccountRepository _repository
- + <> Task> GetAllAsync(int?, int?)
- + <> Task GetByIdAsync(Guid)
- + <> Task UpdateAsync(UserAccount)
- }
- }
-
- package "Service.Emails" {
- interface IEmailService {
- + <> Task SendRegistrationEmailAsync(UserAccount, string)
- }
-
- class EmailService {
- - IEmailProvider _emailProvider
- - IEmailTemplateProvider _templateProvider
- + <> Task SendRegistrationEmailAsync(UserAccount, string)
- }
- }
-}
-
-' Domain Layer
-package "Domain" <> #FFF9C4 {
-
- package "Domain.Entities" {
- class UserAccount {
- + Guid UserAccountId
- + string Username
- + string FirstName
- + string LastName
- + string Email
- + DateTime CreatedAt
- + DateTime? UpdatedAt
- + DateTime DateOfBirth
- + byte[]? Timer
- }
-
- class UserCredential {
- + Guid UserCredentialId
- + Guid UserAccountId
- + DateTime CreatedAt
- + DateTime Expiry
- + string Hash
- + byte[]? Timer
- }
-
- class UserVerification {
- + Guid UserVerificationId
- + Guid UserAccountId
- + DateTime VerificationDateTime
- + byte[]? Timer
- }
- }
-
- package "Domain.Exceptions" {
- class ConflictException {
- + ConflictException(string)
- }
-
- class NotFoundException {
- + NotFoundException(string)
- }
-
- class UnauthorizedException {
- + UnauthorizedException(string)
- }
-
- class ForbiddenException {
- + ForbiddenException(string)
- }
-
- class ValidationException {
- + ValidationException(string)
- }
- }
-}
-
-' Infrastructure Layer
-package "Infrastructure" <> #E1BEE7 {
-
- package "Infrastructure.Repository" {
- interface IAuthRepository {
- + <> Task RegisterUserAsync(string, string, string, string, DateTime, string)
- + <> Task GetUserByEmailAsync(string)
- + <> Task GetUserByUsernameAsync(string)
- + <> Task GetActiveCredentialByUserAccountIdAsync(Guid)
- + <> Task RotateCredentialAsync(Guid, string)
- }
-
- class AuthRepository {
- - ISqlConnectionFactory _connectionFactory
- + <> Task RegisterUserAsync(...)
- + <> Task GetUserByEmailAsync(string)
- + <> Task GetUserByUsernameAsync(string)
- + <> Task GetActiveCredentialByUserAccountIdAsync(Guid)
- + <> Task RotateCredentialAsync(Guid, string)
- # UserAccount MapToEntity(DbDataReader)
- - UserCredential MapToCredentialEntity(DbDataReader)
- }
-
- interface IUserAccountRepository {
- + <> Task GetByIdAsync(Guid)
- + <> Task> GetAllAsync(int?, int?)
- + <> Task UpdateAsync(UserAccount)
- + <> Task DeleteAsync(Guid)
- + <> Task GetByUsernameAsync(string)
- + <> Task GetByEmailAsync(string)
- }
-
- class UserAccountRepository {
- - ISqlConnectionFactory _connectionFactory
- + <> Task GetByIdAsync(Guid)
- + <> Task> GetAllAsync(int?, int?)
- + <> Task UpdateAsync(UserAccount)
- + <> Task DeleteAsync(Guid)
- + <> Task GetByUsernameAsync(string)
- + <> Task GetByEmailAsync(string)
- # UserAccount MapToEntity(DbDataReader)
- }
-
- abstract class "Repository" {
- # ISqlConnectionFactory _connectionFactory
- # <> Task CreateConnection()
- # {abstract} T MapToEntity(DbDataReader)
- }
-
- package "Sql" {
- interface ISqlConnectionFactory {
- + DbConnection CreateConnection()
- }
-
- class DefaultSqlConnectionFactory {
- - string _connectionString
- + DbConnection CreateConnection()
- }
-
- class SqlConnectionStringHelper <> {
- + {static} string BuildConnectionString(string?)
- + {static} string BuildMasterConnectionString()
- }
- }
- }
-
- package "Infrastructure.PasswordHashing" {
- interface IPasswordInfrastructure {
- + string Hash(string)
- + bool Verify(string, string)
- }
-
- class Argon2Infrastructure {
- - {static} int SaltSize = 16
- - {static} int HashSize = 32
- - {static} int ArgonIterations = 4
- - {static} int ArgonMemoryKb = 65536
- + string Hash(string)
- + bool Verify(string, string)
- }
- }
-
- package "Infrastructure.Jwt" {
- interface ITokenInfrastructure {
- + string GenerateJwt(Guid, string, DateTime)
- }
-
- class JwtInfrastructure {
- - string? _secret
- + string GenerateJwt(Guid, string, DateTime)
- }
- }
-
- package "Infrastructure.Email" {
- interface IEmailProvider {
- + <> Task SendAsync(string, string, string, bool)
- + <> Task SendAsync(IEnumerable, string, string, bool)
- }
-
- class SmtpEmailProvider {
- - string _host
- - int _port
- - string? _username
- - string? _password
- - bool _useSsl
- - string _fromEmail
- - string _fromName
- + <> Task SendAsync(string, string, string, bool)
- + <> Task SendAsync(IEnumerable, string, string, bool)
- }
- }
-
- package "Infrastructure.Email.Templates" {
- interface IEmailTemplateProvider {
- + <> Task RenderUserRegisteredEmailAsync(string, string)
- }
-
- class EmailTemplateProvider {
- - IServiceProvider _serviceProvider
- - ILoggerFactory _loggerFactory
- + <> Task RenderUserRegisteredEmailAsync(string, string)
- - <> Task RenderComponentAsync(Dictionary)
- }
-
- class "UserRegistration <>" {
- + string Username
- + string ConfirmationLink
- }
-
- class "Header <>" {
- }
-
- class "Footer <>" {
- + string? FooterText
- }
- }
-}
-
-' Database Layer
-package "Database" <> #FFCCBC {
-
- class "SQL Server" <> {
- .. Tables ..
- UserAccount
- UserCredential
- UserVerification
- UserAvatar
- Photo
- UserFollow
- Country
- StateProvince
- City
- BreweryPost
- BreweryPostLocation
- BreweryPostPhoto
- BeerStyle
- BeerPost
- BeerPostPhoto
- BeerPostComment
- .. Stored Procedures ..
- USP_RegisterUser
- usp_GetUserAccountByUsername
- usp_GetUserAccountByEmail
- usp_GetUserAccountById
- USP_GetActiveUserCredentialByUserAccountId
- USP_RotateUserCredential
- USP_CreateUserVerification
- }
-}
-
-' Relationships - API to Service
-AuthController ..> IRegisterService : uses
-AuthController ..> ILoginService : uses
-UserController ..> IUserService : uses
-
-AuthController ..> RegisterRequest : receives
-AuthController ..> LoginRequest : receives
-AuthController ..> "ResponseBody" : returns
-AuthController ..> RegistrationPayload : returns
-AuthController ..> LoginPayload : returns
-
-RegisterRequest ..> RegisterRequestValidator : validated by
-LoginRequest ..> LoginRequestValidator : validated by
-
-' Relationships - Service Layer
-IRegisterService <|.. RegisterService : implements
-ILoginService <|.. LoginService : implements
-ITokenService <|.. TokenService : implements
-IUserService <|.. UserService : implements
-IEmailService <|.. EmailService : implements
-
-RegisterService ..> IAuthRepository : uses
-RegisterService ..> IPasswordInfrastructure : uses
-RegisterService ..> ITokenService : uses
-RegisterService ..> IEmailService : uses
-RegisterService ..> RegisterServiceReturn : returns
-RegisterService ..> UserAccount : uses
-
-LoginService ..> IAuthRepository : uses
-LoginService ..> IPasswordInfrastructure : uses
-LoginService ..> ITokenService : uses
-LoginService ..> LoginServiceReturn : returns
-LoginService ..> UserAccount : uses
-LoginService ..> UserCredential : uses
-
-TokenService ..> ITokenInfrastructure : uses
-TokenService ..> UserAccount : uses
-
-UserService ..> IUserAccountRepository : uses
-UserService ..> UserAccount : uses
-
-EmailService ..> IEmailProvider : uses
-EmailService ..> IEmailTemplateProvider : uses
-EmailService ..> UserAccount : uses
-
-' Relationships - Repository Layer
-IAuthRepository <|.. AuthRepository : implements
-IUserAccountRepository <|.. UserAccountRepository : implements
-"Repository" <|-- AuthRepository : extends
-"Repository" <|-- UserAccountRepository : extends
-
-AuthRepository ..> ISqlConnectionFactory : uses
-AuthRepository ..> UserAccount : returns
-AuthRepository ..> UserCredential : returns
-AuthRepository ..> "SQL Server" : queries
-
-UserAccountRepository ..> ISqlConnectionFactory : uses
-UserAccountRepository ..> UserAccount : returns
-UserAccountRepository ..> "SQL Server" : queries
-
-"Repository" ..> ISqlConnectionFactory : uses
-
-ISqlConnectionFactory <|.. DefaultSqlConnectionFactory : implements
-DefaultSqlConnectionFactory ..> SqlConnectionStringHelper : uses
-
-' Relationships - Infrastructure
-IPasswordInfrastructure <|.. Argon2Infrastructure : implements
-ITokenInfrastructure <|.. JwtInfrastructure : implements
-IEmailProvider <|.. SmtpEmailProvider : implements
-IEmailTemplateProvider <|.. EmailTemplateProvider : implements
-
-EmailTemplateProvider ..> "UserRegistration <>" : renders
-"UserRegistration <>" ..> "Header <>" : includes
-"UserRegistration <>" ..> "Footer <>" : includes
-
-' Relationships - Domain
-UserAccount -- UserCredential : "1" -- "*"
-UserAccount -- UserVerification : "1" -- "0..1"
-
-' Exception handling
-GlobalExceptionFilter ..> ConflictException : catches
-GlobalExceptionFilter ..> NotFoundException : catches
-GlobalExceptionFilter ..> UnauthorizedException : catches
-GlobalExceptionFilter ..> ForbiddenException : catches
-GlobalExceptionFilter ..> ValidationException : catches
-
-RegisterService ..> ConflictException : throws
-LoginService ..> UnauthorizedException : throws
-UserService ..> NotFoundException : throws
-
-' Notes
-note right of Argon2Infrastructure
- Security Parameters:
- - Memory: 64MB
- - Iterations: 4
- - Salt: 16 bytes
- - Output: 32 bytes
-end note
-
-note right of JwtInfrastructure
- JWT Configuration:
- - Algorithm: HS256
- - Access: 1 hour
- - Refresh: 21 days
-end note
-
-note right of "SQL Server"
- Stored Procedures:
- - USP_RegisterUser: Transaction
- creates UserAccount +
- UserCredential
- - Credentials tracked with
- IsRevoked flag
-end note
-
-note right of AuthRepository
- Uses ADO.NET with
- parameterized queries
- to prevent SQL injection
-end note
-
-note bottom of RegisterService
- Registration Flow:
- 1. Validate user doesn't exist
- 2. Hash password (Argon2)
- 3. Create account + credential
- 4. Generate tokens
- 5. Send confirmation email
-end note
-
-note bottom of LoginService
- Login Flow:
- 1. Find user by username
- 2. Get active credential
- 3. Verify password
- 4. Generate tokens
- 5. Return authenticated user
-end note
-
-@enduml
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 2333844..8074b94 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -185,36 +185,6 @@ npm run dev
The frontend will be available at http://localhost:3000.
-## Generate Diagrams (Optional)
-
-The project includes PlantUML diagrams that can be converted to PDF or PNG:
-
-### Install Java
-
-Make sure Java 8+ is installed:
-
-```bash
-# Check Java version
-java -version
-```
-
-### Generate Diagrams
-
-```bash
-# Generate all PDFs
-make
-
-# Generate PNGs
-make pngs
-
-# Generate both
-make diagrams
-
-# View help
-make help
-```
-
-Generated diagrams will be in `docs/diagrams/pdf/`.
## Next Steps
@@ -223,39 +193,3 @@ Generated diagrams will be in `docs/diagrams/pdf/`.
- **Learn the Architecture**: Read [Architecture Overview](architecture.md)
- **Understand Docker Setup**: See [Docker Guide](docker.md)
- **Database Details**: Check [Database Schema](database.md)
-
-## Troubleshooting
-
-### Port Already in Use
-
-If port 8080 or 1433 is already in use, you can either:
-
-- Stop the service using that port
-- Change the port mapping in `docker-compose.dev.yaml`
-
-### Database Connection Issues
-
-Check that:
-
-- SQL Server container is running: `docker ps`
-- Connection string is correct in `.env.dev`
-- Health check is passing: `docker compose -f docker-compose.dev.yaml ps`
-
-### Container Won't Start
-
-View container logs:
-
-```bash
-docker compose -f docker-compose.dev.yaml logs
-```
-
-### Fresh Start
-
-Remove all containers and volumes:
-
-```bash
-docker compose -f docker-compose.dev.yaml down -v
-docker system prune -f
-```
-
-For more troubleshooting, see the [Docker Guide](docker.md#troubleshooting).