mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-04-05 10:09:03 +00:00
Update diagrams
This commit is contained in:
1
docs/diagrams-out/architecture.svg
Normal file
1
docs/diagrams-out/architecture.svg
Normal file
File diff suppressed because one or more lines are too long
1
docs/diagrams-out/authentication-flow.svg
Normal file
1
docs/diagrams-out/authentication-flow.svg
Normal file
File diff suppressed because one or more lines are too long
1
docs/diagrams-out/database-schema.svg
Normal file
1
docs/diagrams-out/database-schema.svg
Normal file
File diff suppressed because one or more lines are too long
1
docs/diagrams-out/deployment.svg
Normal file
1
docs/diagrams-out/deployment.svg
Normal file
File diff suppressed because one or more lines are too long
@@ -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" <<Rectangle>> #E3F2FD {
|
|
||||||
|
|
||||||
class AuthController {
|
|
||||||
- IRegisterService _registerService
|
|
||||||
- ILoginService _loginService
|
|
||||||
+ <<async>> Task<ActionResult> Register(RegisterRequest)
|
|
||||||
+ <<async>> Task<ActionResult> Login(LoginRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserController {
|
|
||||||
- IUserService _userService
|
|
||||||
+ <<async>> Task<ActionResult<IEnumerable<UserAccount>>> GetAll(int?, int?)
|
|
||||||
+ <<async>> Task<ActionResult<UserAccount>> GetById(Guid)
|
|
||||||
}
|
|
||||||
|
|
||||||
class GlobalExceptionFilter {
|
|
||||||
- ILogger<GlobalExceptionFilter> _logger
|
|
||||||
+ void OnException(ExceptionContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Contracts" {
|
|
||||||
class RegisterRequest <<record>> {
|
|
||||||
+ string Username
|
|
||||||
+ string FirstName
|
|
||||||
+ string LastName
|
|
||||||
+ string Email
|
|
||||||
+ DateTime DateOfBirth
|
|
||||||
+ string Password
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginRequest <<record>> {
|
|
||||||
+ string Username
|
|
||||||
+ string Password
|
|
||||||
}
|
|
||||||
|
|
||||||
class RegisterRequestValidator {
|
|
||||||
+ RegisterRequestValidator()
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginRequestValidator {
|
|
||||||
+ LoginRequestValidator()
|
|
||||||
}
|
|
||||||
|
|
||||||
class "ResponseBody<T>" <<record>> {
|
|
||||||
+ string Message
|
|
||||||
+ T Payload
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginPayload <<record>> {
|
|
||||||
+ Guid UserAccountId
|
|
||||||
+ string Username
|
|
||||||
+ string RefreshToken
|
|
||||||
+ string AccessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
class RegistrationPayload <<record>> {
|
|
||||||
+ Guid UserAccountId
|
|
||||||
+ string Username
|
|
||||||
+ string RefreshToken
|
|
||||||
+ string AccessToken
|
|
||||||
+ bool ConfirmationEmailSent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
' Service Layer
|
|
||||||
package "Service Layer" <<Rectangle>> #C8E6C9 {
|
|
||||||
|
|
||||||
package "Service.Auth" {
|
|
||||||
interface IRegisterService {
|
|
||||||
+ <<async>> Task<RegisterServiceReturn> RegisterAsync(UserAccount, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RegisterService {
|
|
||||||
- IAuthRepository _authRepo
|
|
||||||
- IPasswordInfrastructure _passwordInfra
|
|
||||||
- ITokenService _tokenService
|
|
||||||
- IEmailService _emailService
|
|
||||||
+ <<async>> Task<RegisterServiceReturn> RegisterAsync(UserAccount, string)
|
|
||||||
- <<async>> Task ValidateUserDoesNotExist(UserAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ILoginService {
|
|
||||||
+ <<async>> Task<LoginServiceReturn> LoginAsync(string, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginService {
|
|
||||||
- IAuthRepository _authRepo
|
|
||||||
- IPasswordInfrastructure _passwordInfra
|
|
||||||
- ITokenService _tokenService
|
|
||||||
+ <<async>> Task<LoginServiceReturn> LoginAsync(string, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ITokenService {
|
|
||||||
+ string GenerateAccessToken(UserAccount)
|
|
||||||
+ string GenerateRefreshToken(UserAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
class TokenService {
|
|
||||||
- ITokenInfrastructure _tokenInfrastructure
|
|
||||||
+ string GenerateAccessToken(UserAccount)
|
|
||||||
+ string GenerateRefreshToken(UserAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RegisterServiceReturn <<record>> {
|
|
||||||
+ bool IsAuthenticated
|
|
||||||
+ bool EmailSent
|
|
||||||
+ UserAccount UserAccount
|
|
||||||
+ string AccessToken
|
|
||||||
+ string RefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginServiceReturn <<record>> {
|
|
||||||
+ UserAccount UserAccount
|
|
||||||
+ string RefreshToken
|
|
||||||
+ string AccessToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Service.UserManagement" {
|
|
||||||
interface IUserService {
|
|
||||||
+ <<async>> Task<IEnumerable<UserAccount>> GetAllAsync(int?, int?)
|
|
||||||
+ <<async>> Task<UserAccount> GetByIdAsync(Guid)
|
|
||||||
+ <<async>> Task UpdateAsync(UserAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserService {
|
|
||||||
- IUserAccountRepository _repository
|
|
||||||
+ <<async>> Task<IEnumerable<UserAccount>> GetAllAsync(int?, int?)
|
|
||||||
+ <<async>> Task<UserAccount> GetByIdAsync(Guid)
|
|
||||||
+ <<async>> Task UpdateAsync(UserAccount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Service.Emails" {
|
|
||||||
interface IEmailService {
|
|
||||||
+ <<async>> Task SendRegistrationEmailAsync(UserAccount, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmailService {
|
|
||||||
- IEmailProvider _emailProvider
|
|
||||||
- IEmailTemplateProvider _templateProvider
|
|
||||||
+ <<async>> Task SendRegistrationEmailAsync(UserAccount, string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
' Domain Layer
|
|
||||||
package "Domain" <<Rectangle>> #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" <<Rectangle>> #E1BEE7 {
|
|
||||||
|
|
||||||
package "Infrastructure.Repository" {
|
|
||||||
interface IAuthRepository {
|
|
||||||
+ <<async>> Task<UserAccount> RegisterUserAsync(string, string, string, string, DateTime, string)
|
|
||||||
+ <<async>> Task<UserAccount?> GetUserByEmailAsync(string)
|
|
||||||
+ <<async>> Task<UserAccount?> GetUserByUsernameAsync(string)
|
|
||||||
+ <<async>> Task<UserCredential?> GetActiveCredentialByUserAccountIdAsync(Guid)
|
|
||||||
+ <<async>> Task RotateCredentialAsync(Guid, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
class AuthRepository {
|
|
||||||
- ISqlConnectionFactory _connectionFactory
|
|
||||||
+ <<async>> Task<UserAccount> RegisterUserAsync(...)
|
|
||||||
+ <<async>> Task<UserAccount?> GetUserByEmailAsync(string)
|
|
||||||
+ <<async>> Task<UserAccount?> GetUserByUsernameAsync(string)
|
|
||||||
+ <<async>> Task<UserCredential?> GetActiveCredentialByUserAccountIdAsync(Guid)
|
|
||||||
+ <<async>> Task RotateCredentialAsync(Guid, string)
|
|
||||||
# UserAccount MapToEntity(DbDataReader)
|
|
||||||
- UserCredential MapToCredentialEntity(DbDataReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IUserAccountRepository {
|
|
||||||
+ <<async>> Task<UserAccount?> GetByIdAsync(Guid)
|
|
||||||
+ <<async>> Task<IEnumerable<UserAccount>> GetAllAsync(int?, int?)
|
|
||||||
+ <<async>> Task UpdateAsync(UserAccount)
|
|
||||||
+ <<async>> Task DeleteAsync(Guid)
|
|
||||||
+ <<async>> Task<UserAccount?> GetByUsernameAsync(string)
|
|
||||||
+ <<async>> Task<UserAccount?> GetByEmailAsync(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserAccountRepository {
|
|
||||||
- ISqlConnectionFactory _connectionFactory
|
|
||||||
+ <<async>> Task<UserAccount?> GetByIdAsync(Guid)
|
|
||||||
+ <<async>> Task<IEnumerable<UserAccount>> GetAllAsync(int?, int?)
|
|
||||||
+ <<async>> Task UpdateAsync(UserAccount)
|
|
||||||
+ <<async>> Task DeleteAsync(Guid)
|
|
||||||
+ <<async>> Task<UserAccount?> GetByUsernameAsync(string)
|
|
||||||
+ <<async>> Task<UserAccount?> GetByEmailAsync(string)
|
|
||||||
# UserAccount MapToEntity(DbDataReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class "Repository<T>" {
|
|
||||||
# ISqlConnectionFactory _connectionFactory
|
|
||||||
# <<async>> Task<DbConnection> CreateConnection()
|
|
||||||
# {abstract} T MapToEntity(DbDataReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Sql" {
|
|
||||||
interface ISqlConnectionFactory {
|
|
||||||
+ DbConnection CreateConnection()
|
|
||||||
}
|
|
||||||
|
|
||||||
class DefaultSqlConnectionFactory {
|
|
||||||
- string _connectionString
|
|
||||||
+ DbConnection CreateConnection()
|
|
||||||
}
|
|
||||||
|
|
||||||
class SqlConnectionStringHelper <<static>> {
|
|
||||||
+ {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 {
|
|
||||||
+ <<async>> Task SendAsync(string, string, string, bool)
|
|
||||||
+ <<async>> Task SendAsync(IEnumerable<string>, string, string, bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SmtpEmailProvider {
|
|
||||||
- string _host
|
|
||||||
- int _port
|
|
||||||
- string? _username
|
|
||||||
- string? _password
|
|
||||||
- bool _useSsl
|
|
||||||
- string _fromEmail
|
|
||||||
- string _fromName
|
|
||||||
+ <<async>> Task SendAsync(string, string, string, bool)
|
|
||||||
+ <<async>> Task SendAsync(IEnumerable<string>, string, string, bool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Infrastructure.Email.Templates" {
|
|
||||||
interface IEmailTemplateProvider {
|
|
||||||
+ <<async>> Task<string> RenderUserRegisteredEmailAsync(string, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmailTemplateProvider {
|
|
||||||
- IServiceProvider _serviceProvider
|
|
||||||
- ILoggerFactory _loggerFactory
|
|
||||||
+ <<async>> Task<string> RenderUserRegisteredEmailAsync(string, string)
|
|
||||||
- <<async>> Task<string> RenderComponentAsync<TComponent>(Dictionary<string, object?>)
|
|
||||||
}
|
|
||||||
|
|
||||||
class "UserRegistration <<Razor>>" {
|
|
||||||
+ string Username
|
|
||||||
+ string ConfirmationLink
|
|
||||||
}
|
|
||||||
|
|
||||||
class "Header <<Razor>>" {
|
|
||||||
}
|
|
||||||
|
|
||||||
class "Footer <<Razor>>" {
|
|
||||||
+ string? FooterText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
' Database Layer
|
|
||||||
package "Database" <<Rectangle>> #FFCCBC {
|
|
||||||
|
|
||||||
class "SQL Server" <<Database>> {
|
|
||||||
.. 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<T>" : 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<T>" <|-- AuthRepository : extends
|
|
||||||
"Repository<T>" <|-- 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<T>" ..> ISqlConnectionFactory : uses
|
|
||||||
|
|
||||||
ISqlConnectionFactory <|.. DefaultSqlConnectionFactory : implements
|
|
||||||
DefaultSqlConnectionFactory ..> SqlConnectionStringHelper : uses
|
|
||||||
|
|
||||||
' Relationships - Infrastructure
|
|
||||||
IPasswordInfrastructure <|.. Argon2Infrastructure : implements
|
|
||||||
ITokenInfrastructure <|.. JwtInfrastructure : implements
|
|
||||||
IEmailProvider <|.. SmtpEmailProvider : implements
|
|
||||||
IEmailTemplateProvider <|.. EmailTemplateProvider : implements
|
|
||||||
|
|
||||||
EmailTemplateProvider ..> "UserRegistration <<Razor>>" : renders
|
|
||||||
"UserRegistration <<Razor>>" ..> "Header <<Razor>>" : includes
|
|
||||||
"UserRegistration <<Razor>>" ..> "Footer <<Razor>>" : 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
|
|
||||||
@@ -185,36 +185,6 @@ npm run dev
|
|||||||
|
|
||||||
The frontend will be available at http://localhost:3000.
|
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
|
## Next Steps
|
||||||
|
|
||||||
@@ -223,39 +193,3 @@ Generated diagrams will be in `docs/diagrams/pdf/`.
|
|||||||
- **Learn the Architecture**: Read [Architecture Overview](architecture.md)
|
- **Learn the Architecture**: Read [Architecture Overview](architecture.md)
|
||||||
- **Understand Docker Setup**: See [Docker Guide](docker.md)
|
- **Understand Docker Setup**: See [Docker Guide](docker.md)
|
||||||
- **Database Details**: Check [Database Schema](database.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 <service-name>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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).
|
|
||||||
|
|||||||
Reference in New Issue
Block a user