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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,31 @@
This project is still under restructuring. The web client is currently not functioning properly due to changes in the API server.
If you are looking for a working version of the application, please check the [proof-of-concept branch](https://github.com/Pandetthe/Snapflow/tree/proof-of-concept).
Alternatively, if you are interested in tracking progress towards the MVP, please refer to this [roadmap](https://github.com/users/Pandetthe/projects/2).

## Unified Deployment

This project includes automated setup scripts to spin up the entire environment using Docker Compose.

### Prerequisites
* Docker and Docker Compose installed and running.
* On Windows, ensure Docker Desktop is active.

### How to Run
**Windows (PowerShell):**
Open your terminal and run:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1
```

**Linux / macOS (Bash):**
Open your terminal and run:

```Bash
chmod +x scripts/setup.sh
./scripts/setup.sh
```

Management
View Logs: docker-compose -f deployment/docker-compose.yml logs -f

Stop Services: docker-compose -f deployment/docker-compose.yml down
20 changes: 20 additions & 0 deletions scripts/setup.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Write-Host "Starting Snapflow deployment..." -ForegroundColor Cyan

# Navigate to the project root directory
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
Set-Location -Path "$ScriptDir\.."

# Check if Docker is running
docker info > $null 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Docker is not running or not recognized. Please start Docker Desktop." -ForegroundColor Red
Write-Host "Note: If you just installed Docker, you may need to restart VS Code so the terminal can see it." -ForegroundColor Yellow
exit 1
}

Write-Host "Building and starting containers (Docker Compose)..." -ForegroundColor Yellow
docker-compose -f deployment/docker-compose.yml up -d --build

Write-Host "Deployment successful!" -ForegroundColor Green
Write-Host "Web client and API are now running."
Write-Host "To view logs, run: docker-compose -f deployment/docker-compose.yml logs -f"
22 changes: 22 additions & 0 deletions scripts/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

# Exit immediately if a command exits with a non-zero status
set -e

echo "Starting Snapflow deployment..."

# Navigate to the project root directory (assuming script is in scripts/)
cd "$(dirname "$0")/.."

# Check if Docker is running
if ! docker info > /dev/null 2>&1; then
echo "Error: Docker is not running or not installed. Please start Docker and try again."
exit 1
fi

echo "Building and starting containers..."
docker-compose -f deployment/docker-compose.yml up -d --build

echo "Deployment successful!"
echo "Web client and API are now running via Nginx reverse proxy."
echo "To view logs, run: docker-compose -f deployment/docker-compose.yml logs -f"
2 changes: 1 addition & 1 deletion server/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>

<Version>2026.5.1</Version>
<Version>2026.5.2</Version>

<RootNamespace>Snapflow.$(MSBuildProjectName)</RootNamespace>

Expand Down
2 changes: 2 additions & 0 deletions server/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
<PackageVersion Include="EntityFrameworkCore.Exceptions.Common" Version="10.0.0" />
<PackageVersion Include="Hangfire" Version="1.8.23" />
<PackageVersion Include="Jdenticon-net" Version="3.1.2" />
<PackageVersion Include="MediatR" Version="14.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.OutputCaching.StackExchangeRedis" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="10.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.5.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.5.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ public interface IAppDbContext
DbSet<Tag> Tags { get; }

DbSet<Card> Cards { get; }

DbSet<CardComment> CardComments { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
2 changes: 1 addition & 1 deletion server/src/Application/Boards/Create/CreateBoardCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ public sealed record CreateBoardMemberRequest(int UserId, MemberRole Role);

public sealed record CreateBoardCommand(
string Title,
string Description,
string? Description,
IReadOnlyList<CreateBoardMemberRequest>? Members = null) : ICommand<int>;
2 changes: 1 addition & 1 deletion server/src/Application/Boards/Get/GetBoardsResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public sealed record UserDto(int Id, string UserName)
public sealed record BoardDto(
int Id,
string Title,
string Description,
string? Description,
MemberRole YourRole,
DateTimeOffset CreatedAt,
UserDto CreatedBy,
Expand Down
11 changes: 10 additions & 1 deletion server/src/Application/Boards/GetById/GetBoardByIdHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@ public async Task<Result<GetBoardByIdResponse>> Handle(GetBoardByIdQuery query,
c.CreatedAt,
UserDto.From(c.CreatedBy),
c.UpdatedAt,
UserDto.From(c.UpdatedBy)))
UserDto.From(c.UpdatedBy),
c.Comments
.OrderBy(comm => comm.CreatedAt)
.Select(comm => new CardCommentDto(
comm.Id,
comm.UserId,
comm.User.UserName ?? "Unknown",
comm.Content,
comm.CreatedAt))
.ToList()))
.ToList()))
.ToList()))
.ToList()))
Expand Down
14 changes: 11 additions & 3 deletions server/src/Application/Boards/GetById/GetBoardByIdResponse.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System.Diagnostics.CodeAnalysis;
using Snapflow.Application.Cards.AddComment;
using static Snapflow.Application.Boards.GetById.GetBoardByIdResponse;

namespace Snapflow.Application.Boards.GetById;

public sealed record GetBoardByIdResponse(
int Id,
string Title,
string Description,
string? Description,
IReadOnlyList<SwimlaneDto> Swimlanes)
{
public sealed record UserDto(int Id, string UserName)
Expand Down Expand Up @@ -34,10 +35,17 @@ public sealed record ListDto(
public sealed record CardDto(
int Id,
string Title,
string Description,
string? Description,
string Rank,
DateTimeOffset CreatedAt,
UserDto CreatedBy,
DateTimeOffset? UpdatedAt,
UserDto? UpdatedBy);
UserDto? UpdatedBy,
List<CardCommentDto> Comments);
public sealed record CardCommentDto(
int Id,
int UserId,
string UserName,
string Content,
DateTimeOffset CreatedAt);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Snapflow.Application.Boards.GetDetails;
public sealed record GetBoardDetailsResponse(
int Id,
string Title,
string Description,
string? Description,
IReadOnlyList<GetBoardDetailsMemberResponse> Members);

public sealed record GetBoardDetailsMemberResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Snapflow.Application.Abstractions.Messaging;
using Snapflow.Application.Boards.GetById;

namespace Snapflow.Application.Cards.AddComment;

public sealed record AddCardCommentCommand(int CardId, int UserId, string Content)
: ICommand<GetBoardByIdResponse.CardCommentDto>;
46 changes: 46 additions & 0 deletions server/src/Application/Cards/AppComment/AddCardCommentHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Microsoft.EntityFrameworkCore;
using Snapflow.Application.Abstractions.Messaging;
using Snapflow.Application.Abstractions.Persistence;
using Snapflow.Application.Boards.GetById;
using Snapflow.Common;
using Snapflow.Domain.Cards;

namespace Snapflow.Application.Cards.AddComment;

internal sealed class AddCardCommentCommandHandler(
IAppDbContext context) : ICommandHandler<AddCardCommentCommand, GetBoardByIdResponse.CardCommentDto> // <--- ZMIANA 1: Zwracamy DTO zamiast int
{
public async Task<Result<GetBoardByIdResponse.CardCommentDto>> Handle(AddCardCommentCommand request, CancellationToken cancellationToken = default)
{
var card = await context.Cards
.FirstOrDefaultAsync(c => c.Id == request.CardId && !c.IsDeleted, cancellationToken);

if (card is null)
{
return Result.Failure<GetBoardByIdResponse.CardCommentDto>(CardErrors.NotFound(request.CardId));
}

var user = await context.Users
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);

if (user is null)
{
return Result.Failure<GetBoardByIdResponse.CardCommentDto>(new Error("User.NotFound", "User not found", ErrorType.NotFound));
}

var comment = CardComment.Create(request.CardId, request.UserId, request.Content);

context.CardComments.Add(comment);
await context.SaveChangesAsync(cancellationToken);

var dto = new GetBoardByIdResponse.CardCommentDto(
comment.Id,
user.Id,
user.UserName ?? "Unknown",
comment.Content,
comment.CreatedAt
);

return dto;
}
}
8 changes: 8 additions & 0 deletions server/src/Application/Cards/AppComment/CardCommentDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Snapflow.Application.Cards.AddComment;

public sealed record CardCommentDto(
int Id,
int UserId,
string UserName,
string Content,
DateTimeOffset CreatedAt);
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public sealed record CardDto(
int SwimlaneId,
int BoardId,
string Title,
string Description,
string? Description,
string Rank);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed record GetCardByIdResponse(
int SwimlaneId,
int BoardId,
string Title,
string Description,
string? Description,
string Rank,
DateTimeOffset CreatedAt,
UserDto CreatedBy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public sealed record CardDto(
int SwimlaneId,
int BoardId,
string Title,
string Description,
string? Description,
string Rank);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public sealed record CardDto(
int SwimlaneId,
int BoardId,
string Title,
string Description,
string? Description,
string Rank);
}
2 changes: 1 addition & 1 deletion server/src/Application/Cards/Update/UpdateCardCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

namespace Snapflow.Application.Cards.Update;

public sealed record UpdateCardCommand(int Id, string Title, string Description) : ICommand<UpdateCardResponse>;
public sealed record UpdateCardCommand(int Id, string Title, string? Description) : ICommand<UpdateCardResponse>;
6 changes: 3 additions & 3 deletions server/src/Domain/Boards/Board.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class Board : Entity<int, Board>
public Board() { }

public string Title { get; private set; } = null!;
public string Description { get; private set; } = "";
public string? Description { get; private set; } = "";

public DateTimeOffset CreatedAt { get; private set; }
public int CreatedById { get; private set; }
Expand All @@ -34,7 +34,7 @@ public Board() { }
public virtual ICollection<Card> Cards { get; private set; } = [];
public virtual ICollection<Tag> Tags { get; private set; } = [];

public static Board Create(string title, string description, int createdById, DateTimeOffset createdAt, string? connectionId = null)
public static Board Create(string title, string? description, int createdById, DateTimeOffset createdAt, string? connectionId = null)
{
var board = new Board
{
Expand All @@ -51,7 +51,7 @@ public static Board Create(string title, string description, int createdById, Da
return board;
}

public void Update(string title, string description, int updatedById, DateTimeOffset updatedAt, string? connectionId = null)
public void Update(string title, string? description, int updatedById, DateTimeOffset updatedAt, string? connectionId = null)
{
Title = title;
Description = description;
Expand Down
2 changes: 1 addition & 1 deletion server/src/Domain/Boards/BoardEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed record BoardCreatedDomainEvent(
public sealed record BoardUpdatedDomainEvent(
int Id,
string Title,
string Description,
string? Description,
string? ConnectionId) : IDomainEvent;

public sealed record BoardDeletedDomainEvent(
Expand Down
15 changes: 10 additions & 5 deletions server/src/Domain/Cards/Card.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ public Card() { }
public virtual List List { get; private set; } = null!;

public string Title { get; private set; } = null!;
public string Description { get; private set; } = "";
public string? Description { get; private set; } = "";
public string Rank { get; set; } = null!;

public DateTimeOffset CreatedAt { get; private set; }
public int CreatedById { get; private set; }
public virtual IUser CreatedBy { get; private set; } = null!;
Expand All @@ -38,8 +37,10 @@ public Card() { }
public bool DeletedByCascade { get; private set; }

public virtual ICollection<Tag> Tags { get; private set; } = [];

public static Card Create(int boardId, int swimlaneId, int listId, string title, string description, string rank, int createdById, DateTimeOffset createdAt, string? connectionId = null)

private readonly List<CardComment> _comments = [];
public virtual IReadOnlyCollection<CardComment> Comments => _comments.AsReadOnly();
public static Card Create(int boardId, int swimlaneId, int listId, string title, string? description, string rank, int createdById, DateTimeOffset createdAt, string? connectionId = null)
{
var card = new Card
{
Expand All @@ -58,7 +59,7 @@ public static Card Create(int boardId, int swimlaneId, int listId, string title,
return card;
}

public void Update(string title, string description, int updatedById, DateTimeOffset updatedAt, string? connectionId = null)
public void Update(string title, string? description, int updatedById, DateTimeOffset updatedAt, string? connectionId = null)
{
Title = title;
Description = description;
Expand Down Expand Up @@ -88,4 +89,8 @@ public void SoftDelete(int deletedById, DateTimeOffset deletedAt, string? connec

Raise(c => new CardDeletedDomainEvent(Id, BoardId, connectionId));
}
public void AddComment(int userId, string content)
{
_comments.Add(CardComment.Create(Id, userId, content));
}
}
Loading