feat: comprehensive app improvements with Pinia state management
Backend: - Add API keys management (create, list, delete endpoints) - Add email verification flow (verify, resend verification) - Add account management (profile, change password, delete account) - Add billing/Stripe integration (checkout, portal, webhooks) - Add GeoIP service for analytics - Add bulk link creation and link restore endpoints - Add QR code analytics endpoint - Add project description field with migration - Add QR code name and logo support with migration - Improve QR code generator with logo overlay support - Add rate limiting middleware - Update tests for new functionality Frontend: - Refactor entire app to use Pinia for state management - Add auth store with initialization, login, register, logout - Add workspace store with CRUD for workspaces, projects, links, QR codes, domains, assets, and analytics - Add localStorage persistence for workspace selection - Update App.vue with proper store initialization - Update AppLayout.vue to use store methods instead of direct API - Refactor Projects.vue and Domains.vue to use store state/actions - Add VerifyEmail.vue for email verification flow - Add ForgotPassword.vue and ResetPassword.vue - Add Settings.vue with profile, password, API keys, danger zone - Add QRCodeDetail.vue for QR code analytics - Add Billing.vue for subscription management - Expand api/client.js with all new API methods - Add workspace change watchers for automatic data refresh Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ public class WorkspaceEndpointTests(ApiWebApplicationFactory factory)
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
|
||||
private async Task<string> GetAuthTokenAsync(string email = "workspace-test@example.com")
|
||||
private async Task<(string Token, Guid WorkspaceId)> GetAuthAndWorkspaceAsync(string email, bool upgradeToPro = false)
|
||||
{
|
||||
var response = await _client.PostAsJsonAsync("/auth/register", new { Email = email, Password = "password123" });
|
||||
if (response.StatusCode == HttpStatusCode.Conflict)
|
||||
@@ -20,7 +20,25 @@ public class WorkspaceEndpointTests(ApiWebApplicationFactory factory)
|
||||
response = await _client.PostAsJsonAsync("/auth/login", new { Email = email, Password = "password123" });
|
||||
}
|
||||
var result = await response.Content.ReadFromJsonAsync<AuthResponse>();
|
||||
return result!.Token;
|
||||
var token = result!.Token;
|
||||
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
var workspacesResponse = await _client.GetAsync("/workspaces");
|
||||
var workspaces = await workspacesResponse.Content.ReadFromJsonAsync<WorkspaceListResponse>();
|
||||
var workspaceId = workspaces!.Workspaces.First().Id;
|
||||
|
||||
if (upgradeToPro)
|
||||
{
|
||||
await factory.UpgradeWorkspaceToPro(workspaceId);
|
||||
}
|
||||
|
||||
return (token, workspaceId);
|
||||
}
|
||||
|
||||
private async Task<string> GetAuthTokenAsync(string email = "workspace-test@example.com")
|
||||
{
|
||||
var (token, _) = await GetAuthAndWorkspaceAsync(email);
|
||||
return token;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -53,8 +71,8 @@ public class WorkspaceEndpointTests(ApiWebApplicationFactory factory)
|
||||
[Fact]
|
||||
public async Task CreateWorkspace_WithValidData_ReturnsCreated()
|
||||
{
|
||||
// Arrange
|
||||
var token = await GetAuthTokenAsync("create-ws@example.com");
|
||||
// Arrange - upgrade to Pro to allow creating additional workspaces
|
||||
var (token, _) = await GetAuthAndWorkspaceAsync("create-ws@example.com", upgradeToPro: true);
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
// Act
|
||||
@@ -86,21 +104,18 @@ public class WorkspaceEndpointTests(ApiWebApplicationFactory factory)
|
||||
[Fact]
|
||||
public async Task GetWorkspace_WithValidId_ReturnsWorkspace()
|
||||
{
|
||||
// Arrange
|
||||
var token = await GetAuthTokenAsync("get-ws@example.com");
|
||||
// Arrange - use the default workspace (created on registration)
|
||||
var (token, workspaceId) = await GetAuthAndWorkspaceAsync("get-ws@example.com");
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
var createResponse = await _client.PostAsJsonAsync("/workspaces", new { Name = "Get Test" });
|
||||
var created = await createResponse.Content.ReadFromJsonAsync<WorkspaceResponse>();
|
||||
|
||||
// Act
|
||||
var response = await _client.GetAsync($"/workspaces/{created!.Id}");
|
||||
var response = await _client.GetAsync($"/workspaces/{workspaceId}");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var result = await response.Content.ReadFromJsonAsync<WorkspaceResponse>();
|
||||
result!.Id.Should().Be(created.Id);
|
||||
result.Name.Should().Be("Get Test");
|
||||
result!.Id.Should().Be(workspaceId);
|
||||
result.Name.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -120,15 +135,12 @@ public class WorkspaceEndpointTests(ApiWebApplicationFactory factory)
|
||||
[Fact]
|
||||
public async Task UpdateWorkspace_WithValidData_ReturnsUpdated()
|
||||
{
|
||||
// Arrange
|
||||
var token = await GetAuthTokenAsync("update-ws@example.com");
|
||||
// Arrange - use the default workspace (created on registration)
|
||||
var (token, workspaceId) = await GetAuthAndWorkspaceAsync("update-ws@example.com");
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
var createResponse = await _client.PostAsJsonAsync("/workspaces", new { Name = "Original Name" });
|
||||
var created = await createResponse.Content.ReadFromJsonAsync<WorkspaceResponse>();
|
||||
|
||||
// Act
|
||||
var response = await _client.PutAsJsonAsync($"/workspaces/{created!.Id}", new { Name = "Updated Name" });
|
||||
var response = await _client.PutAsJsonAsync($"/workspaces/{workspaceId}", new { Name = "Updated Name" });
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
@@ -139,8 +151,8 @@ public class WorkspaceEndpointTests(ApiWebApplicationFactory factory)
|
||||
[Fact]
|
||||
public async Task DeleteWorkspace_WithValidId_ReturnsSuccess()
|
||||
{
|
||||
// Arrange
|
||||
var token = await GetAuthTokenAsync("delete-ws@example.com");
|
||||
// Arrange - upgrade to Pro to allow creating additional workspaces
|
||||
var (token, _) = await GetAuthAndWorkspaceAsync("delete-ws@example.com", upgradeToPro: true);
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
var createResponse = await _client.PostAsJsonAsync("/workspaces", new { Name = "To Delete" });
|
||||
|
||||
Reference in New Issue
Block a user