Migrate from User to Account domain concept

Renames core domain entities and services from "User" to "Account"

Refactors project-wide namespaces, classes, and database tables to use "Account" terminology
Updates related components, services, and database schema to reflect new domain naming
Standardizes naming conventions across authentication and organization setup features
This commit is contained in:
Janus C. H. Knudsen 2026-01-09 22:14:46 +01:00
parent e5e7c1c19f
commit 88812177a9
29 changed files with 288 additions and 298 deletions

View file

@ -1,24 +1,24 @@
using Insight.Database;
using Insight.Database;
using PlanTempus.Core;
using PlanTempus.Core.Entities.Users;
using PlanTempus.Core.Entities.Accounts;
using System.Data;
namespace PlanTempus.Database.Core
{
public class UserService
public class AccountService
{
public record UserCreateCommand(string CorrelationId, string Email, string Password);
public record AccountCreateCommand(string CorrelationId, string Email, string Password);
private readonly IDbConnection _db;
public UserService(IDbConnection db)
public AccountService(IDbConnection db)
{
_db = db;
}
public async Task CreateUser(UserCreateCommand command)
public async Task CreateAccount(AccountCreateCommand command)
{
var user = new User
var account = new Account
{
Email = command.Email,
PasswordHash = new SecureTokenizer().TokenizeText(command.Password),
@ -27,13 +27,13 @@ namespace PlanTempus.Database.Core
CreatedDate = DateTime.UtcNow
};
var userId = await _db.ExecuteScalarAsync<int>(@$"
INSERT INTO users (email, password_hash, security_stamp, email_confirmed, created_at)
var accountId = await _db.ExecuteScalarAsync<int>(@$"
INSERT INTO accounts (email, password_hash, security_stamp, email_confirmed, created_at)
VALUES (@Email, @PasswordHash, @SecurityStamp, @EmailConfirmed, @CreatedDate)
RETURNING id", user);
RETURNING id", account);
}
public async Task CreateOrganization(int userId, string organizationConnectionString)
public async Task CreateOrganization(int accountId, string organizationConnectionString)
{
var schema = "dev";
@ -46,7 +46,7 @@ namespace PlanTempus.Database.Core
{
ConnectionString = organizationConnectionString,
CreatedDate = DateTime.UtcNow,
CreatedBy = userId,
CreatedBy = accountId,
IsActive = true
};
@ -55,17 +55,17 @@ namespace PlanTempus.Database.Core
VALUES (@ConnectionString, @CreatedDate, @IsActive)
RETURNING id", organization);
// Link user to organization
var userOrganization = new UserOrganization
// Link account to organization
var accountOrganization = new AccountOrganization
{
UserId = userId,
AccountId = accountId,
OrganizationId = organizationId,
CreatedDate = DateTime.UtcNow
};
await _db.ExecuteAsync(@$"
INSERT INTO {schema}.user_organizations (user_id, organization_id, created_date)
VALUES (@UserId, @OrganizationId, @CreatedDate)", userOrganization);
INSERT INTO {schema}.account_organizations (account_id, organization_id, created_date)
VALUES (@AccountId, @OrganizationId, @CreatedDate)", accountOrganization);
transaction.Commit();
}
@ -76,4 +76,4 @@ namespace PlanTempus.Database.Core
}
}
}
}
}

View file

@ -35,9 +35,9 @@ namespace PlanTempus.Database.Core.DDL
using var transaction = conn.OpenWithTransaction();
try
{
CreateUsersTable(conn);
CreateAccountsTable(conn);
CreateOrganizationsTable(conn);
CreateUserOrganizationsTable(conn);
CreateAccountOrganizationsTable(conn);
SetupRLS(conn);
transaction.Commit();
@ -51,12 +51,12 @@ namespace PlanTempus.Database.Core.DDL
/// <summary>
/// Creates the users table
/// Creates the accounts table
/// </summary>
void CreateUsersTable(IDbConnection db)
void CreateAccountsTable(IDbConnection db)
{
var sql = @$"
CREATE TABLE IF NOT EXISTS {_command.Schema}.users (
CREATE TABLE IF NOT EXISTS {_command.Schema}.accounts (
id SERIAL PRIMARY KEY,
email VARCHAR(256) NOT NULL UNIQUE,
password_hash VARCHAR(256) NOT NULL,
@ -91,17 +91,17 @@ namespace PlanTempus.Database.Core.DDL
}
/// <summary>
/// Creates the user_organizations table
/// Creates the account_organizations table
/// </summary>
void CreateUserOrganizationsTable(IDbConnection db)
void CreateAccountOrganizationsTable(IDbConnection db)
{
var sql = @$"
CREATE TABLE IF NOT EXISTS {_command.Schema}.user_organizations (
user_id INTEGER NOT NULL REFERENCES {_command.Schema}.users(id),
CREATE TABLE IF NOT EXISTS {_command.Schema}.account_organizations (
account_id INTEGER NOT NULL REFERENCES {_command.Schema}.accounts(id),
organization_id INTEGER NOT NULL REFERENCES {_command.Schema}.organizations(id),
pin_code VARCHAR(10) NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, organization_id)
PRIMARY KEY (account_id, organization_id)
);";
db.ExecuteSql(sql);
@ -109,24 +109,24 @@ namespace PlanTempus.Database.Core.DDL
}
/// <summary>
/// Sets up Row Level Security (RLS) for the organizations and user_organizations tables.
/// Sets up Row Level Security (RLS) for the organizations and account_organizations tables.
/// </summary>
void SetupRLS(IDbConnection db)
{
var sql = new[]
{
$"ALTER TABLE {_command.Schema}.organizations ENABLE ROW LEVEL SECURITY;",
$"ALTER TABLE {_command.Schema}.user_organizations ENABLE ROW LEVEL SECURITY;",
$"ALTER TABLE {_command.Schema}.account_organizations ENABLE ROW LEVEL SECURITY;",
$"DROP POLICY IF EXISTS organization_access ON {_command.Schema}.organizations;",
@$"CREATE POLICY organization_access ON {_command.Schema}.organizations
USING (id IN (
SELECT organization_id
FROM {_command.Schema}.user_organizations
WHERE user_id = current_setting('app.user_id', TRUE)::INTEGER
SELECT organization_id
FROM {_command.Schema}.account_organizations
WHERE account_id = current_setting('app.account_id', TRUE)::INTEGER
)) WITH CHECK (true);",
$"DROP POLICY IF EXISTS user_organization_access ON {_command.Schema}.user_organizations;",
@$"CREATE POLICY user_organization_access ON {_command.Schema}.user_organizations
USING (user_id = current_setting('app.user_id', TRUE)::INTEGER) WITH CHECK (true);"
$"DROP POLICY IF EXISTS account_organization_access ON {_command.Schema}.account_organizations;",
@$"CREATE POLICY account_organization_access ON {_command.Schema}.account_organizations
USING (account_id = current_setting('app.account_id', TRUE)::INTEGER) WITH CHECK (true);"
};
foreach (var statement in sql)